package cmb

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"github.com/ZZMarquis/gm/sm4"
	"github.com/tjfoc/gmsm/sm2"
	"strings"
	sm22 "voucher/internal/pkg/cmb/sm2"
	"voucher/internal/pkg/cmb/sm2/model"
	"voucher/internal/pkg/cmb/sm2/sdk"
)

// GenerateSm2Key 生成密钥对
func GenerateSm2Key() (string, string) {
	pri, _ := sm2.GenerateKey(rand.Reader)
	hexPri := pri.D.Text(16)
	// 获取公钥
	publicKeyHex := publicKeyToString(&pri.PublicKey)
	return hexPri, publicKeyHex
}

// publicKeyToString 公钥sm2.PublicKey转字符串(与java中org.bouncycastle.crypto生成的公私钥完全互通使用)
func publicKeyToString(publicKey *sm2.PublicKey) string {
	xBytes := publicKey.X.Bytes()
	yBytes := publicKey.Y.Bytes()

	// 确保坐标字节切片长度相同
	byteLen := len(xBytes)
	if len(yBytes) > byteLen {
		byteLen = len(yBytes)
	}

	// 为坐标补齐前导零
	xBytes = append(make([]byte, byteLen-len(xBytes)), xBytes...)
	yBytes = append(make([]byte, byteLen-len(yBytes)), yBytes...)

	// 添加 "04" 前缀
	publicKeyBytes := append([]byte{0x04}, append(xBytes, yBytes...)...)

	return hex.EncodeToString(publicKeyBytes)
}

func Encrypt(sopPublicKey, input string) (string, error) {

	sm4Key := GenerateSM4Key()
	iv := GetSM4IV()
	encryptedBody, err := sm4.CBCEncrypt(sm4Key, iv, Padding([]byte(input), 1))

	keyAndIv := AssemblingByteArray(sm4Key, iv)

	kvTmp, err := sm22.NewSm2().
		SetHexPublicKey(sopPublicKey).
		SetData([]byte(base64.StdEncoding.EncodeToString(keyAndIv))).
		SetSdk(sdk.NewCmbLifeSdk()).
		SetCipherType(model.C1C3C2).
		Encrypt().
		ToString()

	if err != nil {
		return "", err
	}
	fmt.Println(base64.StdEncoding.EncodeToString([]byte(kvTmp)))

	return fmt.Sprintf("%s|%s", base64.StdEncoding.EncodeToString([]byte(kvTmp)), base64.StdEncoding.EncodeToString(encryptedBody)), nil
}

func Decrypt(privateKey, input string) (string, error) {

	tmpDataArr := strings.Split(input, "|")
	if len(tmpDataArr) != 2 {
		return "", fmt.Errorf("数据格式错误")
	}

	keyAndIvStr := tmpDataArr[0]
	encryptedBody := tmpDataArr[1]

	kvBase64Tmp, err := sm22.NewSm2().
		SetHexPrivateKey(privateKey).
		SetBase64StringData(keyAndIvStr).
		SetSdk(sdk.NewCmbLifeSdk()).
		SetCipherType(model.C1C3C2).
		Decrypt().
		ToString()

	if err != nil {
		return "", err
	}

	kvTmp, err := base64.StdEncoding.DecodeString(kvBase64Tmp)
	if err != nil {
		return "", err
	}

	if len(kvTmp) != 33 {
		return "", fmt.Errorf("iv长度不等于33")
	}

	plainKey := kvTmp[0:16]
	plainIv := kvTmp[17:33]

	data2, err := base64.StdEncoding.DecodeString(encryptedBody)
	if err != nil {
		return "", err
	}

	plainText, err := sm4.CBCDecrypt(plainKey, plainIv, data2)
	if err != nil {
		return "", err
	}

	return string(Padding(plainText, 0)), nil
}

func Sign(privateKey, input string) (string, error) {

	signData, err := sm22.NewSm2().
		SetHexPrivateKey(privateKey).
		SetData([]byte(input)).
		Sign().
		ToString()

	if err != nil {
		return "", err
	}

	return base64.StdEncoding.EncodeToString([]byte(signData)), nil
}

func Verify(publicKey, input, sign string) (bool, error) {

	ok, err := sm22.NewSm2().
		SetHexPublicKey(publicKey).
		SetData([]byte(input)).
		SetBase64Signature(sign).
		Verify().
		ToBool()

	if err != nil {
		return false, err
	}

	if !ok {
		return false, nil
	}

	return true, nil
}
