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

import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.pipeline.core.StageResult;
import com.ycwl.basic.mapper.FaceMapper;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.repository.FaceRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigDecimal;
import java.util.Arrays;

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

/**
 * UpdateFaceResultStage 单元测试
 */
@ExtendWith(MockitoExtension.class)
class UpdateFaceResultStageTest {

    @Mock
    private FaceMapper faceMapper;

    @Mock
    private FaceRepository faceRepository;

    @InjectMocks
    private UpdateFaceResultStage stage;

    private FaceMatchingContext context;
    private FaceEntity originalFace;

    @BeforeEach
    void setUp() {
        context = FaceMatchingContext.forAutoMatching(1L, true);

        originalFace = new FaceEntity();
        originalFace.setId(1L);
        originalFace.setScenicId(10L);
        originalFace.setMemberId(100L);
        originalFace.setFaceUrl("http://example.com/face.jpg");

        context.setFace(originalFace);
    }

    @Test
    void testExecute_Success() {
        // Given
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.85f);
        searchResult.setFirstMatchRate(0.92f);
        searchResult.setSampleListIds(Arrays.asList(101L, 102L, 103L));
        searchResult.setSearchResultJson("{\"score\":0.85}");
        context.setSearchResult(searchResult);

        ArgumentCaptor<FaceEntity> faceCaptor = ArgumentCaptor.forClass(FaceEntity.class);

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

        // Then
        assertTrue(result.isSuccess());
        verify(faceMapper, times(1)).update(faceCaptor.capture());
        verify(faceRepository, times(1)).clearFaceCache(1L);

        FaceEntity updatedFace = faceCaptor.getValue();
        assertEquals(1L, updatedFace.getId());
        assertEquals(0.85f, updatedFace.getScore(), 0.0001);
        assertEquals("{\"score\":0.85}", updatedFace.getMatchResult());
        assertEquals(BigDecimal.valueOf(0.92f), updatedFace.getFirstMatchRate());
        assertEquals("101,102,103", updatedFace.getMatchSampleIds());
        assertEquals(10L, updatedFace.getScenicId());
        assertEquals(100L, updatedFace.getMemberId());
        assertEquals("http://example.com/face.jpg", updatedFace.getFaceUrl());
        assertNotNull(updatedFace.getCreateAt());
    }

    @Test
    void testExecute_SearchResultNull_Skipped() {
        // Given
        context.setSearchResult(null);

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

        // Then
        assertTrue(result.isSkipped());
        assertTrue(result.getMessage().contains("searchResult为空"));
        verify(faceMapper, never()).update(any());
        verify(faceRepository, never()).clearFaceCache(any());
    }

    @Test
    void testExecute_NoFirstMatchRate() {
        // Given
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.75f);
        searchResult.setFirstMatchRate(null); // 无firstMatchRate
        searchResult.setSampleListIds(Arrays.asList(101L));
        context.setSearchResult(searchResult);

        ArgumentCaptor<FaceEntity> faceCaptor = ArgumentCaptor.forClass(FaceEntity.class);

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

        // Then
        assertTrue(result.isSuccess());
        verify(faceMapper, times(1)).update(faceCaptor.capture());

        FaceEntity updatedFace = faceCaptor.getValue();
        assertNull(updatedFace.getFirstMatchRate());
    }

    @Test
    void testExecute_NoSampleListIds() {
        // Given
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.65f);
        searchResult.setSampleListIds(null); // 无样本ID列表
        context.setSearchResult(searchResult);

        ArgumentCaptor<FaceEntity> faceCaptor = ArgumentCaptor.forClass(FaceEntity.class);

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

        // Then
        assertTrue(result.isSuccess());
        verify(faceMapper, times(1)).update(faceCaptor.capture());

        FaceEntity updatedFace = faceCaptor.getValue();
        assertNull(updatedFace.getMatchSampleIds());
    }

    @Test
    void testExecute_EmptySampleListIds() {
        // Given
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.55f);
        searchResult.setSampleListIds(Arrays.asList()); // 空列表
        context.setSearchResult(searchResult);

        ArgumentCaptor<FaceEntity> faceCaptor = ArgumentCaptor.forClass(FaceEntity.class);

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

        // Then
        assertTrue(result.isSuccess());
        verify(faceMapper, times(1)).update(faceCaptor.capture());

        FaceEntity updatedFace = faceCaptor.getValue();
        assertEquals("", updatedFace.getMatchSampleIds()); // 空列表连接为空字符串
    }

    @Test
    void testExecute_MapperUpdateFailed() {
        // Given
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.85f);
        searchResult.setSampleListIds(Arrays.asList(101L));
        context.setSearchResult(searchResult);

        doThrow(new RuntimeException("Database error")).when(faceMapper).update(any());

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

        // Then
        assertTrue(result.isFailed());
        assertTrue(result.getMessage().contains("保存人脸识别结果失败"));
        assertNotNull(result.getException());
        verify(faceRepository, never()).clearFaceCache(any()); // 更新失败时不应清缓存
    }

    @Test
    void testExecute_CacheClearFailed() {
        // Given
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.85f);
        searchResult.setSampleListIds(Arrays.asList(101L));
        context.setSearchResult(searchResult);

        // faceMapper.update()正常执行(不需要mock void方法)
        doThrow(new RuntimeException("Cache clear error")).when(faceRepository).clearFaceCache(any());

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

        // Then
        assertTrue(result.isFailed()); // 缓存清理失败导致整体失败
        verify(faceMapper, times(1)).update(any()); // Mapper仍然被调用了
    }

    @Test
    void testExecute_HighScore() {
        // Given: 高分匹配
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.98f);
        searchResult.setFirstMatchRate(0.99f);
        searchResult.setSampleListIds(Arrays.asList(101L));
        context.setSearchResult(searchResult);

        ArgumentCaptor<FaceEntity> faceCaptor = ArgumentCaptor.forClass(FaceEntity.class);

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

        // Then
        assertTrue(result.isSuccess());
        verify(faceMapper, times(1)).update(faceCaptor.capture());

        FaceEntity updatedFace = faceCaptor.getValue();
        assertEquals(0.98f, updatedFace.getScore(), 0.0001);
    }

    @Test
    void testExecute_MultipleSamples() {
        // Given: 多个样本
        SearchFaceRespVo searchResult = new SearchFaceRespVo();
        searchResult.setScore(0.78f);
        searchResult.setSampleListIds(Arrays.asList(101L, 102L, 103L, 104L, 105L));
        context.setSearchResult(searchResult);

        ArgumentCaptor<FaceEntity> faceCaptor = ArgumentCaptor.forClass(FaceEntity.class);

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

        // Then
        assertTrue(result.isSuccess());
        verify(faceMapper, times(1)).update(faceCaptor.capture());

        FaceEntity updatedFace = faceCaptor.getValue();
        assertEquals("101,102,103,104,105", updatedFace.getMatchSampleIds());
    }
}
