package com.ruoyi.info.collection.service;

import com.ruoyi.info.collection.domain.CcdiBizIntermediary;
import com.ruoyi.info.collection.domain.CcdiIntermediaryEnterpriseRelation;
import com.ruoyi.info.collection.domain.excel.CcdiIntermediaryEnterpriseRelationExcel;
import com.ruoyi.info.collection.domain.vo.IntermediaryEnterpriseRelationImportFailureVO;
import com.ruoyi.info.collection.mapper.CcdiBizIntermediaryMapper;
import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper;
import com.ruoyi.info.collection.service.impl.CcdiIntermediaryEnterpriseRelationImportServiceImpl;
import com.ruoyi.info.collection.service.support.EnterpriseAutoFillService;
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.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CcdiIntermediaryEnterpriseRelationImportServiceImplTest {

    @InjectMocks
    private CcdiIntermediaryEnterpriseRelationImportServiceImpl service;

    @Mock
    private CcdiIntermediaryEnterpriseRelationMapper relationMapper;

    @Mock
    private CcdiBizIntermediaryMapper intermediaryMapper;

    @Mock
    private EnterpriseAutoFillService enterpriseAutoFillService;

    @Mock
    private RedisTemplate<String, Object> redisTemplate;

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

    @Mock
    private ValueOperations<String, Object> valueOperations;

    @Test
    void importEnterpriseRelationAsync_shouldFailWhenOwnerPersonIdDoesNotExist() {
        CcdiIntermediaryEnterpriseRelationExcel excel = buildExcel("320101199001010014", "91330100MA27X12345");
        prepareFailureRedisMocks();
        when(intermediaryMapper.selectList(any())).thenReturn(List.of());

        service.importAsync(List.of(excel), "task-owner-miss", "tester");

        verify(relationMapper, never()).insertBatch(any());
        verify(enterpriseAutoFillService, never()).ensureExistsBatch(any());
        IntermediaryEnterpriseRelationImportFailureVO failure =
            firstFailure("import:intermediary-enterprise-relation:task-owner-miss:failures");
        assertEquals("320101199001010014", failure.getOwnerPersonId());
        assertTrue(failure.getErrorMessage().contains("中介本人不存在"));
    }

    @Test
    void importEnterpriseRelationAsync_shouldAutoFillWhenEnterpriseDoesNotExist() {
        CcdiIntermediaryEnterpriseRelationExcel excel = buildExcel("320101199001010014", "91330100MA27X12345");
        prepareStatusRedisMock();
        when(intermediaryMapper.selectList(any())).thenReturn(List.of(owner("owner-biz", "320101199001010014")));
        when(relationMapper.batchExistsByCombinations(any())).thenReturn(List.of());

        service.importAsync(List.of(excel), "task-ent-miss", "tester");

        ArgumentCaptor<List<CcdiIntermediaryEnterpriseRelation>> relationCaptor = ArgumentCaptor.forClass(List.class);
        verify(relationMapper).insertBatch(relationCaptor.capture());
        assertEquals(1, relationCaptor.getValue().size());
        assertEquals("owner-biz", relationCaptor.getValue().get(0).getIntermediaryBizId());
        assertEquals("91330100MA27X12345", relationCaptor.getValue().get(0).getSocialCreditCode());
        assertIntermediaryAutoFill("91330100MA27X12345");
    }

    @Test
    void importEnterpriseRelationAsync_shouldRejectFileDuplicateAndDbDuplicate() {
        CcdiIntermediaryEnterpriseRelationExcel duplicateInDb = buildExcel("320101199001010014", "91330100MA27X12345");
        CcdiIntermediaryEnterpriseRelationExcel duplicateInFile1 = buildExcel("320101199003030035", "91330100MA27X12346");
        CcdiIntermediaryEnterpriseRelationExcel duplicateInFile2 = buildExcel("320101199003030035", "91330100MA27X12346");
        prepareFailureRedisMocks();
        when(intermediaryMapper.selectList(any())).thenReturn(List.of(
            owner("owner-biz-1", "320101199001010014"),
            owner("owner-biz-2", "320101199003030035")
        ));
        when(relationMapper.batchExistsByCombinations(any())).thenReturn(List.of("owner-biz-1|91330100MA27X12345"));

        service.importAsync(List.of(duplicateInDb, duplicateInFile1, duplicateInFile2), "task-duplicate", "tester");

        ArgumentCaptor<List<CcdiIntermediaryEnterpriseRelation>> captor = ArgumentCaptor.forClass(List.class);
        verify(relationMapper).insertBatch(captor.capture());
        assertEquals(1, captor.getValue().size());
        assertEquals("owner-biz-2", captor.getValue().get(0).getIntermediaryBizId());
        assertIntermediaryAutoFill("91330100MA27X12346");
        IntermediaryEnterpriseRelationImportFailureVO failure =
            firstFailure("import:intermediary-enterprise-relation:task-duplicate:failures");
        assertTrue(failure.getErrorMessage().contains("重复") || failure.getErrorMessage().contains("已存在"));
    }

    @Test
    void importEnterpriseRelationAsync_shouldImportSuccessWhenOwnerAndEnterpriseExist() {
        CcdiIntermediaryEnterpriseRelationExcel excel = buildExcel("320101199001010014", "91330100MA27X12345");
        prepareStatusRedisMock();
        when(intermediaryMapper.selectList(any())).thenReturn(List.of(owner("owner-biz", "320101199001010014")));
        when(relationMapper.batchExistsByCombinations(any())).thenReturn(List.of());

        service.importAsync(List.of(excel), "task-success", "tester");

        ArgumentCaptor<List<CcdiIntermediaryEnterpriseRelation>> captor = ArgumentCaptor.forClass(List.class);
        verify(relationMapper).insertBatch(captor.capture());
        assertEquals(1, captor.getValue().size());
        assertEquals("owner-biz", captor.getValue().get(0).getIntermediaryBizId());
        assertIntermediaryAutoFill("91330100MA27X12345");
        verify(valueOperations, never()).set(any(), any(), any(Long.class), any(TimeUnit.class));
    }

    private void prepareStatusRedisMock() {
        when(redisTemplate.opsForHash()).thenReturn(hashOperations);
    }

    private void prepareFailureRedisMocks() {
        prepareStatusRedisMock();
        when(redisTemplate.opsForValue()).thenReturn(valueOperations);
    }

    private IntermediaryEnterpriseRelationImportFailureVO firstFailure(String key) {
        ArgumentCaptor<Object> failureCaptor = ArgumentCaptor.forClass(Object.class);
        verify(valueOperations).set(org.mockito.ArgumentMatchers.eq(key), failureCaptor.capture(),
            org.mockito.ArgumentMatchers.eq(7L), org.mockito.ArgumentMatchers.eq(TimeUnit.DAYS));
        return (IntermediaryEnterpriseRelationImportFailureVO) ((List<?>) failureCaptor.getValue()).get(0);
    }

    private CcdiIntermediaryEnterpriseRelationExcel buildExcel(String ownerPersonId, String socialCreditCode) {
        CcdiIntermediaryEnterpriseRelationExcel excel = new CcdiIntermediaryEnterpriseRelationExcel();
        excel.setOwnerPersonId(ownerPersonId);
        excel.setSocialCreditCode(socialCreditCode);
        excel.setRelationPersonPost("董事");
        excel.setRemark("备注");
        return excel;
    }

    private CcdiBizIntermediary owner(String bizId, String personId) {
        CcdiBizIntermediary owner = new CcdiBizIntermediary();
        owner.setBizId(bizId);
        owner.setPersonId(personId);
        owner.setPersonSubType("本人");
        return owner;
    }

    private void assertIntermediaryAutoFill(String socialCreditCode) {
        ArgumentCaptor<List<EnterpriseAutoFillService.EnterpriseFillItem>> captor = ArgumentCaptor.forClass(List.class);
        verify(enterpriseAutoFillService).ensureExistsBatch(captor.capture());
        assertEquals(1, captor.getValue().size());
        EnterpriseAutoFillService.EnterpriseFillItem item = captor.getValue().get(0);
        assertEquals(socialCreditCode, item.socialCreditCode());
        assertNull(item.enterpriseName());
        assertEquals("INTERMEDIARY", item.entSource());
        assertEquals("IMPORT", item.dataSource());
        assertEquals("tester", item.userName());
    }
}
