package com.ycwl.basic.face.pipeline.stages;

import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.service.pc.helper.SearchResultMerger;
import com.ycwl.basic.service.task.TaskFaceService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * CustomFaceSearchStage 单元测试
 */
@ExtendWith(MockitoExtension.class)
class CustomFaceSearchStageTest {

    @Mock
    private TaskFaceService taskFaceService;

    @Mock
    private SearchResultMerger resultMerger;

    @Mock
    private IFaceBodyAdapter faceBodyAdapter;

    @InjectMocks
    private CustomFaceSearchStage stage;

    private FaceMatchingContext context;
    private FaceEntity face;

    @BeforeEach
    void setUp() {
        context = FaceMatchingContext.forCustomMatching(1L, Arrays.asList(101L, 102L));
        face = new FaceEntity();
        face.setId(1L);
        face.setScenicId(10L);
        face.setFaceUrl("http://example.com/face.jpg");
        context.setFace(face);
        context.setFaceBodyAdapter(faceBodyAdapter);
    }

    @Test
    void testExecute_Mode2_DirectUse() {
        // Given: 模式2，直接使用用户选择的样本
        context.setFaceSelectPostMode(2);
        context.setFaceSampleIds(Arrays.asList(101L, 102L, 103L));

        SearchFaceRespVo directResult = new SearchFaceRespVo();
        directResult.setSampleListIds(Arrays.asList(101L, 102L, 103L));

        when(resultMerger.createDirectResult(Arrays.asList(101L, 102L, 103L)))
                .thenReturn(directResult);

        // When
        StageResult<FaceMatchingContext> result = stage.execute(context);

        // Then
        assertTrue(result.isSuccess());
        assertEquals(directResult, context.getSearchResult());
        assertEquals(3, context.getSampleListIds().size());
        verify(resultMerger, times(1)).createDirectResult(anyList());
        verify(taskFaceService, never()).searchFace(any(), anyString(), anyString(), anyString());
    }

    @Test
    void testExecute_Mode2_WithMatchResult() {
        // Given: 模式2，保留原始matchResult
        context.setFaceSelectPostMode(2);
        context.setFaceSampleIds(Arrays.asList(101L, 102L));
        face.setMatchResult("{\"score\":0.85}");

        SearchFaceRespVo directResult = new SearchFaceRespVo();
        directResult.setSampleListIds(Arrays.asList(101L, 102L));

        when(resultMerger.createDirectResult(anyList())).thenReturn(directResult);

        // When
        StageResult<FaceMatchingContext> result = stage.execute(context);

        // Then
        assertTrue(result.isSuccess());
        assertEquals("{\"score\":0.85}", directResult.getSearchResultJson());
    }

    @Test
    void testExecute_Mode0_Union() throws Exception {
        // Given: 模式0（并集），搜索多个样本并合并
        context.setFaceSelectPostMode(0);

        FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
        FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
        context.setFaceSamples(Arrays.asList(sample1, sample2));
        context.setFaceSampleIds(Arrays.asList(101L, 102L));

        SearchFaceRespVo searchResult1 = new SearchFaceRespVo();
        searchResult1.setSampleListIds(Arrays.asList(201L, 202L));

        SearchFaceRespVo searchResult2 = new SearchFaceRespVo();
        searchResult2.setSampleListIds(Arrays.asList(202L, 203L));

        when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s1.jpg"), anyString()))
                .thenReturn(searchResult1);
        when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s2.jpg"), anyString()))
                .thenReturn(searchResult2);

        SearchFaceRespVo mergedResult = new SearchFaceRespVo();
        mergedResult.setSampleListIds(Arrays.asList(201L, 202L, 203L)); // 并集

        when(resultMerger.merge(anyList(), eq(0))).thenReturn(mergedResult);

        // When
        StageResult<FaceMatchingContext> result = stage.execute(context);

        // Then
        assertTrue(result.isSuccess());
        assertEquals(mergedResult, context.getSearchResult());
        assertEquals(3, context.getSampleListIds().size());
        verify(taskFaceService, times(2)).searchFace(any(), anyString(), anyString(), anyString());
        verify(resultMerger, times(1)).merge(anyList(), eq(0));
    }

