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.face.pipeline.enums.FaceMatchingScene;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.pc.ScenicService;
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 static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

/**
 * PrepareContextStage 单元测试
 */
@ExtendWith(MockitoExtension.class)
class PrepareContextStageTest {

    @Mock
    private FaceRepository faceRepository;

    @Mock
    private ScenicRepository scenicRepository;

    @Mock
    private ScenicService scenicService;

    @Mock
    private ScenicConfigManager configManager;

    @Mock
    private IFaceBodyAdapter faceBodyAdapter;

    @InjectMocks
    private PrepareContextStage stage;

    private FaceMatchingContext context;

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

    @Test
    void testExecute_Success() {
        // Given
        FaceEntity face = createFace(1L, 100L, 10L, 0); // isManual=0
        when(faceRepository.getFace(1L)).thenReturn(face);
        when(scenicRepository.getScenicConfigManager(10L)).thenReturn(configManager);
        when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);

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

        // Then
        assertTrue(result.isSuccess());
        assertEquals(face, context.getFace());
        assertEquals(configManager, context.getScenicConfig());
        assertEquals(faceBodyAdapter, context.getFaceBodyAdapter());
        verify(faceRepository, times(1)).getFace(1L);
        verify(scenicRepository, times(1)).getScenicConfigManager(10L);
        verify(scenicService, times(1)).getScenicFaceBodyAdapter(10L);
    }

    @Test
    void testExecute_FaceNotFound_Failed() {
        // Given
        when(faceRepository.getFace(1L)).thenReturn(null);

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

        // Then
        assertTrue(result.isFailed());
        assertTrue(result.getMessage().contains("人脸不存在"));
        assertNull(context.getFace());
    }

    @Test
    void testExecute_ManualFaceAndNotNew_Skipped() {
        // Given: isManual=1, isNew=false
        context = FaceMatchingContext.forAutoMatching(1L, false);
        FaceEntity face = createFace(1L, 100L, 10L, 1); // isManual=1
        when(faceRepository.getFace(1L)).thenReturn(face);

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

        // Then
        assertTrue(result.isSkipped());
        assertTrue(result.getMessage().contains("人工选择的人脸"));
        assertEquals(face, context.getFace());
        // 人工选择的人脸跳过，不加载景区配置
        verify(scenicRepository, never()).getScenicConfigManager(anyLong());
        verify(scenicService, never()).getScenicFaceBodyAdapter(anyLong());
    }

    @Test
    void testExecute_ManualFaceButIsNew_Success() {
        // Given: isManual=1, isNew=true (新用户即使人工选择也需要匹配)
        context = FaceMatchingContext.forAutoMatching(1L, true);
        FaceEntity face = createFace(1L, 100L, 10L, 1); // isManual=1
        when(faceRepository.getFace(1L)).thenReturn(face);
        when(scenicRepository.getScenicConfigManager(10L)).thenReturn(configManager);
        when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);

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

        // Then
        assertTrue(result.isSuccess());
        assertEquals(face, context.getFace());
        assertEquals(configManager, context.getScenicConfig());
        assertEquals(faceBodyAdapter, context.getFaceBodyAdapter());
    }

    @Test
    void testExecute_FaceBodyAdapterNotAvailable_Failed() {
        // Given
        FaceEntity face = createFace(1L, 100L, 10L, 0);
        when(faceRepository.getFace(1L)).thenReturn(face);
        when(scenicRepository.getScenicConfigManager(10L)).thenReturn(configManager);
        when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(null); // 无适配器

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

        // Then
        assertTrue(result.isFailed());
        assertTrue(result.getMessage().contains("人脸识别服务不可用"));
        assertEquals(face, context.getFace());
        assertEquals(configManager, context.getScenicConfig());
        assertNull(context.getFaceBodyAdapter());
    }

    @Test
    void testExecute_AutoMatchingOldUser() {
        // Given
        context = FaceMatchingContext.forAutoMatching(1L, false); // 老用户
        FaceEntity face = createFace(1L, 100L, 10L, 0);
        when(faceRepository.getFace(1L)).thenReturn(face);
        when(scenicRepository.getScenicConfigManager(10L)).thenReturn(configManager);
        when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);

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

        // Then
        assertTrue(result.isSuccess());
        assertFalse(context.isNew());
    }

    @Test
    void testExecute_RecognitionOnlyScene() {
        // Given
        context = FaceMatchingContext.forRecognitionOnly(1L);
        FaceEntity face = createFace(1L, 100L, 10L, 0);
        when(faceRepository.getFace(1L)).thenReturn(face);
        when(scenicRepository.getScenicConfigManager(10L)).thenReturn(configManager);
        when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);

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

        // Then
        assertTrue(result.isSuccess());
        assertEquals(FaceMatchingScene.RECOGNITION_ONLY, context.getScene());
    }

    private FaceEntity createFace(Long id, Long memberId, Long scenicId, Integer isManual) {
        FaceEntity face = new FaceEntity();
        face.setId(id);
        face.setMemberId(memberId);
        face.setScenicId(scenicId);
        face.setIsManual(isManual);
        face.setFaceUrl("http://example.com/face.jpg");
        return face;
    }
}
