package com.ruoyi.info.collection.service;

import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
import com.ruoyi.info.collection.service.impl.CcdiCreditInfoServiceImpl;
import com.ruoyi.info.collection.service.support.CreditHtmlStorageService;
import com.ruoyi.info.collection.service.support.CreditInfoPayloadAssembler;
import com.ruoyi.lsfx.client.CreditParseClient;
import com.ruoyi.lsfx.domain.response.CreditParseInvokeData;
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.util.ReflectionTestUtils;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

@ExtendWith(MockitoExtension.class)
class CcdiCreditInfoServiceImplTest {

    @InjectMocks
    private CcdiCreditInfoServiceImpl service;

    @Mock
    private CreditParseClient creditParseClient;

    @Mock
    private CreditHtmlStorageService creditHtmlStorageService;

    @Mock
    private CreditInfoPayloadAssembler assembler;

    @Mock
    private CcdiDebtsInfoMapper debtsInfoMapper;

    @Mock
    private CcdiCreditNegativeInfoMapper negativeInfoMapper;

    @Mock
    private CcdiCreditInfoQueryMapper queryMapper;

    @Test
    void uploadHtmlFiles_shouldStoreCreditObjectWithoutStaffBinding() throws Exception {
        MockMultipartFile file = new MockMultipartFile(
                "files", "family.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));

        when(creditHtmlStorageService.save(any()))
                .thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
                        "/profile/credit-html/2026/05/12/family_1.html",
                        "http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html"));
        when(creditParseClient.parse(anyString()))
                .thenReturn(successResponse("330101199202020022", "李四", "2026-03-24"));
        when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
                .thenReturn(List.of(buildDebt("330101199202020022")));
        when(assembler.buildNegative(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
                .thenReturn(buildNegative("330101199202020022"));

        CreditInfoUploadResultVO result = service.upload(List.of(file));

        assertEquals(1, result.getSuccessCount());
        assertEquals(0, result.getFailureCount());
        verify(creditParseClient).parse("http://127.0.0.1:62318/profile/credit-html/2026/05/12/family_1.html");
        verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
        verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
    }

    @Test
    void uploadHtmlFiles_shouldRejectOlderReportDate() throws Exception {
        MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));

        when(creditHtmlStorageService.save(any()))
                .thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
                        "/profile/credit-html/2026/05/12/a_1.html",
                        "http://127.0.0.1:62318/profile/credit-html/2026/05/12/a_1.html"));
        when(creditParseClient.parse(anyString()))
                .thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
        when(queryMapper.selectLatestQueryDate("330101199001010011"))
                .thenReturn(LocalDate.parse("2026-03-05"));

        CreditInfoUploadResultVO result = service.upload(List.of(file));

        assertEquals(0, result.getSuccessCount());
        assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
    }

    @Test
    void uploadHtmlFiles_shouldRejectInvalidPlatformCode() throws Exception {
        MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));

        when(creditHtmlStorageService.save(any()))
                .thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
                        "/profile/credit-html/2026/05/12/a_1.html",
                        "http://127.0.0.1:62318/profile/credit-html/2026/05/12/a_1.html"));
        CreditParseInvokeResponse response = successResponse("330101199001010011", "张三", "2026-03-03");
        response.setCode(99999);
        when(creditParseClient.parse(anyString())).thenReturn(response);

        CreditInfoUploadResultVO result = service.upload(List.of(file));

        assertEquals(0, result.getSuccessCount());
        assertEquals("征信解析平台状态码异常: 99999", result.getFailures().get(0).getReason());
    }

    @Test
    void uploadHtmlFiles_shouldRejectInvalidResultStatus() throws Exception {
        MockMultipartFile file = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));

        when(creditHtmlStorageService.save(any()))
                .thenReturn(new CreditHtmlStorageService.StoredCreditHtml(
                        "/profile/credit-html/2026/05/12/a_1.html",
                        "http://127.0.0.1:62318/profile/credit-html/2026/05/12/a_1.html"));
        CreditParseInvokeResponse response = successResponse("330101199001010011", "张三", "2026-03-03");
        response.getData().setStatus(0);
        response.getData().setReasonCode(500);
        response.getData().setReasonMessage("结果解析失败");
        when(creditParseClient.parse(anyString())).thenReturn(response);

        CreditInfoUploadResultVO result = service.upload(List.of(file));

        assertEquals(0, result.getSuccessCount());
        assertEquals("结果解析失败", result.getFailures().get(0).getReason());
    }


    @Test
    void creditHtmlStorage_shouldStoreHtmlUnderProfileAndBuildRemotePath(@TempDir Path profileDir) throws Exception {
        String oldProfile = RuoYiConfig.getProfile();
        new RuoYiConfig().setProfile(profileDir.toString());
        try {
            CreditHtmlStorageService storageService = new CreditHtmlStorageService();
            ReflectionTestUtils.setField(storageService, "filePublicBaseUrl", "http://127.0.0.1:62318/");
            MockMultipartFile file = new MockMultipartFile(
                    "files", "credit.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));

            CreditHtmlStorageService.StoredCreditHtml storedHtml = storageService.save(file);

            assertTrue(storedHtml.profilePath().startsWith("/profile/credit-html/"));
            assertTrue(storedHtml.profilePath().endsWith(".html"));
            assertEquals("http://127.0.0.1:62318" + storedHtml.profilePath(), storedHtml.remotePath());
            Path savedFile = profileDir.resolve(storedHtml.profilePath().substring("/profile/".length()));
            assertTrue(Files.exists(savedFile));
        } finally {
            new RuoYiConfig().setProfile(oldProfile);
        }
    }

    private CreditParseInvokeResponse successResponse(String personId, String personName, String reportTime) {
        CreditParsePayload payload = new CreditParsePayload();
        Map<String, Object> header = new HashMap<>();
        header.put("query_cert_no", personId);
        header.put("query_cust_name", personName);
        header.put("report_time", reportTime);
        payload.setLxHeader(header);
        payload.setLxDebt(Map.of("uncle_bank_house_bal", "1"));
        payload.setLxPublictype(Map.of("civil_cnt", 1));

        CreditParseResponse response = new CreditParseResponse();
        response.setMessage("成功");
        response.setStatusCode("ERR_SHOULD_IGNORE");
        response.setPayload(payload);

        CreditParseInvokeData data = new CreditParseInvokeData();
        data.setMappingOutputFields(response);
        data.setStatus(1);
        data.setReasonCode(200);

        CreditParseInvokeResponse invokeResponse = new CreditParseInvokeResponse();
        invokeResponse.setSuccess(true);
        invokeResponse.setCode(10000);
        invokeResponse.setData(data);
        return invokeResponse;
    }

    private CcdiDebtsInfo buildDebt(String personId) {
        CcdiDebtsInfo debt = new CcdiDebtsInfo();
        debt.setPersonId(personId);
        debt.setDebtTotalAmount(new BigDecimal("100"));
        return debt;
    }

    private CcdiCreditNegativeInfo buildNegative(String personId) {
        CcdiCreditNegativeInfo info = new CcdiCreditNegativeInfo();
        info.setPersonId(personId);
        return info;
    }
}
