package com.liquidnet.common.third.secureaccess.biz;

import cn.anicert.secure.access.bean.SARequestPackageVO;
import cn.anicert.secure.access.bean.SARequestVO;
import cn.anicert.secure.access.core.SecureAccess;
import cn.anicert.secure.access.crypto.service.ISoftCryptoService;
import cn.anicert.secure.access.utils.ConfigUtil;
import cn.anicert.secure.access.utils.JSONUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.liquidnet.common.third.secureaccess.config.SecureAccessProperties;
import com.liquidnet.common.third.secureaccess.model.NiaAuthRequest;
import com.liquidnet.common.third.secureaccess.model.NiaAuthResult;
import com.liquidnet.common.third.secureaccess.model.NiaPidParsed;
import com.liquidnet.common.third.secureaccess.util.NiaPidUtils;
import com.liquidnet.commons.lang.util.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import static com.liquidnet.common.third.secureaccess.util.NiaPidUtils.maskPid;

import java.time.LocalDateTime;

@Slf4j
@Component
@RequiredArgsConstructor
public class NiaAuthBiz {

    private static final String SUCCESS_CODE = "C0000000";
    private static final int RAW_LOG_MAX_LEN = 2000;

    private final SecureAccessProperties properties;
    private final ISoftCryptoService cryptoService;

    public boolean isReady() {
        return StringUtils.isNoneBlank(
                properties.getCustomerNo(),
                properties.getAccessUrl(),
                properties.getSignPrivateKey(),
                properties.getSignVerifyPublicKey())
                && ConfigUtil.getConfig() != null;
    }

