package com.ai.da.common.utils;

import cn.hutool.core.io.resource.ClassPathResource;
import com.ai.da.model.dto.AlipayHKParametersDTO;
import com.ai.da.model.dto.AlipayHKRequestDTO;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.signers.RSADigestSigner;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.UUID;

@Slf4j
@Component
public class AlipayHKEncryptionUtil {

    @Value("${alipayHK.merchantId}")
    private String merchantId;

    @Value("${alipayHK.segmentId}")
    private String segmentId;

    @Value("${alipayHK.AESKey}")
    private String aesKey;

    @Value("${alipayHK.rsaPrivateKey}")
    private String privateKeyPath;

    @Value("${alipayHK.rsaPublicKey}")
    private String publicKeyPath;

    @Value("${alipayHK.CODPublicKey}")
    private String CODPublicKeyPath;


    /**
     * 加密
     * @param param
     * @param serviceName
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws IllegalBlockSizeException
     * @throws BadPaddingException
     * @throws IOException
     */
    public AlipayHKRequestDTO AESCBCWithRSA(HashMap<String, Object> param, String serviceName) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, IOException {
        // Pre-shared secret key, DO NOT hardcode this key
        String key = aesKey;
        // The path to the rsa private key file, DO NOT save this key to a publicly accessible location
        // 使用私钥创建数字签名
        // Mode, aes-128-cbc for 128bit key
//        String mode = "aes-128-cbc";
        String mode = "aes-256-cbc";
        // Padding mode
        String padding = "pkcs7";
        // signature alogrithm
        String signatureAlgorithm = "rsa-sha256";
//        String signatureAlgorithm = "SHA256withRSA";
        // Message body
        AlipayHKParametersDTO requestMessage = new AlipayHKParametersDTO();
        requestMessage.setService(serviceName);
        requestMessage.setMerchant_id(merchantId);
        requestMessage.setRequest_time(Long.toString(System.currentTimeMillis() / 1000L));
        requestMessage.setRequest_uuid(UUID.randomUUID().toString());
//        HashMap<String, Object> param = new HashMap<>();
        requestMessage.setParameters(param);

        // Serialize message body
        Gson gson = new GsonBuilder().disableHtmlEscaping().create();
        String content = gson.toJson(requestMessage);
        log.info("alipay-hk request 加密前：{}", content);

        // Secure random iv 获取随机种子
        SecureRandom secureRandom = new SecureRandom();
        byte[] iv = new byte[16]; //NEVER REUSE THIS IV WITH THE SAME KEY
        secureRandom.nextBytes(iv);

        // Algorithms
        String alogrithm = "AES/CBC/PKCS5Padding";

        // 创建 AES 密钥对象
        SecretKey secretKey = new SecretKeySpec(key.getBytes(), "AES");
        // 为加密算法提供一个随机的初始化向量，以增强加密安全性
        IvParameterSpec parameterSpec = new IvParameterSpec(iv);
        // 创建 Cipher 对象
        Cipher cipher = Cipher.getInstance(alogrithm);
        // 初始化为加密模式 Cipher.ENCRYPT_MODE
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);

        // Cipher text 对请求内容加密
        byte[] cipherText = cipher.doFinal(content.getBytes());

        // Concat iv and cipher text for signing
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        outputStream.write(iv);
        outputStream.write(cipherText);
        byte signMessage[] = outputStream.toByteArray();

        // Sign key 读取rsa私钥文件，解析并得到私钥对象 privateKey
        String rsaPrivateKeyPath = privateKeyPath;
        PrivateKey privateKey = null;
        byte[] signature;
        try {
            privateKey = readPrivateKeyFromFile(rsaPrivateKeyPath);
            Signature signer = Signature.getInstance("SHA256withRSA");
            signer.initSign(privateKey);
            // 将加密内容和随机向量均加签
            signer.update(signMessage);
            signature = signer.sign();
        } catch (SignatureException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }

        Base64.Encoder encoder = Base64.getEncoder();
        // Combine result
        AlipayHKRequestDTO alipayHKRequestDTO = new AlipayHKRequestDTO();
        alipayHKRequestDTO.setMerchant_id(merchantId);
        alipayHKRequestDTO.setNonce(encoder.encodeToString(iv));
        alipayHKRequestDTO.setMessage(encoder.encodeToString(cipherText));
        alipayHKRequestDTO.setTag(encoder.encodeToString(signature));
        String cipherSuite = mode + "-" + padding + "-with-" + signatureAlgorithm;
        alipayHKRequestDTO.setCipher_suite(cipherSuite);

