package cn.hztech.sdk.crypto.envelope;

import cn.hztech.sdk.crypto.sm2.SM2Util;
import cn.hztech.sdk.crypto.sm4.SM4Util;
import cn.hztech.sdk.exception.CryptoException;
import lombok.extern.slf4j.Slf4j;

/**
 * 报文解析器
 *
 * 功能：
 * 解析响应报文，实现SM2+SM4数字信封解密方案
 *
 * 流程：
 * 1. 验证时间戳（±60秒）
 * 2. 验证签名（使用发送方SM2公钥）
 * 3. 使用接收方SM2私钥解密SM4密钥
 * 4. 使用SM4密钥解密业务数据
 */
@Slf4j
public class GmPacketParser {

    private final String receiverPrivateKey;
    private final String senderPublicKey;
    private final SM2Util sm2Util;
    private final SM4Util sm4Util;
    private final Long timestampTolerance;

    public GmPacketParser(String receiverPrivateKey, String senderPublicKey,
                         SM2Util sm2Util, SM4Util sm4Util, Long timestampTolerance) {
        this.receiverPrivateKey = receiverPrivateKey;
        this.senderPublicKey = senderPublicKey;
        this.sm2Util = sm2Util;
        this.sm4Util = sm4Util;
        this.timestampTolerance = timestampTolerance;
    }

    /**
     * 解析报文
     *
     * @param packet GmPacket报文对象
     * @return 业务数据（JSON字符串）
     */
    public String parse(GmPacket packet) {
        try {
            // 1. 验证时间戳
            validateTimestamp(packet.getTimestamp());

            // 2. 验证签名
            String signData = packet.getAppId() + packet.getTimestamp()
                            + packet.getEncryptData() + packet.getEncryptKey();
            boolean signValid = sm2Util.verify(signData, packet.getSign(), senderPublicKey);
            if (!signValid) {
                throw new SecurityException("签名验证失败");
            }
            log.debug("签名验证通过");

            // 3. SM2解密SM4密钥（使用接收方私钥）
            String sm4Key = sm2Util.decrypt(packet.getEncryptKey(), receiverPrivateKey);
            log.debug("SM2解密SM4密钥完成");

            // 4. SM4解密业务数据
            String businessData = sm4Util.decrypt(packet.getEncryptData(), sm4Key);
            log.debug("SM4解密业务数据完成");

            log.info("解析报文成功, appId: {}, timestamp: {}",
                    packet.getAppId(), packet.getTimestamp());

            return businessData;

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

    /**
     * 验证时间戳
     *
     * @param timestamp 时间戳（毫秒）
     */
    private void validateTimestamp(Long timestamp) {
        long currentTime = System.currentTimeMillis();
        long diff = Math.abs(currentTime - timestamp);

        if (diff > timestampTolerance) {
            throw new SecurityException(
                String.format("时间戳过期, 当前时间: %d, 报文时间: %d, 差值: %dms",
                currentTime, timestamp, diff)
            );
        }

        log.debug("时间戳验证通过, 差值: {}ms", diff);
    }
}