    /**
     * 调用安全接入平台转发网络身份认证 auth/request。
     */
    public NiaAuthResult authenticate(NiaAuthRequest request) {
        if (!isReady()) {
            return fail(request.getBizSeq(), "CONFIG", "网证认证配置未完成", null);
        }
        if (StringUtils.isAnyBlank(request.getBizSeq(), request.getIdCardAuthData())) {
            return fail(request.getBizSeq(), "PARAM", "bizSeq 或 idCardAuthData 不能为空", null);
        }
        try {
            ObjectNode bizPackage = JsonUtils.OM().createObjectNode();
            bizPackage.put("appName", properties.getAppName());
            bizPackage.put("timestamp", LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_MS_PATTERN));
            bizPackage.put("bizSeq", request.getBizSeq());
            bizPackage.put("mode", properties.getMode());
            bizPackage.put("customerNo", properties.getCustomerNo());
            bizPackage.put("idCardAuthData", request.getIdCardAuthData());

            SARequestPackageVO requestPackage = new SARequestPackageVO();
            requestPackage.setCustomerNo(properties.getCustomerNo());
            requestPackage.setForwardUrl(properties.getForwardUrl());
            requestPackage.setBizPackage(bizPackage);

            log.info("[nia-auth] 请求SAP bizSeq={}, mode={}, customerNo={}, appName={}, forwardUrl={}, idCardAuthDataLen={}",
                    request.getBizSeq(),
                    properties.getMode(),
                    properties.getCustomerNo(),
                    properties.getAppName(),
                    properties.getForwardUrl(),
                    request.getIdCardAuthData().length());

            String packageJson = JSONUtil.toJson(requestPackage);
            SARequestVO saRequest = new SARequestVO();
            saRequest.setRequestPackage(requestPackage);
            saRequest.setRequestSign(cryptoService.sm2Sign(properties.getSignPrivateKey(), packageJson));

            String responseJson = SecureAccess.unified_auth(request.getBizSeq(), JSONUtil.toJson(saRequest));
            if (StringUtils.isBlank(responseJson)) {
                log.warn("[nia-auth] SAP响应为空或验签失败 bizSeq={}, accessUrl={}",
                        request.getBizSeq(), properties.getAccessUrl());
                return fail(request.getBizSeq(), "SAP", "安全接入平台返回为空或验签失败", null);
            }
            JsonNode sapRoot = JsonUtils.OM().readTree(responseJson);
            log.info("[nia-auth] SAP响应 bizSeq={}, platformCode={}, platformMsg={}",
                    request.getBizSeq(),
                    firstText(sapRoot, "platformCode"),
                    firstText(sapRoot, "platformMsg"));
            log.debug("[nia-auth] SAP原始响应 bizSeq={}, body={}",
                    request.getBizSeq(), abbreviate(responseJson, RAW_LOG_MAX_LEN));
            return parseResponse(request.getBizSeq(), responseJson);
        } catch (Exception e) {
            log.error("[nia-auth] bizSeq={} 认证异常", request.getBizSeq(), e);
            return fail(request.getBizSeq(), "ERROR", e.getMessage(), null);
        }
    }

    private NiaAuthResult parseResponse(String bizSeq, String responseJson) {
        try {
            JsonNode root = JsonUtils.OM().readTree(responseJson);
            String platformCode = firstText(root, "platformCode");
            if (StringUtils.isNotBlank(platformCode) && !"200".equals(platformCode)) {
                String platformMsg = firstText(root, "platformMsg");
                log.warn("[nia-auth] SAP平台失败 bizSeq={}, platformCode={}, platformMsg={}",
                        bizSeq, platformCode, platformMsg);
                return fail(bizSeq, platformCode,
                        StringUtils.defaultIfBlank(platformMsg, "安全接入平台请求失败"), responseJson);
            }
            JsonNode bizPackageNode = resolveBizPackage(root);
            if (bizPackageNode == null || bizPackageNode.isMissingNode()) {
                log.warn("[nia-auth] 响应缺少bizPackage bizSeq={}, body={}", bizSeq, abbreviate(responseJson, RAW_LOG_MAX_LEN));
                return fail(bizSeq, "PARSE", "认证响应缺少 bizPackage", responseJson);
            }

            String resultCode = firstText(bizPackageNode, "resultCode");
            String resultDesc = firstText(bizPackageNode, "resultDesc");
            String bizSerialNo = firstText(bizPackageNode, "bizSerialNo");

            log.info("[nia-auth] 解析平台响应 bizSeq={}, resultCode={}, resultDesc={}, bizSerialNo={}",
                    bizSeq, resultCode, resultDesc, bizSerialNo);

            if (StringUtils.isBlank(resultCode)) {
                log.warn("[nia-auth] 响应缺少resultCode bizSeq={}, bizPackage={}",
                        bizSeq, abbreviate(bizPackageNode.toString(), RAW_LOG_MAX_LEN));
                return fail(bizSeq, "UNKNOWN", "平台响应缺少 resultCode", responseJson);
            }
            if (!SUCCESS_CODE.equals(resultCode)) {
                return NiaAuthResult.builder()
                        .success(false)
                        .resultCode(resultCode)
                        .resultDesc(resultDesc)
                        .bizSeq(bizSeq)
                        .bizSerialNo(bizSerialNo)
                        .stage("PLATFORM")
                        .rawResponse(abbreviate(responseJson, RAW_LOG_MAX_LEN))
                        .build();
            }

            JsonNode data = bizPackageNode.path("data");
            if (data.isMissingNode()) {
                data = root.path("data");
            }
            String pid = firstText(data, "PID", "pid");
            if (StringUtils.isBlank(pid)) {
                log.warn("[nia-auth] 成功响应缺少PID bizSeq={}, data={}", bizSeq, data);
                return fail(bizSeq, "PLATFORM", "认证成功但响应缺少 PID", responseJson);
            }

            NiaPidParsed pidParsed = NiaPidUtils.parse(pid);
            if (pidParsed == null) {
                log.warn("[nia-auth] PID解码失败 bizSeq={}, pid={}", bizSeq, maskPid(pid));
                return fail(bizSeq, "PLATFORM", "认证凭据 PID 解码失败", responseJson);
            }
            log.info("[nia-auth] PID结构 bizSeq={}, totalLen={}, version={}, identifierHex={}, identifierVisual=[{}], timestamp={}, signatureLen={}, signatureHex={}",
                    bizSeq,
                    pidParsed.getTotalLen(),
                    pidParsed.getVersion(),
                    pidParsed.identifierHex(),
                    pidParsed.identifierVisual(),
                    pidParsed.getTimestamp(),
                    pidParsed.getSignature() == null ? 0 : pidParsed.getSignature().length,
                    pidParsed.signatureHexPrefix(16));

            String openId = NiaPidUtils.extractOpenId(pid);
            if (!NiaPidUtils.isValidOpenId(openId)) {
                log.warn("[nia-auth] PID标识解析失败 bizSeq={}", bizSeq);
                return fail(bizSeq, "PLATFORM", "认证成功但 PID 标识解析失败", responseJson);
            }
            log.info("[nia-auth] 认证成功 bizSeq={}, bizSerialNo={}, openId={}",
                    bizSeq, bizSerialNo, openId);

            return NiaAuthResult.builder()
                    .success(true)
                    .resultCode(resultCode)
                    .resultDesc(resultDesc)
                    .bizSeq(bizSeq)
                    .bizSerialNo(bizSerialNo)
                    .openId(openId)
                    .photoCompareScore(firstText(data, "photoCompareScore"))
                    .encryptedIdInfo(firstText(data, "encryptedIdInfo"))
                    .stage("SUCCESS")
                    .rawResponse(abbreviate(responseJson, RAW_LOG_MAX_LEN))
                    .build();
        } catch (Exception e) {
            log.error("[nia-auth] bizSeq={} 解析响应失败, body={}", bizSeq, abbreviate(responseJson, RAW_LOG_MAX_LEN), e);
            return fail(bizSeq, "PARSE", "认证响应解析失败: " + e.getMessage(), responseJson);
        }
    }

    /**
     * 解析认证结果 bizPackage。
     * SAP unified_auth 典型结构：{ platformData: { bizPackage: {...}, sign } }
     */
    private static JsonNode resolveBizPackage(JsonNode root) {
        if (root == null) {
            return null;
        }
        JsonNode platformData = root.path("platformData");
        if (!platformData.isMissingNode()) {
            JsonNode platformBizPackage = platformData.path("bizPackage");
            if (!platformBizPackage.isMissingNode()) {
                return platformBizPackage;
            }
        }
        JsonNode bizPackage = root.path("bizPackage");
        if (!bizPackage.isMissingNode() && bizPackage.has("resultCode")) {
            return bizPackage;
        }
        if (!bizPackage.isMissingNode() && bizPackage.has("bizPackage")) {
            JsonNode nested = bizPackage.path("bizPackage");
            if (!nested.isMissingNode()) {
                return nested;
            }
        }
        if (root.has("resultCode")) {
            return root;
        }
        return null;
    }

    private static String firstText(JsonNode node, String... fields) {
        if (node == null || node.isMissingNode()) {
            return null;
        }
        for (String field : fields) {
            String value = text(node, field);
            if (StringUtils.isNotBlank(value)) {
                return value;
            }
        }
        return null;
    }

    private static String text(JsonNode node, String field) {
        JsonNode value = node.path(field);
        return value.isMissingNode() || value.isNull() ? null : value.asText();
    }

    private static String abbreviate(String value, int maxLen) {
        if (value == null) {
            return null;
        }
        return value.length() <= maxLen ? value : value.substring(0, maxLen) + "...(truncated)";
    }

    private static NiaAuthResult fail(String bizSeq, String code, String desc, String rawResponse) {
        return NiaAuthResult.builder()
                .success(false)
                .resultCode(code)
                .resultDesc(desc)
                .bizSeq(bizSeq)
                .stage(code)
                .rawResponse(abbreviate(rawResponse, RAW_LOG_MAX_LEN))
                .build();
    }
}
