package com.ruoyi.info.collection.service;

import com.ruoyi.info.collection.domain.CcdiAssetInfo;
import com.ruoyi.info.collection.domain.dto.CcdiAssetInfoDTO;
import com.ruoyi.info.collection.mapper.CcdiAssetInfoMapper;
import com.ruoyi.info.collection.service.impl.CcdiAssetInfoServiceImpl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.invocation.Invocation;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockingDetails;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiAssetInfoServiceImplTest {

    @InjectMocks
    private CcdiAssetInfoServiceImpl service;

    @Mock
    private CcdiAssetInfoMapper assetInfoMapper;

    @Test
    void selectByFamilyId_shouldReturnMapperResult() {
        List<CcdiAssetInfo> expected = List.of(new CcdiAssetInfo());
        when(assetInfoMapper.selectByFamilyId("320101199001010011")).thenReturn(expected);

        List<CcdiAssetInfo> result = service.selectByFamilyId("320101199001010011");

        assertSame(expected, result);
    }

    @Test
    void replaceByFamilyIdAndPersonId_shouldDeleteThenInsertNormalizedRows() throws Exception {
        CcdiAssetInfoDTO familyOwnedAsset = buildDto("房产");
        CcdiAssetInfoDTO anotherAsset = buildDto("车辆");

        Method method = CcdiAssetInfoServiceImpl.class.getMethod(
                "replaceByFamilyIdAndPersonId", String.class, String.class, List.class);
        method.invoke(service, "320101199001010011", "A123456789", List.of(familyOwnedAsset, anotherAsset));

        Invocation deleteInvocation = mockingDetails(assetInfoMapper).getInvocations().stream()
                .filter(invocation -> "deleteByFamilyIdAndPersonId".equals(invocation.getMethod().getName()))
                .findFirst()
                .orElseThrow();
        assertEquals("320101199001010011", deleteInvocation.getArguments()[0]);
        assertEquals("A123456789", deleteInvocation.getArguments()[1]);
        ArgumentCaptor<List<CcdiAssetInfo>> captor = ArgumentCaptor.forClass(List.class);
        verify(assetInfoMapper).insertBatch(captor.capture());

        List<CcdiAssetInfo> savedList = captor.getValue();
        assertEquals(2, savedList.size());
        assertEquals("320101199001010011", savedList.get(0).getFamilyId());
        assertEquals("A123456789", savedList.get(0).getPersonId());
        assertEquals("320101199001010011", savedList.get(1).getFamilyId());
        assertEquals("A123456789", savedList.get(1).getPersonId());
        assertEquals("房产", savedList.get(0).getAssetMainType());
        assertEquals("车辆", savedList.get(1).getAssetMainType());
    }

    @Test
    void replaceByFamilyIdAndPersonId_shouldIgnoreEmptyRows() throws Exception {
        CcdiAssetInfoDTO emptyRow = new CcdiAssetInfoDTO();

        Method method = CcdiAssetInfoServiceImpl.class.getMethod(
                "replaceByFamilyIdAndPersonId", String.class, String.class, List.class);
        method.invoke(service, "320101199001010011", "A123456789", List.of(emptyRow));

        Invocation deleteInvocation = mockingDetails(assetInfoMapper).getInvocations().stream()
                .filter(invocation -> "deleteByFamilyIdAndPersonId".equals(invocation.getMethod().getName()))
                .findFirst()
                .orElseThrow();
        assertEquals("320101199001010011", deleteInvocation.getArguments()[0]);
        assertEquals("A123456789", deleteInvocation.getArguments()[1]);
        verify(assetInfoMapper, never()).insertBatch(anyList());
    }

    @Test
    void replaceByFamilyIdAndPersonId_shouldValidateRequiredFields() throws Exception {
        CcdiAssetInfoDTO invalid = new CcdiAssetInfoDTO();
        invalid.setAssetMainType("房产");
        invalid.setAssetSubType("商品房");
        invalid.setAssetName("测试房产");
        invalid.setAssetStatus("正常");

        Method method = CcdiAssetInfoServiceImpl.class.getMethod(
                "replaceByFamilyIdAndPersonId", String.class, String.class, List.class);
        InvocationTargetException exception = assertThrows(InvocationTargetException.class,
                () -> method.invoke(service, "320101199001010011", "A123456789", List.of(invalid)));

        assertEquals("当前估值不能为空", exception.getCause().getMessage());
    }

    @Test
    void deleteByFamilyIds_shouldDelegateToMapper() {
        List<String> familyIds = List.of("320101199001010011", "320101199001010033");
        when(assetInfoMapper.deleteByFamilyIds(familyIds)).thenReturn(2);

        int result = service.deleteByFamilyIds(familyIds);

        assertEquals(2, result);
        verify(assetInfoMapper).deleteByFamilyIds(eq(familyIds));
    }

    private CcdiAssetInfoDTO buildDto(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;
    }
}
