package com.dk.mdm.infrastructure.util;
import org.springframework.stereotype.Component;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
 * 签名工具类
 */
@Component
public class SignatureUtils {

    /**
     * 签名
     */
    public static final String HEADER_PARAM_API_VERIFY_SIGN = "apiVerifySign";

    /**
     * 随机字符串
     */
    public static final String HEADER_PARAM_API_VERIFY_NONCE = "apiVerifyNonce";

    /**
     * 时间戳
     */
    public static final String HEADER_PARAM_API_VERIFY_TS = "apiVerifyTS";

    /**
     * 应用标识
     */
    public static final String HEADER_PARAM_ACCESS_KEY = "accessKey";

    /**
     * 签名算法
     */
    private static final String RSA = "RSA";

    /**
     * 加密算法
     */
    private static final String SHA512_WITH_RSA = "SHA512withRSA";

    private SignatureUtils() {

    }

    /**
     * 执行签名
     *
     * @param rsaPrivateKey 私钥
     * @param toSignStr     参数内容
     * @return 签名后的内容，base64后的字符串
     */
    public static String executeSignature(String rsaPrivateKey, String toSignStr) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
        // base64解码私钥
        byte[] decodePrivateKey = Base64.getDecoder().decode(rsaPrivateKey.replace("\r\n", ""));

        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decodePrivateKey);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

        Signature signature = Signature.getInstance(SHA512_WITH_RSA);
        signature.initSign(privateKey);
        signature.update(toSignStr.getBytes());

        // 生成签名
        byte[] result = signature.sign();

        // base64编码签名为字符串
        return Base64.getEncoder().encodeToString(result);
    }

    /**
     * 验证签名
     *
     * @param rsaPublicKey 公钥
     * @param sign         签名
     * @param src          参数内容
     * @return 验证结果
     */
    public static boolean verifySignature(String rsaPublicKey, String sign, String src) throws IllegalArgumentException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
        // base64解码公钥
        byte[] decodePublicKey = Base64.getDecoder().decode(rsaPublicKey.replace("\r\n", ""));

        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(decodePublicKey);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Signature signature = Signature.getInstance(SHA512_WITH_RSA);
        signature.initVerify(publicKey);
        signature.update(src.getBytes());
        // base64解码签名为字节数组
        byte[] decodeSign = Base64.getDecoder().decode(sign);

        // 验证签名
        return signature.verify(decodeSign);
    }

    /**
     * 构建加签参数字符串
     *
     * @param urlParamsStr   url参数（格式如：”a=1&b=2“）
     * @param bodyStr        body参数
     * @param accessKey          应用标识
     * @param apiVerifyTS    时间戳
     * @param apiVerifyNonce 随机字符串
     * @return 签名字符串
     */
    public static String buildToSignatureStr(String urlParamsStr, String bodyStr, String accessKey, long apiVerifyTS, String apiVerifyNonce) {
        Map<String, Object> urlParams = null;

        if (null != urlParamsStr && !urlParamsStr.isEmpty()) {
            // 将url参数解析为Map
            urlParams = Stream.of(urlParamsStr.split("&"))
                    .filter(Objects::nonNull)
                    .map(String::trim)
                    .filter(param -> !param.isEmpty())
                    .map(param -> param.split("="))
                    .filter(paramSplitArr -> paramSplitArr.length >= 2)
                    .collect(Collectors.toMap(paramSplitArr -> paramSplitArr[0].trim(), paramSplitArr -> paramSplitArr[1].trim()));
        }
        return buildToSignatureStr(urlParams, bodyStr, accessKey, apiVerifyTS, apiVerifyNonce);
    }

    /**
     * 构建加签参数字符串
     *
     * @param urlParams      url参数
     * @param bodyStr        body参数
     * @param accessKey          应用标识
     * @param apiVerifyTS    时间戳
     * @param apiVerifyNonce 随机字符串
     * @return 签名字符串
     */
    public static String buildToSignatureStr(Map<String, Object> urlParams, String bodyStr, String accessKey, long apiVerifyTS, String apiVerifyNonce) {

        Map<String, Object> parameterMap = new HashMap<>();

        // 拼接时间戳与随机字符串
        parameterMap.put(HEADER_PARAM_ACCESS_KEY, accessKey);
        parameterMap.put(HEADER_PARAM_API_VERIFY_TS, apiVerifyTS);
        parameterMap.put(HEADER_PARAM_API_VERIFY_NONCE, apiVerifyNonce);

        // 拼接请求参数
        if (urlParams != null && !urlParams.isEmpty()) {
            parameterMap.putAll(urlParams);
        }

        StringBuilder sb = new StringBuilder();

        // 字典序排序拼接
        parameterMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry ->
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&")
        );
        String substring = sb.substring(0, sb.length() - 1);
        return null == bodyStr || bodyStr.isEmpty() ? substring : substring + ";" + bodyStr;
    }

}
