package com.ruoyi.info.collection.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO;
import com.ruoyi.info.collection.domain.vo.CreditInfoDetailVO;
import com.ruoyi.info.collection.domain.vo.CreditInfoListVO;
import com.ruoyi.info.collection.domain.vo.CreditInfoNegativeVO;
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadFailureVO;
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.ICcdiCreditInfoService;
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.CreditParseInvokeResponse;
import com.ruoyi.lsfx.domain.response.CreditParsePayload;
import com.ruoyi.lsfx.domain.response.CreditParseResponse;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 征信维护服务实现
 */
@Service
public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {

    private static final int CREDIT_PARSE_SUCCESS_CODE = 10000;
    private static final int CREDIT_PARSE_SUCCESS_STATUS = 1;
    private static final int CREDIT_PARSE_SUCCESS_REASON_CODE = 200;

    @Resource
    private CreditParseClient creditParseClient;

    @Resource
    private CreditHtmlStorageService creditHtmlStorageService;

    @Resource
    private CreditInfoPayloadAssembler assembler;

    @Resource
    private CcdiDebtsInfoMapper debtsInfoMapper;

    @Resource
    private CcdiCreditNegativeInfoMapper negativeInfoMapper;

    @Resource
    private CcdiCreditInfoQueryMapper queryMapper;

    @Override
    public CreditInfoUploadResultVO upload(List<MultipartFile> files) {
        CreditInfoUploadResultVO result = new CreditInfoUploadResultVO();
        List<CreditInfoUploadFailureVO> failures = new ArrayList<>();
        int totalCount = files == null ? 0 : files.size();
        int successCount = 0;
        String userName = currentUserName();

        if (files == null || files.isEmpty()) {
            result.setTotalCount(0);
            result.setSuccessCount(0);
            result.setFailureCount(0);
            result.setFailures(failures);
            return result;
        }

        for (MultipartFile file : files) {
            try {
                validateHtmlFile(file);
                handleSingleFile(file, userName);
                successCount++;
            } catch (Exception e) {
                failures.add(buildFailure(file, null, null, e.getMessage()));
            }
        }

        result.setTotalCount(totalCount);
        result.setSuccessCount(successCount);
        result.setFailureCount(failures.size());
        result.setFailures(failures);
        return result;
    }

    @Override
    public Page<CreditInfoListVO> selectCreditInfoPage(Page<CreditInfoListVO> page, CcdiCreditInfoQueryDTO queryDTO) {
        return queryMapper.selectCreditInfoPage(page, queryDTO);
    }

    @Override
    public CreditInfoDetailVO selectDetailByPersonId(String personId) {
        CreditInfoListVO summary = queryMapper.selectCreditInfoSummaryByPersonId(personId);
        CcdiCreditNegativeInfo negative = negativeInfoMapper.selectByPersonId(personId);
        List<CcdiDebtsInfo> debts = debtsInfoMapper.selectByPersonId(personId);
        if (summary == null && negative == null && (debts == null || debts.isEmpty())) {
            return null;
        }

        CreditInfoDetailVO detail = new CreditInfoDetailVO();
        detail.setPersonId(personId);
        detail.setIdCard(summary != null && !isBlank(summary.getIdCard()) ? summary.getIdCard() : personId);
        if (summary != null) {
            detail.setPersonName(summary.getName());
        }
        if (isBlank(detail.getPersonName()) && negative != null) {
            detail.setPersonName(negative.getPersonName());
        }
        if (isBlank(detail.getPersonName()) && debts != null && !debts.isEmpty()) {
            detail.setPersonName(debts.get(0).getPersonName());
        }
        detail.setNegativeInfo(toNegativeVO(negative));
        detail.setDebtList(debts == null ? List.of() : debts);
        return detail;
    }

    @Override
    public int deleteByPersonId(String personId) {
        int debtCount = debtsInfoMapper.deleteByPersonId(personId);
        int negativeCount = negativeInfoMapper.deleteByPersonId(personId);
        return debtCount + negativeCount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void replaceEmployeeCredit(String personId, List<CcdiDebtsInfo> debts, CcdiCreditNegativeInfo negative, String userName) {
        debtsInfoMapper.deleteByPersonId(personId);
        negativeInfoMapper.deleteByPersonId(personId);
        if (debts != null && !debts.isEmpty()) {
            debts.forEach(item -> {
                item.setCreateBy(userName);
                item.setUpdateBy(userName);
            });
            debtsInfoMapper.insertBatch(debts);
        }
        if (negative != null) {
            negative.setCreateBy(userName);
            negative.setUpdateBy(userName);
            negativeInfoMapper.insert(negative);
        }
    }

    private void handleSingleFile(MultipartFile multipartFile, String userName) throws Exception {
        CreditHtmlStorageService.StoredCreditHtml storedHtml = creditHtmlStorageService.save(multipartFile);
        CreditParseInvokeResponse response = creditParseClient.parse(storedHtml.remotePath());
        CreditParsePayload payload = requireResponse(response).getPayload();
        Map<String, Object> header = requireHeader(payload);
        String personId = stringValue(header.get("query_cert_no"));
        String personName = stringValue(header.get("query_cust_name"));
        LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time")));
        ensurePersonIdPresent(personId);
        ensureLatestQueryDate(personId, queryDate);

        List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload);
        CcdiCreditNegativeInfo negative = assembler.buildNegative(personId, personName, queryDate, payload);
        replaceEmployeeCredit(personId, debts, negative, userName);
    }

    private void validateHtmlFile(MultipartFile file) {
        String originalFilename = file == null ? null : file.getOriginalFilename();
        if (originalFilename == null) {
            throw new RuntimeException("上传文件不能为空");
        }
        String lowerName = originalFilename.toLowerCase();
        if (!lowerName.endsWith(".html") && !lowerName.endsWith(".htm")) {
            throw new RuntimeException("仅支持上传.html或.htm征信文件");
        }
    }

    private CreditParseResponse requireResponse(CreditParseInvokeResponse response) {
        if (response == null || response.getData() == null || response.getData().getMappingOutputFields() == null) {
            throw new RuntimeException("征信解析结果为空");
        }
        CreditParseResponse mappingOutputFields = response.getData().getMappingOutputFields();
        if (!Boolean.TRUE.equals(response.getSuccess())) {
            throw new RuntimeException(stringValue(mappingOutputFields.getMessage(), "征信解析平台调用失败"));
        }
        if (!Integer.valueOf(CREDIT_PARSE_SUCCESS_CODE).equals(response.getCode())) {
            throw new RuntimeException("征信解析平台状态码异常: " + response.getCode());
        }
        if (!Integer.valueOf(CREDIT_PARSE_SUCCESS_STATUS).equals(response.getData().getStatus())) {
            throw new RuntimeException(parseErrorMessage(response, "征信解析状态异常: " + response.getData().getStatus()));
        }
        if (!Integer.valueOf(CREDIT_PARSE_SUCCESS_REASON_CODE).equals(response.getData().getReasonCode())) {
            throw new RuntimeException(parseErrorMessage(response, "征信解析原因码异常: " + response.getData().getReasonCode()));
        }
        if (mappingOutputFields.getPayload() == null) {
            throw new RuntimeException("征信解析结果为空");
        }
        return mappingOutputFields;
    }

    private String parseErrorMessage(CreditParseInvokeResponse response, String defaultValue) {
        if (response == null || response.getData() == null) {
            return defaultValue;
        }
        String reasonMessage = stringValue(response.getData().getReasonMessage());
        if (!isBlank(reasonMessage)) {
            return reasonMessage;
        }
        if (response.getData().getMappingOutputFields() != null) {
            return stringValue(response.getData().getMappingOutputFields().getMessage(), defaultValue);
        }
        return defaultValue;
    }

    private Map<String, Object> requireHeader(CreditParsePayload payload) {
        Map<String, Object> header = payload.getLxHeader();
        if (header == null || header.isEmpty()) {
            throw new RuntimeException("征信解析结果缺少头信息");
        }
        return header;
    }

    private void ensurePersonIdPresent(String personId) {
        if (isBlank(personId)) {
            throw new RuntimeException("征信解析结果缺少身份证号");
        }
    }

    private void ensureLatestQueryDate(String personId, LocalDate queryDate) {
        LocalDate latestQueryDate = queryMapper.selectLatestQueryDate(personId);
        if (latestQueryDate != null && queryDate.isBefore(latestQueryDate)) {
            throw new RuntimeException("上传征信日期早于当前已维护最新记录");
        }
    }

    private LocalDate parseQueryDate(String reportTime) {
        if (isBlank(reportTime)) {
            throw new RuntimeException("征信解析结果缺少征信查询日期");
        }
        String normalized = reportTime.trim();
        if (normalized.length() > 10) {
            normalized = normalized.substring(0, 10);
        }
        return LocalDate.parse(normalized);
    }

    private CreditInfoUploadFailureVO buildFailure(MultipartFile file, String personId, String personName, String reason) {
        CreditInfoUploadFailureVO failure = new CreditInfoUploadFailureVO();
        failure.setFileName(file == null ? null : file.getOriginalFilename());
        failure.setPersonId(personId);
        failure.setPersonName(personName);
        failure.setReason(reason);
        return failure;
    }

    private String stringValue(Object value) {
        return stringValue(value, null);
    }

    private String stringValue(Object value, String defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        String text = value.toString().trim();
        return text.isEmpty() ? defaultValue : text;
    }

    private boolean isBlank(String text) {
        return text == null || text.trim().isEmpty();
    }

    private CreditInfoNegativeVO toNegativeVO(CcdiCreditNegativeInfo negative) {
        if (negative == null) {
            return null;
        }
        CreditInfoNegativeVO negativeVO = new CreditInfoNegativeVO();
        negativeVO.setPersonId(negative.getPersonId());
        negativeVO.setPersonName(negative.getPersonName());
        negativeVO.setQueryDate(negative.getQueryDate());
        negativeVO.setCivilCnt(negative.getCivilCnt());
        negativeVO.setEnforceCnt(negative.getEnforceCnt());
        negativeVO.setAdmCnt(negative.getAdmCnt());
        negativeVO.setCivilLmt(negative.getCivilLmt());
        negativeVO.setEnforceLmt(negative.getEnforceLmt());
        negativeVO.setAdmLmt(negative.getAdmLmt());
        return negativeVO;
    }

    private String currentUserName() {
        try {
            return SecurityUtils.getUsername();
        } catch (Exception e) {
            return "system";
        }
    }
}
