package com.cool.store.utils;

import java.net.URLEncoder;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.*;
/**
 * RSA签名工具类
 * @Author suzhuhong
 * @Date 2025/11/12 22:57
 * @Version 1.0
 */
@Slf4j
public class RsaSignUtil {


    /**
     * 加签方法
     */
    public static String generateSign(Map<String, Object> params, String privateKeyPEM) {
        // 移除可能存在的sign字段
        params.remove("sign");
        String afterSort = objectToSortedString(params);
        log.info("排序后的参数：{}", afterSort);
        String formattedPrivateKey = KeyFormatUtil.convertToPEMFormat(privateKeyPEM, "RSA_PRIVATE");
        return rsaPrivateKeySign(afterSort, privateKeyPEM, "SHA256withRSA");
    }

    /**
     * 验签方法
     */
    public static boolean verifySign(Map<String, Object> params, String publicKeyPEM) {
        String sign = String.valueOf(params.get("sign"));
        params.remove("sign");
        String afterSort = objectToSortedString(params);
        log.info("排序后的参数：{}", afterSort);
        return rsaPublicKeyVerify(afterSort, sign, publicKeyPEM, "SHA256withRSA");
    }

    /**
     * 查询签名验证 - 主入口方法
     */
    public static boolean querySignCrm(String publicKey, Map<String, Object> params) {
        return verifySign(params, publicKey);
    }

    /**
     * 将对象转换为排序后的字符串
     */
    public static String objectToSortedString(Map<String, Object> obj) {
        return convertToSortedString(obj, "");
    }

    /**
     * 递归将参数转换为排序字符串
     */
    private static String convertToSortedString(Map<String, Object> params, String prefix) {
        // 按键排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);

        List<String> parts = new ArrayList<>();

        for (String key : keys) {
            Object value = params.get(key);
            if (value == null || "".equals(value) ||
                    (value instanceof Number && ((Number) value).doubleValue() == 0)) {
                continue;
            }

            String currentKey = key;
            if (!prefix.isEmpty()) {
                currentKey = prefix + "[" + key + "]";
            }

            if (value instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> nestedMap = (Map<String, Object>) value;
                String nestedString = convertToSortedString(nestedMap, currentKey);
                if (!nestedString.isEmpty()) {
                    parts.add(nestedString);
                }
            } else if (value instanceof List) {
                List<?> list = (List<?>) value;
                List<String> elemStrs = new ArrayList<>();

                for (Object element : list) {
                    String elementStr;
                    if (element instanceof Map) {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> elementMap = (Map<String, Object>) element;
                        String inner = convertToSortedString(elementMap, "");
                        elementStr = "{" + inner + "}";
                    } else if (element instanceof Double) {
                        elementStr = String.format("%.0f", (Double) element);
                    } else if (element instanceof Float) {
                        elementStr = String.format("%.0f", (Float) element);
                    } else {
                        elementStr = String.valueOf(element);
                    }
                    elemStrs.add(elementStr);
                }

                // 对元素字符串排序
                Collections.sort(elemStrs);

                // URL编码并添加到parts
                for (String es : elemStrs) {
                    String encodedValue = urlEncode(es);
                    parts.add(currentKey + "[]=" + encodedValue);
                }
            } else if (value instanceof Double) {
                String strValue = String.format("%.0f", (Double) value);
                parts.add(currentKey + "=" + urlEncode(strValue));
            } else if (value instanceof Float) {
                String strValue = String.format("%.0f", (Float) value);
                parts.add(currentKey + "=" + urlEncode(strValue));
            } else {
                String strValue = String.valueOf(value);
                parts.add(currentKey + "=" + urlEncode(strValue));
            }
        }

        return String.join("&", parts);
    }

    /**
     * URL编码（替换+为%20）
     */
    private static String urlEncode(String value) {
        try {
            return URLEncoder.encode(value, StandardCharsets.UTF_8.name())
                    .replace("+", "%20");
        } catch (Exception e) {
            return value;
        }
    }

    /**
     * RSA私钥加签
     */
    public static String rsaPrivateKeySign(String text, String privateKeyPEM, String algorithm) {
        try {
            // 1. 解析私钥
            log.debug("Received private key PEM: {}", privateKeyPEM);
            PrivateKey privateKey = parsePrivateKey(privateKeyPEM);
            if (privateKey == null) {
                throw new RuntimeException("解析私钥失败");
            }

            // 2. 创建签名实例
            Signature signature = Signature.getInstance(algorithm);
            signature.initSign(privateKey);
            signature.update(text.getBytes(StandardCharsets.UTF_8));

            // 3. 生成签名并Base64编码
            byte[] digitalSignature = signature.sign();
            return Base64.getEncoder().encodeToString(digitalSignature);

        } catch (Exception e) {
            throw new RuntimeException("加签失败: " + e.getMessage(), e);
        }
    }

