package shop.alien.dining.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

/**
 * 微信支付工具类
 *
 * @author ssk
 * @version 1.0
 * @date 2024/12/4
 */
@Slf4j
public class WeChatPayUtil {
    // TODO: 实现微信支付相关工具方法

    // AES-GCM模式的TAG长度（微信固定为128位/16字节）
    private static final int GCM_TAG_LENGTH = 128;
    // AES-256算法要求密钥长度为32字节（APIv3密钥必须是32位）
    private static final int API_V3_KEY_LENGTH = 32;

    /**
     * 解密微信支付resource字段密文
     * @param apiV3Key 商户平台设置的APIv3密钥（32位字符串）
     * @param nonce resource.nonce（加密随机串）
     * @param associatedData resource.associated_data（关联数据）
     * @param ciphertext resource.ciphertext（Base64编码的密文）
     * @return 解密后的明文（JSON格式业务信息）
     * @throws Exception 解密异常（密钥错误、参数非法等）
     */
    public static String decrypt(String apiV3Key, String nonce, String associatedData, String ciphertext) throws Exception {
        // 1. 校验APIv3密钥长度（必须32字节）
        if (apiV3Key == null || apiV3Key.length() != API_V3_KEY_LENGTH) {
            throw new IllegalArgumentException("APIv3密钥必须是32位字符串");
        }

        // 2. 转换密钥为SecretKeySpec（AES-256）
        SecretKeySpec secretKeySpec = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");

        // 3. Base64解密密文
        byte[] ciphertextBytes = Base64.decodeBase64(ciphertext);

        // 4. 初始化GCM参数（nonce为16字节？微信实际是12字节，符合rfc5116）
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, nonce.getBytes(StandardCharsets.UTF_8));

        // 5. 初始化AES-GCM解密器
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);

        // 6. 设置关联数据（AEAD附加数据）
        cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));

        // 7. 执行解密
        byte[] plaintextBytes = cipher.doFinal(ciphertextBytes);

        // 8. 转换为UTF-8明文（JSON格式）
        return new String(plaintextBytes, StandardCharsets.UTF_8);
    }
}

