package com.ruoyi.info.collection.service;

import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import com.ruoyi.info.collection.domain.CcdiStaffFmyRelation;
import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiStaffFmyRelationEditDTO;
import com.ruoyi.info.collection.domain.vo.CcdiStaffFmyRelationVO;
import com.ruoyi.info.collection.mapper.CcdiStaffEnterpriseRelationMapper;
import com.ruoyi.info.collection.mapper.CcdiStaffFmyRelationMapper;
import com.ruoyi.info.collection.service.impl.CcdiStaffFmyRelationServiceImpl;
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.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiStaffFmyRelationServiceImplTest {

    @InjectMocks
    private CcdiStaffFmyRelationServiceImpl service;

    @Mock
    private CcdiStaffFmyRelationMapper relationMapper;

    @Mock
    private ICcdiStaffFmyRelationImportService relationImportService;

    @Mock
    private RedisTemplate<String, Object> redisTemplate;

    @Mock
    private ICcdiAssetInfoService assetInfoService;

    @Mock
    private CcdiStaffEnterpriseRelationMapper staffEnterpriseRelationMapper;

    @Test
    void selectRelationById_shouldAggregateAssetInfoList() {
        CcdiStaffFmyRelationVO relationVO = new CcdiStaffFmyRelationVO();
        relationVO.setId(10L);
        relationVO.setPersonId("320101199001010011");
        relationVO.setRelationCertNo("A123456789");
        CcdiAssetInfo assetInfo = new CcdiAssetInfo();
        assetInfo.setFamilyId("320101199001010011");
        assetInfo.setPersonId("A123456789");
        assetInfo.setAssetMainType("房产");

        when(relationMapper.selectRelationById(10L)).thenReturn(relationVO);
        when(assetInfoService.selectByFamilyIdAndPersonId("320101199001010011", "A123456789"))
                .thenReturn(List.of(assetInfo));

        CcdiStaffFmyRelationVO result = service.selectRelationById(10L);

        assertNotNull(result.getAssetInfoList());
        assertEquals(1, result.getAssetInfoList().size());
        assertEquals("A123456789", result.getAssetInfoList().get(0).getPersonId());
        assertEquals("房产", result.getAssetInfoList().get(0).getAssetMainType());
    }

    @Test
    void insertRelation_shouldSaveRelationThenReplaceRelativeAssets() {
        CcdiStaffFmyRelationAddDTO addDTO = new CcdiStaffFmyRelationAddDTO();
        addDTO.setPersonId("320101199001010011");
        addDTO.setRelationType("配偶");
        addDTO.setRelationName("李四");
        addDTO.setRelationCertType("护照");
        addDTO.setRelationCertNo("A123456789");
        addDTO.setAnnualIncome(new BigDecimal("23456.78"));
        addDTO.setAssetInfoList(List.of(buildAssetDto("房产")));

        when(relationMapper.insert(any(CcdiStaffFmyRelation.class))).thenReturn(1);

        int result = service.insertRelation(addDTO);

        assertEquals(1, result);
        ArgumentCaptor<CcdiStaffFmyRelation> relationCaptor = ArgumentCaptor.forClass(CcdiStaffFmyRelation.class);
        verify(relationMapper).insert(relationCaptor.capture());
        assertEquals("MANUAL", relationCaptor.getValue().getDataSource());
        assertEquals(Boolean.TRUE, relationCaptor.getValue().getIsEmpFamily());
        assertEquals(Boolean.FALSE, relationCaptor.getValue().getIsCustFamily());
        assertEquals(new BigDecimal("23456.78"), relationCaptor.getValue().getAnnualIncome());
        verify(assetInfoService).replaceByFamilyIdAndPersonId("320101199001010011", "A123456789", addDTO.getAssetInfoList());
    }

    @Test
    void updateRelation_shouldRejectRelationCertChange() {
        CcdiStaffFmyRelation existing = new CcdiStaffFmyRelation();
        existing.setId(10L);
        existing.setRelationCertType("护照");
        existing.setRelationCertNo("A123456789");

        CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO();
        editDTO.setId(10L);
        editDTO.setPersonId("320101199001010011");
        editDTO.setRelationType("配偶");
        editDTO.setRelationName("李四");
        editDTO.setRelationCertType("身份证");
        editDTO.setRelationCertNo("A123456789");

        when(relationMapper.selectById(10L)).thenReturn(existing);

        RuntimeException exception = assertThrows(RuntimeException.class, () -> service.updateRelation(editDTO));

        assertEquals("关系人证件类型/证件号码不允许修改", exception.getMessage());
        verify(relationMapper, never()).updateById(any(CcdiStaffFmyRelation.class));
        verify(assetInfoService, never()).replaceByFamilyIdAndPersonId(any(), any(), any());
    }

    @Test
    void updateRelation_shouldRejectRelationCertNoChange() {
        CcdiStaffFmyRelation existing = new CcdiStaffFmyRelation();
        existing.setId(10L);
        existing.setRelationCertType("护照");
        existing.setRelationCertNo("A123456789");

        CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO();
        editDTO.setId(10L);
        editDTO.setPersonId("320101199001010011");
        editDTO.setRelationType("配偶");
        editDTO.setRelationName("李四");
        editDTO.setRelationCertType("护照");
        editDTO.setRelationCertNo("B987654321");

        when(relationMapper.selectById(10L)).thenReturn(existing);

        RuntimeException exception = assertThrows(RuntimeException.class, () -> service.updateRelation(editDTO));

        assertEquals("关系人证件类型/证件号码不允许修改", exception.getMessage());
        verify(relationMapper, never()).updateById(any(CcdiStaffFmyRelation.class));
        verify(assetInfoService, never()).replaceByFamilyIdAndPersonId(any(), any(), any());
    }

    @Test
    void updateRelation_shouldReplaceAssetsByOwnershipKey() {
        CcdiStaffFmyRelation existing = new CcdiStaffFmyRelation();
        existing.setId(10L);
        existing.setRelationCertType("护照");
        existing.setRelationCertNo("A123456789");

        CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO();
        editDTO.setId(10L);
        editDTO.setPersonId("320101199001010011");
        editDTO.setRelationType("配偶");
        editDTO.setRelationName("李四");
        editDTO.setRelationCertType("护照");
        editDTO.setRelationCertNo("A123456789");
        editDTO.setAnnualIncome(new BigDecimal("76543.21"));
        editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));

        when(relationMapper.selectById(10L)).thenReturn(existing);
        when(relationMapper.updateById(any(CcdiStaffFmyRelation.class))).thenReturn(1);

        int result = service.updateRelation(editDTO);

        assertEquals(1, result);
        ArgumentCaptor<CcdiStaffFmyRelation> relationCaptor = ArgumentCaptor.forClass(CcdiStaffFmyRelation.class);
        verify(relationMapper).updateById(relationCaptor.capture());
        assertEquals(new BigDecimal("76543.21"), relationCaptor.getValue().getAnnualIncome());
        verify(assetInfoService).replaceByFamilyIdAndPersonId("320101199001010011", "A123456789", editDTO.getAssetInfoList());
    }

    @Test
    void updateRelation_shouldInvalidateEnterpriseRelationsWhenFamilyBecomesInvalid() {
        CcdiStaffFmyRelation existing = new CcdiStaffFmyRelation();
        existing.setId(10L);
        existing.setRelationCertType("护照");
        existing.setRelationCertNo("A123456789");
        existing.setStatus(1);

        CcdiStaffFmyRelationEditDTO editDTO = new CcdiStaffFmyRelationEditDTO();
        editDTO.setId(10L);
        editDTO.setPersonId("320101199001010011");
        editDTO.setRelationType("配偶");
        editDTO.setRelationName("李四");
        editDTO.setRelationCertType("护照");
        editDTO.setRelationCertNo("A123456789");
        editDTO.setStatus(0);
        editDTO.setAssetInfoList(List.of(buildAssetDto("车辆")));

        when(relationMapper.selectById(10L)).thenReturn(existing);
        when(relationMapper.updateById(any(CcdiStaffFmyRelation.class))).thenReturn(1);

        int result = service.updateRelation(editDTO);

        assertEquals(1, result);
        var order = inOrder(relationMapper, staffEnterpriseRelationMapper, assetInfoService);
        order.verify(relationMapper).updateById(any(CcdiStaffFmyRelation.class));
        order.verify(staffEnterpriseRelationMapper).invalidateByFamilyCertNo("A123456789");
        order.verify(assetInfoService).replaceByFamilyIdAndPersonId("320101199001010011", "A123456789", editDTO.getAssetInfoList());
    }

    @Test
    void deleteRelationByIds_shouldDeleteRelativeAssetsBeforeDeletingRelations() {
        CcdiStaffFmyRelation relation1 = new CcdiStaffFmyRelation();
        relation1.setId(10L);
        relation1.setPersonId("320101199001010011");
        relation1.setRelationCertNo("A123456789");
        CcdiStaffFmyRelation relation2 = new CcdiStaffFmyRelation();
        relation2.setId(11L);
        relation2.setPersonId("320101199001010022");
        relation2.setRelationCertNo("B987654321");

        when(relationMapper.selectBatchIds(List.of(10L, 11L))).thenReturn(List.of(relation1, relation2));
        when(relationMapper.deleteBatchIds(List.of(10L, 11L))).thenReturn(2);

        int result = service.deleteRelationByIds(new Long[]{10L, 11L});

        assertEquals(2, result);
        var order = inOrder(assetInfoService, relationMapper);
        order.verify(assetInfoService).deleteByFamilyIdAndPersonId("320101199001010011", "A123456789");
        order.verify(assetInfoService).deleteByFamilyIdAndPersonId("320101199001010022", "B987654321");
        order.verify(relationMapper).deleteBatchIds(List.of(10L, 11L));
    }

    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;
    }
}
