package com.jeesharp.business.modules.pay.config.weixin;

import com.jeesharp.business.modules.pay.paywechatconfig.entity.PayWechatConfig;
import org.springframework.util.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Locale;

/**
 * 微信支付商户凭证解析（私钥 PEM、证书序列号、微信支付公钥 PEM）。
 *
 * @author jeesharp
 * @since 2026-05-25
 */
public final class PayWechatCredentialSupport {

    /**
     * 管理端详情/编辑表单中敏感字段的脱敏占位，与保存入参「留空则保留原值」逻辑配合使用。
     * <p>若该字符串被误写入 {@code pay_wechat_config.api_v3_key}，微信 V3 回调解密会得到
     * {@code Invalid AES key length: 6 bytes}。</p>
     */
    public static final String SECRET_FIELD_MASK = "******";

    /** 微信支付商户平台设置的 APIv3 密钥固定为 32 个字符（按字节计为 32，用于 AES-256-GCM）。 */
    public static final int API_V3_KEY_REQUIRED_LENGTH = 32;

    private PayWechatCredentialSupport() {
    }

    /**
     * 校验 APIv3 密钥可用于真实微信 V3 加解密（回调解密、证书等）。
     *
     * @param apiV3Key 已 trim 或原始值均可；为 null/空白/掩码占位/长度非 32 时抛出 {@link IllegalStateException}
     */
    public static void requireValidApiV3KeyForRealWx(String apiV3Key) {
        if (apiV3Key == null) {
            throw new IllegalStateException("未配置 APIv3 密钥：请在微信支付商户平台「账户中心 - API安全」获取 32 位密钥并填入【支付 · 微信配置】");
        }
        String k = apiV3Key.trim();
        if (k.isEmpty()) {
            throw new IllegalStateException("未配置 APIv3 密钥：请在微信支付商户平台「账户中心 - API安全」获取 32 位密钥并填入【支付 · 微信配置】");
        }
        if (SECRET_FIELD_MASK.equals(k)) {
            throw new IllegalStateException(
                "APIv3 密钥不能为界面脱敏占位符「" + SECRET_FIELD_MASK + "」。请填写商户平台完整的 32 位密钥；若仅修改其它配置项，请将密钥字段留空以保留库中原值");
        }
        if (k.length() != API_V3_KEY_REQUIRED_LENGTH) {
            throw new IllegalStateException(
                "APIv3 密钥长度须为 32（与微信支付商户平台一致），当前长度：" + k.length()
                    + "。若库中误存了掩码占位符，请重新保存正确密钥或修正数据库字段 api_v3_key");
        }
    }

