package com.ruoyi.info.collection.service;

import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
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.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiBaseStaffImportServiceImplTest {

    @InjectMocks
    private CcdiBaseStaffImportServiceImpl service;

    @Mock
    private SysDeptMapper deptMapper;

    @Test
    void validateStaffData_shouldAllowEmptyAnnualIncome() {
        mockNormalDept();
        assertDoesNotThrow(() -> service.validateStaffData(buildDto(null), Collections.emptySet(), Collections.emptySet()));
    }

    @Test
    void validateStaffData_shouldAllowZeroAndTwoDecimalAnnualIncome() {
        mockNormalDept();
        assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("0.00")), Collections.emptySet(), Collections.emptySet()));
        assertDoesNotThrow(() -> service.validateStaffData(buildDto(new BigDecimal("12345.67")), Collections.emptySet(), Collections.emptySet()));
    }

    @Test
    void validateStaffData_shouldAllowPartyMemberValuesZeroAndOne() {
        mockNormalDept();
        CcdiBaseStaffAddDTO nonPartyMember = buildDto(null);
        nonPartyMember.setPartyMember(0);
        CcdiBaseStaffAddDTO partyMember = buildDto(null);
        partyMember.setPartyMember(1);

        assertDoesNotThrow(() -> service.validateStaffData(nonPartyMember, Collections.emptySet(), Collections.emptySet()));
        assertDoesNotThrow(() -> service.validateStaffData(partyMember, Collections.emptySet(), Collections.emptySet()));
    }

    @Test
    void validateStaffData_shouldRejectInvalidPartyMemberValue() {
        mockNormalDept();
        CcdiBaseStaffAddDTO dto = buildDto(null);
        dto.setPartyMember(2);

        RuntimeException exception = assertThrows(RuntimeException.class,
                () -> service.validateStaffData(dto, Set.of(), Set.of()));

        assertEquals("是否党员只能填写'0'或'1'", exception.getMessage());
    }

    @Test
    void validateStaffData_shouldRejectNegativeAnnualIncome() {
        mockNormalDept();
        RuntimeException exception = assertThrows(RuntimeException.class,
                () -> service.validateStaffData(buildDto(new BigDecimal("-1.00")), Set.of(), Set.of()));

        assertEquals("年收入不能为负数", exception.getMessage());
    }

    @Test
    void validateStaffData_shouldRejectAnnualIncomeWithMoreThanTwoDecimals() {
        mockNormalDept();
        RuntimeException exception = assertThrows(RuntimeException.class,
                () -> service.validateStaffData(buildDto(new BigDecimal("12.345")), Set.of(), Set.of()));

        assertEquals("年收入最多保留2位小数", exception.getMessage());
    }

    @Test
    void validateStaffData_shouldAllowWhenDeptIsNormalAndNotDeleted() {
        mockNormalDept();
        assertDoesNotThrow(() -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));
    }

    @Test
    void validateStaffData_shouldRejectWhenDeptDoesNotExist() {
        when(deptMapper.selectDeptById(10L)).thenReturn(null);

        RuntimeException exception = assertThrows(RuntimeException.class,
                () -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));

        assertEquals("所属部门ID[10]不存在或已停用/删除，请检查机构号", exception.getMessage());
    }

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

        RuntimeException exception = assertThrows(RuntimeException.class,
                () -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));

        assertEquals("所属部门ID[10]不存在或已停用/删除，请检查机构号", exception.getMessage());
    }

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

        RuntimeException exception = assertThrows(RuntimeException.class,
                () -> service.validateStaffData(buildDto(null), Set.of(), Set.of()));

        assertEquals("所属部门ID[10]不存在或已停用/删除，请检查机构号", exception.getMessage());
    }

    private CcdiBaseStaffAddDTO buildDto(BigDecimal annualIncome) {
        CcdiBaseStaffAddDTO dto = new CcdiBaseStaffAddDTO();
        dto.setName("张三");
        dto.setStaffId(1001L);
        dto.setDeptId(10L);
        dto.setIdCard("320101199001010014");
        dto.setPhone("13812345678");
        dto.setStatus("0");
        dto.setPartyMember(1);
        dto.setAnnualIncome(annualIncome);
        return dto;
    }

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

    private void mockNormalDept() {
        lenient().when(deptMapper.selectDeptById(10L)).thenReturn(buildDept("0", "0"));
    }
}
