package com.ruoyi.lsfx.client;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.lsfx.domain.response.CreditParseInvokeResponse;
import com.ruoyi.lsfx.exception.LsfxApiException;
import com.ruoyi.lsfx.util.HttpUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class CreditParseClient {

    private static final int PLATFORM_SUCCESS_CODE = 10000;
    private static final int INITIATE_SUCCESS_STATUS = 1;
    private static final int INITIATE_SUCCESS_REASON_CODE = 200;
    private static final int RESULT_QUERY_MAX_ATTEMPTS = 5;
    private static final long RESULT_QUERY_INTERVAL_MILLIS = 2000L;

    @Resource
    private HttpUtil httpUtil;

    @Resource
    private ObjectMapper objectMapper;

    @Value("${credit-parse.api.url}")
    private String creditParseUrl;

    @Value("${credit-parse.api.result-url}")
    private String creditParseResultUrl;

    @Value("${credit-parse.api.org-code:999000}")
    private String orgCode;

    @Value("${credit-parse.api.run-type:1}")
    private String runType;

    @Value("${credit-parse.api.model:LXCUSTALL}")
    private String defaultModel;

    public CreditParseInvokeResponse parse(String remotePath) {
        return parse(defaultModel, remotePath);
    }

    public CreditParseInvokeResponse parse(String model, String remotePath) {
        long startTime = System.currentTimeMillis();
        String actualModel = StringUtils.isBlank(model) ? defaultModel : model;
        String serialNum = buildSerialNum();
        try {
            Map<String, Object> initiateParams = buildInitiateParams(serialNum, actualModel, remotePath);
            CreditParseInvokeResponse initiateResponse = request(creditParseUrl, initiateParams, "发起接口");
            requireSuccessfulInitiateResponse(initiateResponse, "征信解析发起接口");

            CreditParseInvokeResponse response = queryResult(serialNum);

            long elapsed = System.currentTimeMillis() - startTime;
            log.info("【征信解析】调用完成: success={}, code={}, businessStatusCode={}, cost={}ms",
                response != null ? response.getSuccess() : null,
                response != null ? response.getCode() : null,
                response != null && response.getData() != null && response.getData().getMappingOutputFields() != null
                    ? response.getData().getMappingOutputFields().getStatusCode() : null,
                elapsed);
            return response;
        } catch (LsfxApiException e) {
            log.error("【征信解析】调用失败: serialNum={}, model={}, remotePath={}, error={}",
                serialNum, actualModel, remotePath, e.getMessage(), e);
            throw e;
        } catch (Exception e) {
            log.error("【征信解析】调用失败: serialNum={}, model={}, remotePath={}, error={}",
                serialNum, actualModel, remotePath, e.getMessage(), e);
            throw new LsfxApiException("征信解析调用失败: " + e.getMessage(), e);
        }
    }

    private Map<String, Object> buildInitiateParams(String serialNum, String model, String remotePath) {
        Map<String, Object> params = buildBaseParams(serialNum);
        params.put("remotePath", remotePath);
        params.put("model", model);
        return params;
    }

    private Map<String, Object> buildBaseParams(String serialNum) {
        Map<String, Object> params = new HashMap<>();
        params.put("serialNum", serialNum);
        params.put("orgCode", orgCode);
        params.put("runType", runType);
        return params;
    }

    private CreditParseInvokeResponse queryResult(String serialNum) {
        Map<String, Object> params = buildBaseParams(serialNum);
        for (int attempt = 1; attempt <= RESULT_QUERY_MAX_ATTEMPTS; attempt++) {
            CreditParseInvokeResponse response = request(creditParseResultUrl, params,
                "结果接口第" + attempt + "次查询");
            requireSuccessfulServiceResponse(response, "征信解析结果接口");
            if (response.getData() == null || response.getData().getMappingOutputFields() == null) {
                waitForNextResult(serialNum, attempt);
                continue;
            }
            if (response.getData().getMappingOutputFields().getPayload() != null) {
                return response;
            }
            waitForNextResult(serialNum, attempt);
        }
        throw new LsfxApiException("征信解析结果未返回");
    }

    private void waitForNextResult(String serialNum, int attempt) {
        if (attempt >= RESULT_QUERY_MAX_ATTEMPTS) {
            return;
        }
        log.info("【征信解析】结果未返回: serialNum={}, attempt={}/{}, {}ms后重试",
            serialNum, attempt, RESULT_QUERY_MAX_ATTEMPTS, RESULT_QUERY_INTERVAL_MILLIS);
        try {
            sleepBeforeNextResultQuery(RESULT_QUERY_INTERVAL_MILLIS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new LsfxApiException("征信解析结果查询被中断", e);
        }
    }

    protected void sleepBeforeNextResultQuery(long intervalMillis) throws InterruptedException {
        Thread.sleep(intervalMillis);
    }

    private CreditParseInvokeResponse request(String url, Map<String, Object> params, String stage) {
        try {
            log.info("【征信解析】{}请求: url={}, params={}", stage, url, toJson(params));
            String responseJson = httpUtil.postUrlEncodedFormForString(url, params, null);
            log.info("【征信解析】{}返回JSON: {}", stage, responseJson);
            return objectMapper.readValue(responseJson, CreditParseInvokeResponse.class);
        } catch (LsfxApiException e) {
            throw e;
        } catch (Exception e) {
            throw new LsfxApiException("征信解析" + stage + "调用失败: " + e.getMessage(), e);
        }
    }

    private void requireSuccessfulInitiateResponse(CreditParseInvokeResponse response, String stage) {
        requireSuccessfulServiceResponse(response, stage);
    }

    private void requireSuccessfulServiceResponse(CreditParseInvokeResponse response, String stage) {
        requireSuccessfulPlatformResponse(response, stage);
        if (response.getData() == null) {
            throw new LsfxApiException(stage + "返回结果为空");
        }
        if (!Integer.valueOf(INITIATE_SUCCESS_STATUS).equals(response.getData().getStatus())) {
            throw new LsfxApiException(serviceErrorMessage(response, stage + "状态异常: " + response.getData().getStatus()));
        }
        if (!Integer.valueOf(INITIATE_SUCCESS_REASON_CODE).equals(response.getData().getReasonCode())) {
            throw new LsfxApiException(serviceErrorMessage(response, stage + "原因码异常: " + response.getData().getReasonCode()));
        }
    }

    private void requireSuccessfulPlatformResponse(CreditParseInvokeResponse response, String stage) {
        if (response == null) {
            throw new LsfxApiException(stage + "返回结果为空");
        }
        if (!Boolean.TRUE.equals(response.getSuccess())) {
            throw new LsfxApiException(stage + "平台调用失败");
        }
        if (!Integer.valueOf(PLATFORM_SUCCESS_CODE).equals(response.getCode())) {
            throw new LsfxApiException(stage + "平台状态码异常: " + response.getCode());
        }
    }

    private String buildSerialNum() {
        return "CCDI_CREDIT_" + System.currentTimeMillis() + "_" + IdUtils.fastSimpleUUID();
    }

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

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

    private String toJson(Object value) {
        try {
            return objectMapper.writeValueAsString(value);
        } catch (Exception e) {
            return String.valueOf(value);
        }
    }
}