        // Encode to json
        String jsonEncoded = JSONObject.toJSONString(alipayHKRequestDTO);
        log.info("alipay-hk request 加密加签后：{}",jsonEncoded);

//        String info = AlipayHKRequestUtil.createOrder(alipayHKRequestDTO);
//        log.info(info);
        return alipayHKRequestDTO;

    }

    /**
     * 从文件中读取RSA私钥
     * @param filePath
     * @return
     * @throws Exception
     */
    private static PrivateKey readPrivateKeyFromFile(String filePath) throws Exception {
        ClassPathResource classPathResource = new ClassPathResource(filePath);
        InputStream inputstream = classPathResource.getStream();
        String privateKeyContent = getString(inputstream);

        // 进行 Base64 解码
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyContent);

        // 根据 PKCS8 格式的私钥字节数组构造私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        return keyFactory.generatePrivate(keySpec);
    }

    @NotNull
    private static String getString(InputStream inputstream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream));
//        BufferedReader reader = new BufferedReader(new FileReader(filePath));
        StringBuilder keyBuffer = new StringBuilder();
        String line;

        // 读取文本文件中的内容
        while ((line = reader.readLine()) != null) {
            keyBuffer.append(line);
        }
        reader.close();

        // 去除私钥内容中的头部和尾部信息
        String privateKeyContent = keyBuffer.toString()
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", ""); // 去除空格、换行等字符
        return privateKeyContent;
    }

    /**
     * 使用 AES 密钥和随机向量进行解密
     */
    public String decryptAES(String encryptedText, String iv) throws Exception {
        byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText);
        byte[] ivBytes = Base64.getDecoder().decode(iv);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(aesKey.getBytes(), "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);

        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    public void test() throws Exception {
        // 加密数据
        AlipayHKParametersDTO requestMessage = new AlipayHKParametersDTO();
        requestMessage.setService("create_order");
        requestMessage.setMerchant_id(merchantId);
        requestMessage.setRequest_time(Long.toString(System.currentTimeMillis() / 1000L));
        requestMessage.setRequest_uuid(UUID.randomUUID().toString());
        HashMap<String, Object> param = new HashMap<>();
        requestMessage.setParameters(param);
        String orderRef = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        param.put("order_ref", orderRef);
        param.put("amount", 208.12);
        param.put("subject", "四月帳單 April Bill");
        param.put("wallet", "ALIPAYHK");
        param.put("segment_id", segmentId);
        param.put("payment_solution", "WAP");
        Gson gson = new Gson();
        String plaintext = gson.toJson(requestMessage);
        System.out.println(plaintext);

        // 生成 AES 密钥
        String key = aesKey;
        SecretKey aesKey = new SecretKeySpec(key.getBytes(), "AES");

        // 使用 RSA 公钥加密 AES 密钥
        String rsaPublicKeyPath = publicKeyPath;
        PublicKey publicKey = readPublicKey(rsaPublicKeyPath);

        Cipher rsaCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
//        Cipher rsaCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        rsaCipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded());

        // 使用 AES-128-CBC 加密数据
        Cipher aesCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] ivBytes = generateRandomIV(aesCipher.getBlockSize());
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);

        aesCipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
        byte[] encryptedData = aesCipher.doFinal(plaintext.getBytes());

        // 数字签名（使用 RSA 私钥）
        String rsaPrivateKeyPath = privateKeyPath;
        PrivateKey privateKey = readPrivateKeyFromFile(rsaPrivateKeyPath);
        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initSign(privateKey);
        signer.update(encryptedData);
        byte[] signatureBytes = signer.sign();
        Base64.Encoder encoder = Base64.getEncoder();

        // 将加密后的数据和签名进行Base64编码
        String base64EncryptedData = Base64.getEncoder().encodeToString(encryptedData);
        String base64Signature = Base64.getEncoder().encodeToString(signatureBytes);

        // 输出加密后的数据和签名
        System.out.println("加密后的数据：" + base64EncryptedData);
        System.out.println("数字签名：" + base64Signature);


        HashMap<String, String> result = new HashMap<String, String>();
        result.put("merchant_id",merchantId);
        result.put("nonce", encoder.encodeToString(ivBytes));
        result.put("message", encoder.encodeToString(encryptedData));
        result.put("tag", encoder.encodeToString(signatureBytes));

        String mode = "aes-128-cbc";
        // Padding mode
        String padding = "pkcs7";
        // signature alogrithm
        String signatureAlgorithm = "rsa-sha256";
        String cipherSuite = mode + "-" + padding + "-with-" + signatureAlgorithm;
        result.put("cipher_suite", cipherSuite);

        System.out.println("map 格式：" + result);
        // Encode to json
        String jsonEncoded = JSONObject.toJSONString(result);
        System.out.println("json 格式：" + jsonEncoded);

        AlipayHKRequestDTO alipayHKRequestDTO = new AlipayHKRequestDTO();
        alipayHKRequestDTO.setMerchant_id(merchantId);
        alipayHKRequestDTO.setNonce(encoder.encodeToString(ivBytes));
        alipayHKRequestDTO.setMessage(encoder.encodeToString(encryptedData));
        alipayHKRequestDTO.setTag(encoder.encodeToString(signatureBytes));
        alipayHKRequestDTO.setCipher_suite(cipherSuite);

