package cmb

import (
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"github.com/ZZMarquis/gm/util"
	tsm2 "github.com/tjfoc/gmsm/sm2"
	"github.com/tjfoc/gmsm/sm4"
	"github.com/tjfoc/gmsm/x509"
	"strings"
	"voucher/internal/pkg/cmb/encrypt"
)

type Decrypts struct {
	EncryptBody string
	PrivateKey  string
}

type Encrypts struct {
	JsonParam string
	SoaPubKey string
}

func EncryptBody(encrypts *Encrypts) (string, error) {

	paddedPlaintext := util.PKCS5Padding([]byte(encrypts.JsonParam), sm4.BlockSize)
	//随机生成对称密钥RandomKey和偏移向量RandomIV
	randomKey := make([]byte, sm4.BlockSize)
	randomIV := make([]byte, sm4.BlockSize)
	if _, err := rand.Read(randomKey); err != nil {
		return "", fmt.Errorf("生成randomKey失败:%v", err)
	}

	if _, err := rand.Read(randomIV); err != nil {
		return "", fmt.Errorf("生成randomIV失败: %v", err)
	}
	// 使用对称密钥对报文进行加密，并对结果进行Base64编码
	//对称加密算法为SM4/CBC/PKCS5Padding，密钥长度为16字节，明文长度不做要求，IV长度为16字节。
	block, err := sm4.NewCipher(randomKey)
	if err != nil {
		return "", fmt.Errorf("IV加密失败: %v", err)
	}
	ciphertext := make([]byte, len(paddedPlaintext))
	if len(paddedPlaintext)%sm4.BlockSize != 0 {
		return "", fmt.Errorf("加密格式错误:%v", err)
	}
	mode := cipher.NewCBCEncrypter(block, randomIV)
	mode.CryptBlocks(ciphertext, paddedPlaintext)
	//ciphertext, err := sm4.Sm4Cbc(randomKey, randomIV, util.PKCS5Padding([]byte(encrypts.JsonParam), sm4.BlockSize))

	ciphertextBase64 := base64.StdEncoding.EncodeToString(ciphertext)

	//使用掌上生活的SM2算法公钥对“RandomKey|RandomIV”进行base64之后加密，并对结果进行Base64编码
	//非对称加密算法为SM2，公钥长度为65字节，私钥长度为32字节。
	keyAndIV := AssemblingByteArray(randomKey, randomIV)
	keyAndIVBase64 := base64.StdEncoding.EncodeToString(keyAndIV)
	pubk, err := x509.ReadPublicKeyFromHex(encrypts.SoaPubKey)
	if err != nil {
		return "", fmt.Errorf("解析公钥失败: %v", err)
	}
	sm2EncryptKeyAndIV, err := tsm2.Encrypt(pubk, []byte(keyAndIVBase64), rand.Reader, tsm2.C1C3C2)
	if err != nil {
		return "", fmt.Errorf("RandomKey|RandomIV加密失败: %v", err)
	}
	sm2EncryptKeyAndIVBase64 := base64.StdEncoding.EncodeToString(sm2EncryptKeyAndIV)

	//将加密后的对称密钥以及加密后的报文进行拼接，使用“ | ”进行分割。
	encryptedBody := fmt.Sprintf("%s|%s", sm2EncryptKeyAndIVBase64, ciphertextBase64)
	return encryptedBody, nil
}

func DecryptBody(decrypts *Decrypts) ([]byte, error) {
	var (
		EncryptSlice []string
	)
	EncryptSlice = strings.Split(decrypts.EncryptBody, "|")
	if len(EncryptSlice) != 2 {
		return nil, fmt.Errorf("解密失败: 格式错误")
	}
	sm2EncryptKeyAndIV, err := base64.StdEncoding.DecodeString(EncryptSlice[0])
	if err != nil {
		return nil, fmt.Errorf("解密失败:向量格式错误: %v", err)
	}
	ciphertext, err := base64.StdEncoding.DecodeString(EncryptSlice[1])
	if err != nil {
		return nil, fmt.Errorf("解密失败:内容格式错误: %v", err)
	}
	prik, err := x509.ReadPrivateKeyFromHex(decrypts.PrivateKey)
	if err != nil {
		return nil, fmt.Errorf("解密失败:密钥错误: %v", err)
	}
	keyAndIv, err := tsm2.Decrypt(prik, sm2EncryptKeyAndIV, tsm2.C1C3C2)
	if err != nil {
		return nil, fmt.Errorf("解密失败: %v", err)
	}
	key, iv, err := Base64DecodeAndSplit(string(keyAndIv))
	if err != nil {
		return nil, fmt.Errorf("解密失败:解密后向量格式错误: %v", err)
	}
	block, err := sm4.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("IV加密失败: %v", err)
	}
	paddedPlaintext := make([]byte, len(ciphertext))
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(paddedPlaintext, ciphertext)
	res := util.PKCS5UnPadding(paddedPlaintext)
	return res, err
}
func VerifyBody(crypt string, signDataBase64 string, pubKey string) (bool, error) {
	signByte, _ := base64.StdEncoding.DecodeString(signDataBase64)
	return encrypt.Sm2Sm3VerySign(crypt, string(signByte), pubKey)
}

func SignBody(sign string, privateKey string) (string, error) {
	//签名
	signData, err := encrypt.SM3WithSM2Sign(privateKey, sign)
	return base64.StdEncoding.EncodeToString(signData), err
}

func Base64DecodeAndSplit(encoded string) ([]byte, []byte, error) {
	// 解码 Base64 字符串
	decodedBytes, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return nil, nil, err
	}

	// 拆分字节切片
	firstParam := decodedBytes[:sm4.BlockSize]
	secondParam := decodedBytes[sm4.BlockSize+1:] // +1 以跳过分隔符本身

	return firstParam, secondParam, nil
}
