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.integration.common.manager.DeviceConfigManager;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.repository.DeviceRepository;
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.*;

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

/**
 * FilterByDevicePhotoLimitStage 单元测试
 */
@ExtendWith(MockitoExtension.class)
class FilterByDevicePhotoLimitStageTest {

    @InjectMocks
    private FilterByDevicePhotoLimitStage stage;

    @Mock
    private DeviceRepository deviceRepository;

    @Mock
    private DeviceConfigManager deviceConfigManager;

    private FaceMatchingContext context;

    @BeforeEach
    void setUp() {
        context = FaceMatchingContext.forCustomMatching(1L, Arrays.asList(101L, 102L));
    }

    @Test
    void testExecute_Success_FilterByLimit() {
        // Given: 设备1有5个样本,限制为2张
        when(deviceRepository.getDeviceConfigManager(1L)).thenReturn(deviceConfigManager);
        when(deviceConfigManager.getInteger("limit_photo")).thenReturn(2);

        Date baseTime = new Date();
        FaceSampleEntity sample1 = createSample(101L, 1L, new Date(baseTime.getTime() - 5000));
        FaceSampleEntity sample2 = createSample(102L, 1L, new Date(baseTime.getTime() - 4000));
        FaceSampleEntity sample3 = createSample(103L, 1L, new Date(baseTime.getTime() - 3000));
        FaceSampleEntity sample4 = createSample(104L, 1L, new Date(baseTime.getTime() - 2000));
        FaceSampleEntity sample5 = createSample(105L, 1L, new Date(baseTime.getTime() - 1000));

        context.setFaceSamples(Arrays.asList(sample1, sample2, sample3, sample4, sample5));
        context.setSampleListIds(Arrays.asList(101L, 102L, 103L, 104L, 105L));

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

        // Then
        assertTrue(result.isSuccess());
        List<Long> filteredIds = context.getSampleListIds();
        assertEquals(2, filteredIds.size()); // 限制为2张
    }

    @Test
    void testExecute_EmptyFaceSamples_Skip() {
        // Given
        context.setFaceSamples(Collections.emptyList());
        context.setSampleListIds(Arrays.asList(101L, 102L));

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

        // Then
        assertTrue(result.isSkipped());
    }

    @Test
    void testExecute_NullFaceSamples_Skip() {
        // Given
        context.setFaceSamples(null);
        context.setSampleListIds(Arrays.asList(101L, 102L));

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

        // Then
        assertTrue(result.isSkipped());
    }

    @Test
    void testExecute_NoLimitPhoto_KeepAll() {
        // Given: 设备配置limit_photo为null,不限制
        when(deviceRepository.getDeviceConfigManager(1L)).thenReturn(deviceConfigManager);
        when(deviceConfigManager.getInteger("limit_photo")).thenReturn(null);

        FaceSampleEntity sample1 = createSample(101L, 1L, new Date());
        FaceSampleEntity sample2 = createSample(102L, 1L, new Date());
        FaceSampleEntity sample3 = createSample(103L, 1L, new Date());

        context.setFaceSamples(Arrays.asList(sample1, sample2, sample3));
        context.setSampleListIds(Arrays.asList(101L, 102L, 103L));

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

        // Then
        assertTrue(result.isSuccess());
        assertEquals(3, context.getSampleListIds().size()); // 全部保留
    }

    @Test
    void testExecute_LimitPhotoZero_KeepAll() {
        // Given: limit_photo=0,不限制
        when(deviceRepository.getDeviceConfigManager(1L)).thenReturn(deviceConfigManager);
        when(deviceConfigManager.getInteger("limit_photo")).thenReturn(0);

        FaceSampleEntity sample1 = createSample(101L, 1L, new Date());
        FaceSampleEntity sample2 = createSample(102L, 1L, new Date());

        context.setFaceSamples(Arrays.asList(sample1, sample2));
        context.setSampleListIds(Arrays.asList(101L, 102L));

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

        // Then
        assertTrue(result.isSuccess());
        assertEquals(2, context.getSampleListIds().size());
    }