    /**
     * RSA公钥验签
     */
    public static boolean rsaPublicKeyVerify(String text, String signBase64,
                                             String publicKeyPEM, String algorithm) {
        try {
            // 1. 解码Base64签名
            byte[] signature = Base64.getDecoder().decode(signBase64);

            // 2. 解析公钥
            PublicKey publicKey = parsePublicKey(publicKeyPEM);
            if (publicKey == null) {
                return false;
            }

            // 3. 验证签名
            Signature sig = Signature.getInstance(algorithm);
            sig.initVerify(publicKey);
            sig.update(text.getBytes(StandardCharsets.UTF_8));

            return sig.verify(signature);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 解析PEM格式私钥（兼容PKCS#1和PKCS#8格式）
     */
    private static PrivateKey parsePrivateKey(String privateKeyPEM) {
        try {
            String pemContent = privateKeyPEM
                    .replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replace("-----BEGIN RSA PRIVATE KEY-----", "")
                    .replace("-----END RSA PRIVATE KEY-----", "")
                    .replaceAll("\\s", "");

            byte[] keyBytes = Base64.getDecoder().decode(pemContent);

            // 尝试PKCS#8格式
            try {
                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                return keyFactory.generatePrivate(keySpec);
            } catch (Exception e) {
                // 使用Bouncy Castle处理PKCS#1
                RSAPrivateKey rsaPrivKey = RSAPrivateKey.getInstance(keyBytes);
                return KeyFactory.getInstance("RSA").generatePrivate(
                        new RSAPrivateKeySpec(rsaPrivKey.getModulus(), rsaPrivKey.getPrivateExponent()));
            }
        } catch (Exception e) {
            throw new RuntimeException("解析私钥失败: " + e.getMessage(), e);
        }
    }

    /**
     * 解析PKCS#1格式私钥
     */
    private static PrivateKey parsePKCS1PrivateKey(byte[] keyBytes) {
        try {
            int length = keyBytes.length;
            byte[] pkcs8Header = new byte[] {
                    0x30, (byte) 0x82,
                    (byte) ((length + 26) >> 8), (byte) (length + 26),  // 总长度
                    0x02, 0x01, 0x00,
                    0x30, 0x0D, 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00,
                    0x04, (byte) 0x82,
                    (byte) (length >> 8), (byte) length  // BIT STRING 长度
            };

            byte[] pkcs8Key = new byte[pkcs8Header.length + keyBytes.length];
            System.arraycopy(pkcs8Header, 0, pkcs8Key, 0, pkcs8Header.length);
            System.arraycopy(keyBytes, 0, pkcs8Key, pkcs8Header.length, keyBytes.length);

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Key);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(keySpec);
        } catch (Exception e) {
            throw new RuntimeException("解析PKCS#1私钥失败: " + e.getMessage(), e);
        }
    }

    /**
     * 解析PEM格式公钥（兼容PKCS#1和PKCS#8格式）
     */
    private static PublicKey parsePublicKey(String publicKeyPEM) {
        try {
            // 移除PEM头尾标记和空白字符
            String pemContent = publicKeyPEM
                    .replace("-----BEGIN PUBLIC KEY-----", "")
                    .replace("-----END PUBLIC KEY-----", "")
                    .replace("-----BEGIN RSA PUBLIC KEY-----", "")
                    .replace("-----END RSA PUBLIC KEY-----", "")
                    .replaceAll("\\s", "");

            byte[] keyBytes = Base64.getDecoder().decode(pemContent);

            // 先尝试PKCS#8格式
            try {
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
                KeyFactory keyFactory = KeyFactory.getInstance("RSA");
                return keyFactory.generatePublic(keySpec);
            } catch (Exception e) {
                // 如果PKCS#8失败，尝试PKCS#1格式
                return parsePKCS1PublicKey(keyBytes);
            }
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 解析PKCS#1格式公钥
     */
    private static PublicKey parsePKCS1PublicKey(byte[] keyBytes) {
        try {
            // PKCS#1 RSA公钥转换为PKCS#8格式
            byte[] pkcs8Prefix = new byte[] {
                    0x30, (byte) 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, (byte) 0x86,
                    0x48, (byte) 0x86, (byte) 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
                    (byte) 0x82, 0x01, 0x0f, 0x00
            };

            byte[] pkcs8Key = new byte[pkcs8Prefix.length + keyBytes.length];
            System.arraycopy(pkcs8Prefix, 0, pkcs8Key, 0, pkcs8Prefix.length);
            System.arraycopy(keyBytes, 0, pkcs8Key, pkcs8Prefix.length, keyBytes.length);

            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pkcs8Key);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePublic(keySpec);
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 针对 /zxjp/open/v1/wallet/** 接口的专用验签方法
     * @param params 包含签名参数的Map
     * @param privateKeyPEM 用于生成签名的私钥
     * @return 验签是否通过
     */
    public static boolean verifyWalletSign(Map<String, Object> params, String privateKeyPEM) {
        try {
            // 1. 获取请求中的签名
            String requestSign = String.valueOf(params.get("sign"));
            if (requestSign == null || requestSign.isEmpty()) {
                log.warn("请求中缺少签名参数");
                return false;
            }

            // 2. 移除签名参数，生成待签名字符串
            Map<String, Object> paramsToSign = new HashMap<>(params);
            paramsToSign.remove("sign");

            // 3. 使用相同的私钥生成签名
            String generatedSign = generateSign(paramsToSign, privateKeyPEM);

            // 4. 对比签名
            boolean isVerified = Objects.equals(requestSign, generatedSign);

            if (!isVerified) {
                log.warn("签名验证失败，请求签名: {}, 生成签名: {}", requestSign, generatedSign);
            }

            return isVerified;
        } catch (Exception e) {
            log.error("验签过程中发生异常", e);
            return false;
        }
    }

}