//        String info = AlipayHKRequestUtil.createOrder(result);
//        log.info(info);

        // 解密和验证
//        byte[] decryptedAesKey = rsaCipher.doFinal(encryptedAesKey);
        byte[] decryptedAesKey = rsaCipher.doFinal(aesKey.getEncoded());
        SecretKey decryptedAesSecretKey = new SecretKeySpec(decryptedAesKey, "AES");

        aesCipher.init(Cipher.DECRYPT_MODE, decryptedAesSecretKey, ivSpec);
        byte[] decryptedData = aesCipher.doFinal(Base64.getDecoder().decode(base64EncryptedData));

        // 验证数字签名
        Signature verifier = Signature.getInstance("SHA256withRSA");
        verifier.initVerify(publicKey);
        verifier.update(decryptedData);
        boolean signatureValid = verifier.verify(Base64.getDecoder().decode(base64Signature));

        System.out.println("解密后的数据：" + new String(decryptedData));
        System.out.println("数字签名验证结果：" + signatureValid);
    }

//    public static PublicKey readPublicKey(File file) throws Exception {
    public static PublicKey readPublicKey(String fileName) throws Exception {
        KeyFactory factory = KeyFactory.getInstance("RSA");

        ClassPathResource classPathResource = new ClassPathResource(fileName);
        InputStream inputStream = classPathResource.getStream();
        BufferedReader fileReader = new BufferedReader(new InputStreamReader(inputStream));
        try (PemReader pemReader = new PemReader(fileReader)) {

            PemObject pemObject = pemReader.readPemObject();
            byte[] content = pemObject.getContent();
            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
            return factory.generatePublic(pubKeySpec);
        }
    }

    private static byte[] generateRandomIV(int blockSize) {
        SecureRandom random = new SecureRandom();
        byte[] iv = new byte[blockSize];
        random.nextBytes(iv);
        return iv;
    }

    public Boolean signatureVerification(String data, String signatureBase64){

        Base64.Decoder decoder = Base64.getDecoder();
        // Verify key
        try {
            ClassPathResource classPathResource = new ClassPathResource(CODPublicKeyPath);
            InputStream inputstream = classPathResource.getStream();
            // 从指定的路径读取公钥文件
            InputStreamReader isrPub = new InputStreamReader(inputstream);
            PEMParser pemParserPub = new PEMParser(isrPub);
            // 使用 PEMParser 解析公钥文件
            SubjectPublicKeyInfo pubInfo = (SubjectPublicKeyInfo) pemParserPub.readObject();
            // 从 SubjectPublicKeyInfo 创建 AsymmetricKeyParameter 对象
            AsymmetricKeyParameter pubKey = PublicKeyFactory.createKey(pubInfo);
            // Verifying; 创建 RSADigestSigner 对象并使用 SHA-256 作为摘要算法
            RSADigestSigner verifier = new RSADigestSigner(new SHA256Digest());
            // 初始化 RSADigestSigner，设置为验证模式并提供公钥参数
            verifier.init(false, pubKey);
            // 将要验证的数据转换为字节数组
            byte[] signMessageForVerifying = data.getBytes();
            // 将 Base64 编码的签名转换为字节数组
            byte[] signatureBase64ForVerifying = decoder.decode(signatureBase64);
            // 使用签名器更新要验证的数据
            verifier.update(signMessageForVerifying, 0, signMessageForVerifying.length);
            // 验证签名并返回结果
            Boolean verifyResult = verifier.verifySignature(signatureBase64ForVerifying);
            return verifyResult;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}