    public static String resolveMerchantPrivateKeyPem(PayWechatConfig c) {
        if (c == null) {
            return "";
        }
        if (StringUtils.hasText(c.getMerchantPrivateKeyPem())) {
            return c.getMerchantPrivateKeyPem().trim();
        }
        String path = c.getCertPrivateKeyPath();
        if (!StringUtils.hasText(path)) {
            return "";
        }
        try {
            return Files.readString(Path.of(path.trim()), StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new IllegalStateException("无法读取商户私钥路径: " + path, e);
        }
    }

    public static String resolveMerchantSerialNo(PayWechatConfig c) {
        if (c == null) {
            return "";
        }
        if (StringUtils.hasText(c.getMerchantSerialNo())) {
            return c.getMerchantSerialNo().trim();
        }
        String certPath = c.getCertPrivateCertPath();
        if (!StringUtils.hasText(certPath)) {
            return "";
        }
        try {
            return parseCertSerialHex(Path.of(certPath.trim()));
        } catch (IOException | CertificateException e) {
            throw new IllegalStateException("无法从商户证书路径解析序列号: " + certPath, e);
        }
    }

    private static String parseCertSerialHex(Path certPemFile) throws IOException, CertificateException {
        String pem = Files.readString(certPemFile, StandardCharsets.UTF_8);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        try (ByteArrayInputStream in = new ByteArrayInputStream(pem.getBytes(StandardCharsets.UTF_8))) {
            X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
            return cert.getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
        }
    }

    public static String resolveWechatPayPublicKeyPem(PayWechatConfig c) {
        if (c == null) {
            return "";
        }
        if (StringUtils.hasText(c.getPublicKeyPem())) {
            String pem = normalizePemText(c.getPublicKeyPem());
            if (pem.startsWith("/") || pem.startsWith("file:")) {
                try {
                    return normalizePemText(Files.readString(Path.of(pem.replaceFirst("^file:", "")), StandardCharsets.UTF_8));
                } catch (IOException e) {
                    throw new IllegalStateException("无法读取微信支付公钥路径: " + pem, e);
                }
            }
            return pem;
        }
        String path = c.getPublicKeyPath();
        if (!StringUtils.hasText(path)) {
            return "";
        }
        try {
            return normalizePemText(Files.readString(Path.of(path.trim()), StandardCharsets.UTF_8));
        } catch (IOException e) {
            throw new IllegalStateException("无法读取微信支付公钥路径: " + path, e);
        }
    }

    /**
     * 解析微信支付公钥 PEM（支持 {@code BEGIN PUBLIC KEY} 与 {@code BEGIN CERTIFICATE}）。
     */
    public static PublicKey parseWechatPayPublicKey(String pem) {
        String normalized = normalizePemText(pem);
        if (!StringUtils.hasText(normalized)) {
            throw new IllegalStateException("未配置微信支付公钥 PEM 或路径");
        }
        if (SECRET_FIELD_MASK.equals(normalized)) {
            throw new IllegalStateException(
                "微信支付公钥不能为界面脱敏占位符「" + SECRET_FIELD_MASK + "」。请填写完整公钥或配置 public_key_path");
        }
        if (normalized.contains("BEGIN CERTIFICATE")) {
            return parseX509Certificate(normalized).getPublicKey();
        }
        if (normalized.contains("BEGIN PUBLIC KEY")) {
            return parseRsaPublicKeyPem(normalized);
        }
        throw new IllegalStateException(
            "微信支付公钥格式不正确，须为 PEM（-----BEGIN PUBLIC KEY----- 或 -----BEGIN CERTIFICATE-----）");
    }

    /** 界面/JSON 中可能以字面量 \\n 存储 PEM，统一还原换行。 */
    public static String normalizePemText(String pem) {
        if (pem == null) {
            return "";
        }
        String s = pem.trim();
        if (s.contains("\\n")) {
            s = s.replace("\\n", "\n");
        }
        return s;
    }

    private static X509Certificate parseX509Certificate(String pem) {
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            try (ByteArrayInputStream in = new ByteArrayInputStream(pem.getBytes(StandardCharsets.UTF_8))) {
                return (X509Certificate) cf.generateCertificate(in);
            }
        } catch (CertificateException | IOException e) {
            throw new IllegalStateException("解析微信支付公钥失败（CERTIFICATE 格式）", e);
        }
    }

    private static PublicKey parseRsaPublicKeyPem(String pem) {
        try {
            String base64 = pem
                .replace("-----BEGIN PUBLIC KEY-----", "")
                .replace("-----END PUBLIC KEY-----", "")
                .replaceAll("\\s", "");
            byte[] decoded = Base64.getDecoder().decode(base64);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePublic(new X509EncodedKeySpec(decoded));
        } catch (IllegalArgumentException | InvalidKeySpecException | java.security.NoSuchAlgorithmException e) {
            throw new IllegalStateException("解析微信支付公钥失败（PUBLIC KEY 格式）", e);
        }
    }

    public static boolean isWxEnabled(PayWechatConfig c) {
        return c != null && c.getWxEnabled() != null && c.getWxEnabled() == 1;
    }

    public static boolean isSkipSignatureVerify(PayWechatConfig c) {
        return c != null && c.getSkipSignatureVerify() != null && c.getSkipSignatureVerify() == 1;
    }
}
