package cn.hztech.sdk.crypto.sm2;

import cn.hztech.sdk.exception.CryptoException;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Base64;

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * SM2非对称加密工具类
 *
 * 功能：
 * - encrypt: 使用公钥加密数据
 * - decrypt: 使用私钥解密数据
 * - sign: 使用私钥对数据签名
 * - verify: 使用公钥验证签名
 */
@Slf4j
public class SM2Util {

    static {
        // 注册BouncyCastle提供者
        Security.addProvider(new BouncyCastleProvider());
    }

    private static final String ALGORITHM = "EC";
    private static final String PROVIDER = "BC";

    /**
     * SM2加密
     *
     * @param data 明文数据
     * @param publicKeyBase64 SM2公钥（Base64编码）
     * @return Base64编码的密文
     */
    public String encrypt(String data, String publicKeyBase64) {
        try {
            // 使用Cipher进行SM2加密（更简单的方式）
            byte[] keyBytes = Base64.decode(publicKeyBase64);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER);
            PublicKey publicKey = keyFactory.generatePublic(keySpec);

            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("SM2", PROVIDER);
            cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, publicKey);
            byte[] encrypted = cipher.doFinal(data.getBytes());

            return Base64.toBase64String(encrypted);

        } catch (Exception e) {
            log.error("SM2加密失败", e);
            throw new CryptoException("SM2加密失败: " + e.getMessage(), e);
        }
    }

    /**
     * SM2解密
     *
     * @param encryptedDataBase64 Base64编码的密文
     * @param privateKeyBase64 SM2私钥（Base64编码）
     * @return 明文数据
     */
    public String decrypt(String encryptedDataBase64, String privateKeyBase64) {
        try {
            // 使用Cipher进行SM2解密（更简单的方式）
            byte[] keyBytes = Base64.decode(privateKeyBase64);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER);
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

            javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("SM2", PROVIDER);
            cipher.init(javax.crypto.Cipher.DECRYPT_MODE, privateKey);
            byte[] encryptedData = Base64.decode(encryptedDataBase64);
            byte[] decrypted = cipher.doFinal(encryptedData);

            return new String(decrypted);

        } catch (Exception e) {
            log.error("SM2解密失败", e);
            throw new CryptoException("SM2解密失败: " + e.getMessage(), e);
        }
    }

    /**
     * SM2签名
     *
     * @param data 待签名数据
     * @param privateKeyBase64 SM2私钥（Base64编码）
     * @return Base64编码的签名
     */
    public String sign(String data, String privateKeyBase64) {
        try {
            // 解码私钥
            byte[] keyBytes = Base64.decode(privateKeyBase64);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER);
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

            // 使用Java标准Signature API进行SM2签名
            Signature signature = Signature.getInstance("SM3withSM2", PROVIDER);
            signature.initSign(privateKey);
            signature.update(data.getBytes());
            byte[] signBytes = signature.sign();

            return Base64.toBase64String(signBytes);

        } catch (Exception e) {
            log.error("SM2签名失败", e);
            throw new CryptoException("SM2签名失败: " + e.getMessage(), e);
        }
    }

    /**
     * SM2验签
     *
     * @param data 原始数据
     * @param signBase64 Base64编码的签名
     * @param publicKeyBase64 SM2公钥（Base64编码）
     * @return 是否验签通过
     */
    public boolean verify(String data, String signBase64, String publicKeyBase64) {
        try {
            // 解码公钥
            byte[] keyBytes = Base64.decode(publicKeyBase64);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, PROVIDER);
            PublicKey publicKey = keyFactory.generatePublic(keySpec);

            // 使用Java标准Signature API进行SM2验签
            Signature signature = Signature.getInstance("SM3withSM2", PROVIDER);
            signature.initVerify(publicKey);
            signature.update(data.getBytes());
            byte[] signBytes = Base64.decode(signBase64);

            return signature.verify(signBytes);

        } catch (Exception e) {
            log.error("SM2验签失败", e);
            throw new CryptoException("SM2验签失败: " + e.getMessage(), e);
        }
    }
}
