package com.ruoyi.generator.service.front;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.lang.reflect.Field;
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.front.AiGenerationTask;
import com.ruoyi.generator.domain.front.FrontProject;
import com.ruoyi.generator.domain.front.dto.AiGenerationTaskCreateRequest;
import com.ruoyi.generator.domain.front.dto.AiGenerationTaskStatusResponse;
import com.ruoyi.generator.mapper.front.AiGenerationTaskMapper;
import com.ruoyi.generator.mapper.front.FrontProjectMapper;

public class AiGenerationTaskServiceImplTest
{
    private AiGenerationTaskServiceImpl service;

    @Mock
    private FrontProjectMapper frontProjectMapper;
    @Mock
    private AiGenerationTaskMapper aiGenerationTaskMapper;
    @Mock
    private AiQuotaService aiQuotaService;

    @Before
    public void setUp() throws Exception
    {
        MockitoAnnotations.initMocks(this);
        service = new AiGenerationTaskServiceImpl();
        setField("frontProjectMapper", frontProjectMapper);
        setField("aiGenerationTaskMapper", aiGenerationTaskMapper);
        setField("aiQuotaService", aiQuotaService);
    }

    @Test
    public void createTaskQueuesDatabaseGenerationAndReservesQuota()
    {
        when(frontProjectMapper.selectFrontProjectByUserAndId(7L, 10L)).thenReturn(project());
        org.mockito.Mockito.doAnswer(invocation -> {
            AiGenerationTask task = invocation.getArgument(0);
            task.setTaskId(88L);
            return 1;
        }).when(aiGenerationTaskMapper).insertAiGenerationTask(any(AiGenerationTask.class));

        AiGenerationTaskStatusResponse response = service.createTask(7L, 10L, request("database"));

        ArgumentCaptor<AiGenerationTask> taskCaptor = ArgumentCaptor.forClass(AiGenerationTask.class);
        verify(aiQuotaService).reserve(7L);
        verify(aiGenerationTaskMapper).insertAiGenerationTask(taskCaptor.capture());
        AiGenerationTask task = taskCaptor.getValue();
        assertEquals(Long.valueOf(10L), task.getProjectId());
        assertEquals(Long.valueOf(7L), task.getUserId());
        assertEquals("database", task.getGenerateType());
        assertEquals("QUEUED", task.getStatus());
        assertEquals(Integer.valueOf(0), task.getProgress());
        assertEquals(Integer.valueOf(0), task.getAttempts());
        assertEquals(Integer.valueOf(3), task.getMaxAttempts());
        assertNotNull(task.getRequestHash());
        assertEquals(Long.valueOf(88L), response.getTaskId());
        assertEquals("QUEUED", response.getStatus());
    }

    @Test
    public void createTaskReusesRunningTaskWithSameRequestHash()
    {
        when(frontProjectMapper.selectFrontProjectByUserAndId(7L, 10L)).thenReturn(project());
        AiGenerationTask existing = new AiGenerationTask();
        existing.setTaskId(99L);
        existing.setProjectId(10L);
        existing.setUserId(7L);
        existing.setGenerateType("database");
        existing.setStatus("RUNNING");
        existing.setProgress(35);
        when(aiGenerationTaskMapper.selectActiveTaskByRequestHash(anyLong(), anyLong(), org.mockito.ArgumentMatchers.anyString()))
                .thenReturn(existing);

        AiGenerationTaskStatusResponse response = service.createTask(7L, 10L, request("database"));

        verify(aiQuotaService, never()).reserve(anyLong());
        verify(aiGenerationTaskMapper, never()).insertAiGenerationTask(any(AiGenerationTask.class));
        assertEquals(Long.valueOf(99L), response.getTaskId());
        assertEquals("RUNNING", response.getStatus());
        assertEquals(Integer.valueOf(35), response.getProgress());
    }

    @Test(expected = ServiceException.class)
    public void createTaskRejectsMissingProjectOwnership()
    {
        when(frontProjectMapper.selectFrontProjectByUserAndId(7L, 10L)).thenReturn(null);

        service.createTask(7L, 10L, request("database"));
    }

    @Test
    public void getTaskReturnsOnlyOwnedTask()
    {
        AiGenerationTask task = new AiGenerationTask();
        task.setTaskId(99L);
        task.setProjectId(10L);
        task.setUserId(7L);
        task.setGenerateType("database");
        task.setStatus("SUCCEEDED");
        task.setResultPayload("{\"tables\":[]}");
        when(aiGenerationTaskMapper.selectTaskForUser(7L, 10L, 99L)).thenReturn(task);

        AiGenerationTaskStatusResponse response = service.getTask(7L, 10L, 99L);

        assertSame(task.getTaskId(), response.getTaskId());
        assertEquals("SUCCEEDED", response.getStatus());
        assertEquals("{\"tables\":[]}", response.getResultPayload());
    }

    private AiGenerationTaskCreateRequest request(String generateType)
    {
        AiGenerationTaskCreateRequest request = new AiGenerationTaskCreateRequest();
        request.setGenerateType(generateType);
        request.setProjectName("客户中心");
        request.setProjectDesc("管理客户资料");
        request.setIndustryTemplate("crm");
        request.setExtraRequirements("客户分层");
        request.setAppBlueprint("{\"roles\":[]}");
        return request;
    }

    private FrontProject project()
    {
        FrontProject project = new FrontProject();
        project.setProjectId(10L);
        project.setUserId(7L);
        project.setProjectName("客户中心");
        return project;
    }

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