package com.ruoyi.info.collection.service;

import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiBaseStaffEditDTO;
import com.ruoyi.info.collection.domain.vo.CcdiBaseStaffVO;
import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper;
import com.ruoyi.info.collection.service.impl.CcdiBaseStaffServiceImpl;
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.RedisTemplate;

import java.math.BigDecimal;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
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 CcdiBaseStaffServiceImplTest {

    @InjectMocks
    private CcdiBaseStaffServiceImpl service;

    @Mock
    private CcdiBaseStaffMapper baseStaffMapper;

    @Mock
    private ICcdiBaseStaffImportService importAsyncService;

    @Mock
    private RedisTemplate<String, Object> redisTemplate;

    @Mock
    private ICcdiAssetInfoService assetInfoService;

    @Test
    void insertBaseStaff_shouldPersistEmployeeThenReplaceAssetsUsingEmployeeIdCard() {
        CcdiBaseStaffAddDTO addDTO = new CcdiBaseStaffAddDTO();
        addDTO.setStaffId(1001L);
        addDTO.setName("张三");
        addDTO.setDeptId(10L);
        addDTO.setIdCard("320101199001010011");
        addDTO.setPhone("13812345678");
        addDTO.setStatus("0");
        addDTO.setPartyMember(1);
        addDTO.setAnnualIncome(new BigDecimal("12345.67"));
        addDTO.setAssetInfoList(List.of(
                buildAssetDto("房产"),
                buildAssetDto("车辆")
        ));

        when(baseStaffMapper.selectById(1001L)).thenReturn(null);
        when(baseStaffMapper.selectCount(any())).thenReturn(0L);
        when(baseStaffMapper.insert(any(CcdiBaseStaff.class))).thenReturn(1);

        int result = service.insertBaseStaff(addDTO);

        assertEquals(1, result);
        ArgumentCaptor<CcdiBaseStaff> staffCaptor = ArgumentCaptor.forClass(CcdiBaseStaff.class);
        verify(baseStaffMapper).insert(staffCaptor.capture());
        assertEquals(1, staffCaptor.getValue().getPartyMember());
        assertEquals(new BigDecimal("12345.67"), staffCaptor.getValue().getAnnualIncome());
        ArgumentCaptor<List<CcdiAssetInfoDTO>> captor = ArgumentCaptor.forClass(List.class);
        verify(assetInfoService).replaceByFamilyId(eq("320101199001010011"), captor.capture());
        List<CcdiAssetInfoDTO> savedAssets = captor.getValue();
        assertEquals(2, savedAssets.size());
        assertEquals("房产", savedAssets.get(0).getAssetMainType());
        assertEquals("车辆", savedAssets.get(1).getAssetMainType());
    }

    @Test
    void updateBaseStaff_shouldReplaceAssetsForCurrentIdCard() {
        CcdiBaseStaff existing = new CcdiBaseStaff();
        existing.setStaffId(1001L);
        existing.setIdCard("320101199001010011");

        CcdiBaseStaffEditDTO editDTO = new CcdiBaseStaffEditDTO();
        editDTO.setStaffId(1001L);
        editDTO.setDeptId(10L);
        editDTO.setName("张三");
        editDTO.setIdCard("320101199001010011");
        editDTO.setPhone("13812345678");
        editDTO.setStatus("0");
        editDTO.setPartyMember(0);
        editDTO.setAnnualIncome(new BigDecimal("45678.90"));
        editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));

        when(baseStaffMapper.selectById(1001L)).thenReturn(existing);
        when(baseStaffMapper.selectCount(any())).thenReturn(0L);
        when(baseStaffMapper.updateById(any(CcdiBaseStaff.class))).thenReturn(1);

        int result = service.updateBaseStaff(editDTO);

        assertEquals(1, result);
        ArgumentCaptor<CcdiBaseStaff> staffCaptor = ArgumentCaptor.forClass(CcdiBaseStaff.class);
        verify(baseStaffMapper).updateById(staffCaptor.capture());
        assertEquals(0, staffCaptor.getValue().getPartyMember());
        assertEquals(new BigDecimal("45678.90"), staffCaptor.getValue().getAnnualIncome());
        verify(assetInfoService, never()).deleteByFamilyId("320101199001010011");
        verify(assetInfoService).replaceByFamilyId("320101199001010011", editDTO.getAssetInfoList());
    }

    @Test
    void updateBaseStaff_shouldDeleteOldAssetsWhenIdCardChanges() {
        CcdiBaseStaff existing = new CcdiBaseStaff();
        existing.setStaffId(1001L);
        existing.setIdCard("320101199001010099");

        CcdiBaseStaffEditDTO editDTO = new CcdiBaseStaffEditDTO();
        editDTO.setStaffId(1001L);
        editDTO.setDeptId(10L);
        editDTO.setName("张三");
        editDTO.setIdCard("320101199001010011");
        editDTO.setPhone("13812345678");
        editDTO.setStatus("0");
        editDTO.setPartyMember(1);
        editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));

        when(baseStaffMapper.selectById(1001L)).thenReturn(existing);
        when(baseStaffMapper.selectCount(any())).thenReturn(0L);
        when(baseStaffMapper.updateById(any(CcdiBaseStaff.class))).thenReturn(1);

        service.updateBaseStaff(editDTO);

        verify(assetInfoService).deleteByFamilyId("320101199001010099");
        verify(assetInfoService).replaceByFamilyId("320101199001010011", editDTO.getAssetInfoList());
    }

    @Test
    void selectBaseStaffById_shouldReturnSelfOwnedAssetInfoList() {
        CcdiBaseStaff staff = new CcdiBaseStaff();
        staff.setStaffId(1001L);
        staff.setName("张三");
        staff.setIdCard("320101199001010011");
        staff.setStatus("0");
        staff.setPartyMember(1);
        staff.setAnnualIncome(new BigDecimal("88888.88"));

        CcdiAssetInfo assetInfo = new CcdiAssetInfo();
        assetInfo.setFamilyId("320101199001010011");
        assetInfo.setPersonId("320101199001010011");
        assetInfo.setAssetMainType("车辆");
        assetInfo.setAssetSubType("小汽车");
        assetInfo.setAssetName("家庭车辆");
        assetInfo.setCurrentValue(new BigDecimal("100000.00"));
        assetInfo.setAssetStatus("正常");

        when(baseStaffMapper.selectById(1001L)).thenReturn(staff);
        when(assetInfoService.selectByFamilyIdAndPersonId("320101199001010011", "320101199001010011"))
                .thenReturn(List.of(assetInfo));

        CcdiBaseStaffVO result = service.selectBaseStaffById(1001L);

        assertNotNull(result.getAssetInfoList());
        assertEquals(1, result.getPartyMember());
        assertEquals(new BigDecimal("88888.88"), result.getAnnualIncome());
        assertEquals(1, result.getAssetInfoList().size());
        assertEquals("320101199001010011", result.getAssetInfoList().get(0).getPersonId());
        assertEquals("车辆", result.getAssetInfoList().get(0).getAssetMainType());
    }

    @Test
    void deleteBaseStaffByIds_shouldCascadeDeleteAssets() {
        CcdiBaseStaff staff1 = new CcdiBaseStaff();
        staff1.setStaffId(1001L);
        staff1.setIdCard("320101199001010011");
        CcdiBaseStaff staff2 = new CcdiBaseStaff();
        staff2.setStaffId(1002L);
        staff2.setIdCard("320101199001010022");

        when(baseStaffMapper.selectBatchIds(List.of(1001L, 1002L))).thenReturn(List.of(staff1, staff2));
        when(baseStaffMapper.deleteBatchIds(List.of(1001L, 1002L))).thenReturn(2);

        int result = service.deleteBaseStaffByIds(new Long[]{1001L, 1002L});

        assertEquals(2, result);
        verify(assetInfoService).deleteByFamilyIds(List.of("320101199001010011", "320101199001010022"));
    }

    private CcdiAssetInfoDTO buildAssetDto(String assetMainType) {
        CcdiAssetInfoDTO dto = new CcdiAssetInfoDTO();
        dto.setAssetMainType(assetMainType);
        dto.setAssetSubType(assetMainType + "小类");
        dto.setAssetName(assetMainType + "名称");
        dto.setCurrentValue(new BigDecimal("100.00"));
        dto.setAssetStatus("正常");
        return dto;
    }
}
