package com.ruoyi.info.collection.service;

import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffAssetInfoExcel;
import com.ruoyi.info.collection.domain.vo.BaseStaffAssetImportFailureVO;
import com.ruoyi.info.collection.domain.vo.ImportStatusVO;
import com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper;
import com.ruoyi.info.collection.service.impl.CcdiBaseStaffAssetImportServiceImpl;
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 org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiBaseStaffAssetImportServiceImplTest {

    @InjectMocks
    private CcdiBaseStaffAssetImportServiceImpl service;

    @Mock
    private CcdiAssetInfoMapper assetInfoMapper;

    @Mock
    private RedisTemplate<String, Object> redisTemplate;

    @Mock
    private ICcdiBaseStaffAssetImportService baseStaffAssetImportService;

    @Mock
    private HashOperations<String, Object, Object> hashOperations;

    @Mock
    private ValueOperations<String, Object> valueOperations;

    @Test
    void importAssetInfo_shouldUseDedicatedBaseStaffAssetTaskKeys() {
        List<CcdiBaseStaffAssetInfoExcel> excelList = List.of(buildExcel("320101199001010011", "房产"));
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);

        String taskId = service.importAssetInfo(excelList);

        verify(hashOperations).putAll(eq("import:baseStaffAsset:" + taskId), anyMap());
        verify(redisTemplate).expire("import:baseStaffAsset:" + taskId, 7, TimeUnit.DAYS);
        verify(baseStaffAssetImportService).importAssetInfoAsync(eq(excelList), eq(taskId), any());
    }

    @Test
    void importAssetInfoAsync_shouldImportWhenEmployeeIdCardExists() {
        CcdiBaseStaffAssetInfoExcel excel = buildExcel("320101199001010011", "房产");
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        when(assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(List.of("320101199001010011")))
                .thenReturn(List.of(owner("320101199001010011", "320101199001010011")));

        service.importAssetInfoAsync(List.of(excel), "task-1", "tester");

        ArgumentCaptor<List<CcdiAssetInfo>> captor = ArgumentCaptor.forClass(List.class);
        verify(assetInfoMapper).insertBatch(captor.capture());
        assertEquals("320101199001010011", captor.getValue().get(0).getFamilyId());
        assertEquals("320101199001010011", captor.getValue().get(0).getPersonId());
    }

    @Test
    void importAssetInfoSync_shouldImportWhenOwnerComesFromCurrentWorkbook() {
        CcdiBaseStaffAssetInfoExcel excel = buildExcel("320101199001010033", "存款");
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        when(assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(List.of("320101199001010033")))
                .thenReturn(List.of());

        service.importAssetInfoSync(
                List.of(excel),
                "task-current-workbook",
                "tester",
                Map.of("320101199001010033", Set.of("320101199001010033"))
        );

        ArgumentCaptor<List<CcdiAssetInfo>> captor = ArgumentCaptor.forClass(List.class);
        verify(assetInfoMapper).insertBatch(captor.capture());
        assertEquals("320101199001010033", captor.getValue().get(0).getFamilyId());
        assertEquals("320101199001010033", captor.getValue().get(0).getPersonId());
    }

    @Test
    void importAssetInfoAsync_shouldFailWhenFamilyCertificateIsUsed() {
        CcdiBaseStaffAssetInfoExcel excel = buildExcel("320101199201010022", "车辆");
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        when(assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(List.of("320101199201010022")))
                .thenReturn(List.of());

        service.importAssetInfoAsync(List.of(excel), "task-2", "tester");

        verify(assetInfoMapper, never()).insertBatch(any());
        ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
        verify(valueOperations).set(eq("import:baseStaffAsset:task-2:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
        BaseStaffAssetImportFailureVO failure = (BaseStaffAssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
        assertEquals("员工资产信息", failure.getSheetName());
        assertEquals(2, failure.getRowNum());
        assertEquals("320101199201010022", failure.getPersonId());
        assertTrue(failure.getErrorMessage().contains("员工资产导入仅支持员工本人证件号"));
    }

    @Test
    void importAssetInfoAsync_shouldFailWhenAssetAlreadyExists() {
        CcdiBaseStaffAssetInfoExcel excel = buildExcel("320101199001010011", "房产");
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        when(assetInfoMapper.selectOwnerCandidatesByBaseStaffIdCards(List.of("320101199001010011")))
                .thenReturn(List.of(owner("320101199001010011", "320101199001010011")));

        CcdiAssetInfo existing = new CcdiAssetInfo();
        existing.setFamilyId("320101199001010011");
        existing.setPersonId("320101199001010011");
        existing.setAssetMainType("房产");
        existing.setAssetSubType("房产小类");
        existing.setAssetName("房产名称");
        when(assetInfoMapper.selectList(any())).thenReturn(List.of(existing));

        service.importAssetInfoAsync(List.of(excel), "task-duplicate", "tester");

        verify(assetInfoMapper, never()).insertBatch(any());
        ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
        verify(valueOperations).set(eq("import:baseStaffAsset:task-duplicate:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
        BaseStaffAssetImportFailureVO failure = (BaseStaffAssetImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
        assertEquals("员工资产信息", failure.getSheetName());
        assertEquals(2, failure.getRowNum());
        assertTrue(failure.getErrorMessage().contains("资产记录已存在"));
    }

    @Test
    void getImportStatusAndFailures_shouldUseBaseStaffAssetPrefixes() {
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        when(redisTemplate.hasKey("import:baseStaffAsset:task-3")).thenReturn(true);
        when(hashOperations.entries("import:baseStaffAsset:task-3")).thenReturn(Map.of(
                "taskId", "task-3",
                "status", "SUCCESS",
                "totalCount", 1,
                "successCount", 1,
                "failureCount", 0,
                "progress", 100,
                "startTime", 1L,
                "endTime", 2L,
                "message", "全部成功"
        ));
        BaseStaffAssetImportFailureVO failureVO = new BaseStaffAssetImportFailureVO();
        failureVO.setPersonId("320101199001010099");
        failureVO.setErrorMessage("员工资产导入仅支持员工本人证件号");
        when(valueOperations.get("import:baseStaffAsset:task-3:failures")).thenReturn(List.of(failureVO));

        ImportStatusVO statusVO = service.getImportStatus("task-3");
        List<BaseStaffAssetImportFailureVO> failures = service.getImportFailures("task-3");

        assertEquals("task-3", statusVO.getTaskId());
        assertEquals("SUCCESS", statusVO.getStatus());
        assertNotNull(failures);
        assertEquals(1, failures.size());
        assertEquals("320101199001010099", failures.get(0).getPersonId());
    }

    private CcdiBaseStaffAssetInfoExcel buildExcel(String personId, String assetMainType) {
        CcdiBaseStaffAssetInfoExcel excel = new CcdiBaseStaffAssetInfoExcel();
        excel.setPersonId(personId);
        excel.setAssetMainType(assetMainType);
        excel.setAssetSubType(assetMainType + "小类");
        excel.setAssetName(assetMainType + "名称");
        excel.setCurrentValue(new BigDecimal("100.00"));
        excel.setAssetStatus("正常");
        return excel;
    }

    private Map<String, String> owner(String personId, String familyId) {
        return Map.of("personId", personId, "familyId", familyId);
    }
}
