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;

import java.util.UUID;

/**
 * 报文构建器
 *
 * 功能：
 * 构建请求报文，实现SM2+SM4数字信封加密方案
 *
 * 流程：
 * 1. 生成随机SM4密钥
 * 2. 使用SM4加密业务数据
 * 3. 使用接收方SM2公钥加密SM4密钥
 * 4. 生成签名（appId + timestamp + encryptData + encryptKey）
 * 5. 组装GmPacket报文
 */
@Slf4j
public class GmPacketBuilder {

    private final String appId;
    private final String senderPrivateKey;
    private final String receiverPublicKey;
    private final SM2Util sm2Util;
    private final SM4Util sm4Util;

    public GmPacketBuilder(String appId, String senderPrivateKey,
                          String receiverPublicKey, SM2Util sm2Util, SM4Util sm4Util) {
        this.appId = appId;
        this.senderPrivateKey = senderPrivateKey;
        this.receiverPublicKey = receiverPublicKey;
        this.sm2Util = sm2Util;
        this.sm4Util = sm4Util;
    }

    /**
     * 构建请求报文
     *
     * @param businessData 业务数据（JSON字符串）
     * @return GmPacket报文对象
     */
    public GmPacket build(String businessData) {
        try {
            // 1. 生成随机SM4密钥
            String sm4Key = sm4Util.generateKey();
            log.debug("生成SM4密钥");

            // 2. SM4加密业务数据
            String encryptData = sm4Util.encrypt(businessData, sm4Key);
            log.debug("SM4加密业务数据完成");

            // 3. SM2加密SM4密钥（使用接收方公钥）
            String encryptKey = sm2Util.encrypt(sm4Key, receiverPublicKey);
            log.debug("SM2加密SM4密钥完成");

            // 4. 生成报文元数据
            long timestamp = System.currentTimeMillis();
            String nonce = UUID.randomUUID().toString();

            // 5. 生成签名（使用发送方私钥）
            String signData = appId + timestamp + encryptData + encryptKey;
            String sign = sm2Util.sign(signData, senderPrivateKey);
            log.debug("生成签名完成");

            // 6. 组装报文
            GmPacket packet = new GmPacket();
            packet.setAppId(appId);
            packet.setVersion("1.0");
            packet.setTimestamp(timestamp);
            packet.setNonce(nonce);
            packet.setEncryptKey(encryptKey);
            packet.setEncryptData(encryptData);
            packet.setSign(sign);

            log.info("构建报文成功, appId: {}, timestamp: {}, nonce: {}",
                    appId, timestamp, nonce);

            return packet;

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