package com.ruoyi.generator.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.generator.domain.SourceProject;
import com.ruoyi.generator.domain.SourcePurchase;
import com.ruoyi.generator.domain.source.SourceProjectResponse;
import com.ruoyi.generator.mapper.SourceProjectMapper;
import com.ruoyi.generator.mapper.SourcePurchaseMapper;

public class SourceProjectServiceImplTest
{
    private SourceProjectServiceImpl service;

    @Mock
    private SourceProjectMapper sourceProjectMapper;
    @Mock
    private SourcePurchaseMapper sourcePurchaseMapper;

    @Before
    public void setUp() throws Exception
    {
        MockitoAnnotations.initMocks(this);
        service = new SourceProjectServiceImpl();
        setField("sourceProjectMapper", sourceProjectMapper);
        setField("sourcePurchaseMapper", sourcePurchaseMapper);
    }

    @Test
    public void listPublicProjectsFormatsCatalogFieldsAndPurchaseState()
    {
        SourceProject query = new SourceProject();
        SourceProject project = project("0");
        project.setTags("[\"Spring Boot\",\"Vue2\"]");
        project.setModules("[\"Dealer\",\"Repair\"]");
        project.setScenes("[\"Aftermarket\"]");
        when(sourceProjectMapper.selectPublicSourceProjectList(query)).thenReturn(Collections.singletonList(project));
        when(sourcePurchaseMapper.selectPaidPurchase(7L, 10L)).thenReturn(purchase());

        List<SourceProjectResponse> projects = service.listPublicProjects(7L, query);

        assertEquals(1, projects.size());
        SourceProjectResponse response = projects.get(0);
        assertEquals(Long.valueOf(10L), response.getProjectId());
        assertEquals("dms-platform", response.getSlug());
        assertEquals("Commercial DMS", response.getProjectName());
        assertEquals("¥399", response.getPriceLabel());
        assertEquals(Long.valueOf(2860L), response.getViewCount());
        assertEquals("Spring Boot", response.getTags().get(0));
        assertEquals("Repair", response.getModules().get(1));
        assertTrue(response.isPurchased());
    }

    @Test
    public void purchaseCreatesPaidPurchaseForActiveProject()
    {
        SourceProject project = project("0");
        project.setResourceUrl("https://example.com/dms.zip");
        when(sourceProjectMapper.selectSourceProjectByProjectId(10L)).thenReturn(project);

        SourcePurchase purchase = service.purchase(7L, 10L);

        ArgumentCaptor<SourcePurchase> captor = ArgumentCaptor.forClass(SourcePurchase.class);
        verify(sourcePurchaseMapper).insertSourcePurchase(captor.capture());
        assertEquals(Long.valueOf(7L), captor.getValue().getUserId());
        assertEquals(Long.valueOf(10L), captor.getValue().getProjectId());
        assertEquals(Long.valueOf(39900L), captor.getValue().getAmount());
        assertEquals("1", captor.getValue().getPayStatus());
        assertEquals("1", captor.getValue().getResourceStatus());
        assertTrue(captor.getValue().getOrderNo().startsWith("SRC"));
        assertEquals(captor.getValue(), purchase);
    }

    @Test
    public void purchaseRejectsOfflineProject()
    {
        when(sourceProjectMapper.selectSourceProjectByProjectId(10L)).thenReturn(project("1"));

        ServiceException exception = expectServiceException(new ThrowingRunnable()
        {
            @Override
            public void run()
            {
                service.purchase(7L, 10L);
            }
        });

        assertEquals("源码项目未上架或不存在", exception.getMessage());
    }

    private SourceProject project(String status)
    {
        SourceProject project = new SourceProject();
        project.setProjectId(10L);
        project.setSlug("dms-platform");
        project.setProjectName("Commercial DMS");
        project.setCategory("enterprise");
        project.setCoverText("DMS");
        project.setSummary("DMS source");
        project.setDescriptionMd("# DMS");
        project.setPrice(39900L);
        project.setViewCount(2860L);
        project.setStatus(status);
        return project;
    }

    private SourcePurchase purchase()
    {
        SourcePurchase purchase = new SourcePurchase();
        purchase.setPurchaseId(20L);
        return purchase;
    }

    private void setField(String name, Object value) throws Exception
    {
        Field field = SourceProjectServiceImpl.class.getDeclaredField(name);
        field.setAccessible(true);
        field.set(service, value);
    }

    private ServiceException expectServiceException(ThrowingRunnable runnable)
    {
        try
        {
            runnable.run();
        }
        catch (ServiceException e)
        {
            return e;
        }
        throw new AssertionError("Expected ServiceException");
    }

    private interface ThrowingRunnable
    {
        void run();
    }
}