    @Test
    void testExecute_Mode1_Intersection() throws Exception {
        // Given: 模式1（交集）
        context.setFaceSelectPostMode(1);

        FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
        FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
        context.setFaceSamples(Arrays.asList(sample1, sample2));
        context.setFaceSampleIds(Arrays.asList(101L, 102L));

        SearchFaceRespVo searchResult1 = new SearchFaceRespVo();
        searchResult1.setSampleListIds(Arrays.asList(201L, 202L, 203L));

        SearchFaceRespVo searchResult2 = new SearchFaceRespVo();
        searchResult2.setSampleListIds(Arrays.asList(202L, 203L, 204L));

        when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
                .thenReturn(searchResult1, searchResult2);

        SearchFaceRespVo mergedResult = new SearchFaceRespVo();
        mergedResult.setSampleListIds(Arrays.asList(202L, 203L)); // 交集

        when(resultMerger.merge(anyList(), eq(1))).thenReturn(mergedResult);

        // When
        StageResult<FaceMatchingContext> result = stage.execute(context);

        // Then
        assertTrue(result.isSuccess());
        assertEquals(2, context.getSampleListIds().size());
        verify(resultMerger, times(1)).merge(anyList(), eq(1));
    }

    @Test
    void testExecute_DefaultMode() throws Exception {
        // Given: 未设置faceSelectPostMode，默认为0
        context.setFaceSelectPostMode(null);

        FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
        context.setFaceSamples(Arrays.asList(sample1));
        context.setFaceSampleIds(Arrays.asList(101L));

        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setSampleListIds(Arrays.asList(201L));

        when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
                .thenReturn(searchResult);

        SearchFaceRespVo mergedResult = new SearchFaceRespVo();
        mergedResult.setSampleListIds(Arrays.asList(201L));

        when(resultMerger.merge(anyList(), eq(0))).thenReturn(mergedResult);

        // When
        StageResult<FaceMatchingContext> result = stage.execute(context);

        // Then
        assertTrue(result.isSuccess());
        verify(resultMerger, times(1)).merge(anyList(), eq(0)); // 默认模式0
    }

    @Test
    void testExecute_PartialSearchFailure_Success() throws Exception {
        // Given: 部分样本搜索失败，但至少有一个成功
        context.setFaceSelectPostMode(0);

        FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
        FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
        FaceSampleEntity sample3 = createSample(103L, "http://example.com/s3.jpg");
        context.setFaceSamples(Arrays.asList(sample1, sample2, sample3));

        SearchFaceRespVo searchResult1 = new SearchFaceRespVo();
        searchResult1.setSampleListIds(Arrays.asList(201L));

        when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s1.jpg"), anyString()))
                .thenReturn(searchResult1);
        when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s2.jpg"), anyString()))
                .thenThrow(new RuntimeException("Network error"));
        when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s3.jpg"), anyString()))
                .thenReturn(null);

        SearchFaceRespVo mergedResult = new SearchFaceRespVo();
        mergedResult.setSampleListIds(Arrays.asList(201L));

        when(resultMerger.merge(anyList(), eq(0))).thenReturn(mergedResult);

        // When
        StageResult<FaceMatchingContext> result = stage.execute(context);

        // Then
        assertTrue(result.isSuccess());
        verify(taskFaceService, times(3)).searchFace(any(), anyString(), anyString(), anyString());
    }

    @Test
    void testExecute_AllSearchFailure_ThrowException() throws Exception {
        // Given: 所有样本搜索都失败
        context.setFaceSelectPostMode(0);

        FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
        FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
        context.setFaceSamples(Arrays.asList(sample1, sample2));

        when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
                .thenThrow(new RuntimeException("Network error"));

        // When & Then
        assertThrows(BaseException.class, () -> {
            stage.execute(context);
        });
    }

    @Test
    void testExecute_BaseException_Rethrow() throws Exception {
        // Given
        context.setFaceSelectPostMode(0);

        FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
        context.setFaceSamples(Arrays.asList(sample1));

        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
                .thenReturn(searchResult);

        BaseException baseException = new BaseException("服务不可用");
        when(resultMerger.merge(anyList(), anyInt())).thenThrow(baseException);

        // When & Then
        assertThrows(BaseException.class, () -> {
            stage.execute(context);
        });
    }

    @Test
    void testExecute_GenericException_Failed() throws Exception {
        // Given
        context.setFaceSelectPostMode(0);

        FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
        context.setFaceSamples(Arrays.asList(sample1));

        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
                .thenReturn(searchResult);

        when(resultMerger.merge(anyList(), anyInt())).thenThrow(new RuntimeException("Merge error"));

        // When
        StageResult<FaceMatchingContext> result = stage.execute(context);

        // Then
        assertTrue(result.isFailed());
        assertTrue(result.getMessage().contains("自定义人脸搜索失败"));
    }

    private FaceSampleEntity createSample(Long id, String faceUrl) {
        FaceSampleEntity sample = new FaceSampleEntity();
        sample.setId(id);
        sample.setFaceUrl(faceUrl);
        return sample;
    }
}
