package com.ruoyi.info.collection.service;

import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.vo.ImportFailureVO;
import com.ruoyi.info.collection.service.impl.CcdiBaseStaffImportServiceImpl;
import com.ruoyi.system.mapper.SysDeptMapper;
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.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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiBaseStaffDualImportServiceTest {

    @InjectMocks
    private CcdiBaseStaffImportServiceImpl service;

    @Mock
    private com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper baseStaffMapper;

    @Mock
    private RedisTemplate<String, Object> redisTemplate;

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

    @Mock
    private ValueOperations<String, Object> valueOperations;

    @Mock
    private SysDeptMapper deptMapper;

    @Test
    void importBaseStaffAsync_shouldTreatExistingEmployeeAsFailureInsteadOfUpdate() {
        CcdiBaseStaffExcel excel = new CcdiBaseStaffExcel();
        excel.setStaffId(1001L);
        excel.setName("张三");
        excel.setDeptId(10L);
        excel.setIdCard("11010519491231002X");
        excel.setPhone("13812345678");
        excel.setStatus("0");
        excel.setPartyMember(1);

        CcdiBaseStaff existing = new CcdiBaseStaff();
        existing.setStaffId(1001L);
        existing.setIdCard("11010519491231002X");

        when(baseStaffMapper.selectBatchIds(List.of(1001L))).thenReturn(List.of(existing));
        when(baseStaffMapper.selectList(any())).thenReturn(List.of(existing));
        lenient().when(deptMapper.selectDeptById(10L)).thenReturn(buildDept(10L, "0", "0"));
        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);

        service.importBaseStaffAsync(List.of(excel), "task-existing");

        verify(baseStaffMapper, never()).insertBatch(any());
        verify(baseStaffMapper, never()).insertOrUpdateBatch(any());

        ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
        verify(valueOperations).set(eq("import:baseStaff:task-existing:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
        ImportFailureVO failure = (ImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
        assertEquals("员工信息", failure.getSheetName());
        assertEquals(2, failure.getRowNum());
        assertEquals(1001L, failure.getStaffId());
        assertEquals("该员工ID已存在", failure.getErrorMessage());
    }

    @Test
    void validateStaffData_shouldRejectExistingIdCardWhenStaffIdDoesNotExist() {
        when(deptMapper.selectDeptById(10L)).thenReturn(buildDept(10L, "0", "0"));

        RuntimeException exception = org.junit.jupiter.api.Assertions.assertThrows(
                RuntimeException.class,
                () -> service.validateStaffData(buildExcelDto(), Set.of(), Set.of("11010519491231002X"))
        );

        assertEquals("该身份证号已存在", exception.getMessage());
    }

    @Test
    void importBaseStaffAsync_shouldSaveFailureWhenDeptIsInvalid() {
        CcdiBaseStaffExcel validExcel = buildExcel(1001L, 10L, "11010519491231002X");
        CcdiBaseStaffExcel invalidExcel = buildExcel(1002L, 99L, "320101199001010014");

        when(baseStaffMapper.selectBatchIds(List.of(1001L, 1002L))).thenReturn(List.of());
        when(baseStaffMapper.selectList(any())).thenReturn(List.of());
        when(deptMapper.selectDeptById(10L)).thenReturn(buildDept(10L, "0", "0"));
        when(deptMapper.selectDeptById(99L)).thenReturn(null);
        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);

        service.importBaseStaffAsync(List.of(validExcel, invalidExcel), "task-invalid-dept");

        verify(baseStaffMapper).insertBatch(any());

        ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
        verify(valueOperations).set(eq("import:baseStaff:task-invalid-dept:failures"), failureCaptor.capture(), eq(7L), eq(TimeUnit.DAYS));
        ImportFailureVO failure = (ImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
        assertEquals("员工信息", failure.getSheetName());
        assertEquals(3, failure.getRowNum());
        assertEquals(1002L, failure.getStaffId());
        assertEquals("所属部门ID[99]不存在或已停用/删除，请检查机构号", failure.getErrorMessage());

        ArgumentCaptor<Map<String, Object>> statusCaptor = ArgumentCaptor.forClass(Map.class);
        verify(hashOperations).putAll(eq("import:baseStaff:task-invalid-dept"), statusCaptor.capture());
        assertEquals("PARTIAL_SUCCESS", statusCaptor.getValue().get("status"));
        assertEquals(1, statusCaptor.getValue().get("successCount"));
        assertEquals(1, statusCaptor.getValue().get("failureCount"));
    }

    private com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO buildExcelDto() {
        com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO dto = new com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO();
        dto.setName("李四");
        dto.setStaffId(2001L);
        dto.setDeptId(10L);
        dto.setIdCard("11010519491231002X");
        dto.setPhone("13812345678");
        dto.setStatus("0");
        dto.setPartyMember(1);
        return dto;
    }

    private CcdiBaseStaffExcel buildExcel(Long staffId, Long deptId, String idCard) {
        CcdiBaseStaffExcel excel = new CcdiBaseStaffExcel();
        excel.setStaffId(staffId);
        excel.setName("张三");
        excel.setDeptId(deptId);
        excel.setIdCard(idCard);
        excel.setPhone("13812345678");
        excel.setStatus("0");
        excel.setPartyMember(1);
        return excel;
    }

    private SysDept buildDept(Long deptId, String status, String delFlag) {
        SysDept dept = new SysDept();
        dept.setDeptId(deptId);
        dept.setDeptName("测试部门");
        dept.setStatus(status);
        dept.setDelFlag(delFlag);
        return dept;
    }
}