    @Test
    void testExecute_MultipleDevices() {
        // Given: 设备1限制2张,设备2限制3张
        when(deviceRepository.getDeviceConfigManager(1L)).thenReturn(deviceConfigManager);
        when(deviceConfigManager.getInteger("limit_photo")).thenReturn(2);

        DeviceConfigManager deviceConfig2 = mock(DeviceConfigManager.class);
        when(deviceRepository.getDeviceConfigManager(2L)).thenReturn(deviceConfig2);
        when(deviceConfig2.getInteger("limit_photo")).thenReturn(3);

        Date baseTime = new Date();
        FaceSampleEntity sample1 = createSample(101L, 1L, new Date(baseTime.getTime() - 3000));
        FaceSampleEntity sample2 = createSample(102L, 1L, new Date(baseTime.getTime() - 2000));
        FaceSampleEntity sample3 = createSample(103L, 1L, new Date(baseTime.getTime() - 1000));
        FaceSampleEntity sample4 = createSample(104L, 2L, new Date(baseTime.getTime() - 5000));
        FaceSampleEntity sample5 = createSample(105L, 2L, new Date(baseTime.getTime() - 4000));
        FaceSampleEntity sample6 = createSample(106L, 2L, new Date(baseTime.getTime() - 3000));
        FaceSampleEntity sample7 = createSample(107L, 2L, new Date(baseTime.getTime() - 2000));

        context.setFaceSamples(Arrays.asList(sample1, sample2, sample3, sample4, sample5, sample6, sample7));
        context.setSampleListIds(Arrays.asList(101L, 102L, 103L, 104L, 105L, 106L, 107L));

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

        // Then
        assertTrue(result.isSuccess());
        List<Long> filteredIds = context.getSampleListIds();
        assertTrue(filteredIds.size() <= 5); // 设备1最多2张 + 设备2最多3张
    }

    @Test
    void testExecute_SampleWithoutDeviceId_Passthrough() {
        // Given: 有样本没有deviceId,应该直接保留
        when(deviceRepository.getDeviceConfigManager(1L)).thenReturn(deviceConfigManager);
        when(deviceConfigManager.getInteger("limit_photo")).thenReturn(1);

        FaceSampleEntity sample1 = createSample(101L, 1L, new Date());
        FaceSampleEntity sample2 = createSample(102L, 1L, new Date());
        FaceSampleEntity sample3 = createSample(103L, null, new Date()); // 无deviceId

        context.setFaceSamples(Arrays.asList(sample1, sample2, sample3));
        context.setSampleListIds(Arrays.asList(101L, 102L, 103L));

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

        // Then
        assertTrue(result.isSuccess());
        List<Long> filteredIds = context.getSampleListIds();
        assertTrue(filteredIds.contains(103L)); // 无deviceId的样本被保留
        assertTrue(filteredIds.size() >= 2); // 至少包含103和设备1的1张样本
    }

    @Test
    void testExecute_Exception_Degraded() {
        // Given
        when(deviceRepository.getDeviceConfigManager(anyLong())).thenThrow(new RuntimeException("DB error"));

        FaceSampleEntity sample1 = createSample(101L, 1L, new Date());
        context.setFaceSamples(Arrays.asList(sample1));
        context.setSampleListIds(Arrays.asList(101L));

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

        // Then
        assertTrue(result.isDegraded());
        assertTrue(result.getMessage().contains("设备照片数量限制筛选失败"));
    }

    private FaceSampleEntity createSample(Long id, Long deviceId, Date createAt) {
        FaceSampleEntity sample = new FaceSampleEntity();
        sample.setId(id);
        sample.setDeviceId(deviceId);
        sample.setCreateAt(createAt);
        return sample;
    }
}
