package utils

import (
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"math/big"
	"strings"
	"voucher/internal/pkg/cmbv2/model"
	"voucher/internal/pkg/helper"
)

func GenerateSM4Key() []byte {
	str := "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
	buffer := make([]byte, 16)
	for i := 0; i < 16; i++ {
		nextInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(str))))
		buffer[i] = str[nextInt.Int64()]
	}
	return buffer
}

// GetSM4IV 获取SM4的IV
func GetSM4IV() []byte {
	return RandomBytes(16)
}

func RandomBytes(length int) []byte {
	str := "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
	buffer := make([]byte, length)
	for i := 0; i < 16; i++ {
		nextInt, _ := rand.Int(rand.Reader, big.NewInt(int64(len(str))))
		buffer[i] = str[nextInt.Int64()]
	}
	return buffer
}

func Padding(input []byte, mode int) []byte {
	if input == nil {
		return nil
	} else {
		var ret []byte
		if mode == 1 {
			p := 16 - len(input)%16
			ret = make([]byte, len(input)+p)
			copy(ret, input)

			for i := 0; i < p; i++ {
				ret[len(input)+i] = byte(p)
			}
		} else {
			p := input[len(input)-1]
			ret = make([]byte, len(input)-int(p))
			copy(ret, input[:len(input)-int(p)])
		}

		return ret
	}
}

func AssemblingByteArray(key, iv []byte) []byte {
	os := make([]byte, 0)
	os = append(os, key...)
	os = append(os, []byte("|")...)
	os = append(os, iv...)
	return os
}

func SortStructStr(req interface{}) string {
	kvRows := helper.SortStructFieldsByKey(req)

	var strToBeSigned strings.Builder
	for _, kv := range kvRows {
		if kv.Key == "sign" {
			continue
		}
		if kv.Value == "" {
			continue
		}
		strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
	}

	return strings.TrimRight(strToBeSigned.String(), "&")
}

func BigIntToByte(n *big.Int) []byte {
	byteArray := n.Bytes()

	// If the bytes is not a multiple of 32, pad with zero bytes.
	byteArrLen := len(byteArray)
	KeyBytes := 32
	if byteArrLen == KeyBytes {
		return byteArray
	}
	byteArray = append(make([]byte, KeyBytes-byteArrLen), byteArray...)

	// If the most significant byte's most significant bit is set,
	// prepend a 0 byte to the slice to avoid being interpreted as a negative number.
	if (byteArray[0] & 0x80) != 0 {
		byteArray = append([]byte{0}, byteArray...)
	}
	return byteArray
}

func JoinBytes(params ...[]byte) ([]byte, error) {
	var buffer bytes.Buffer
	for i := 0; i < len(params); i++ {
		_, err := buffer.Write(params[i])
		if err != nil {
			return nil, err
		}
	}
	return buffer.Bytes(), nil
}

func HexToPrivateKey(params *Sm2P256Curve, d []byte) (*model.PrivateKey, error) {
	k := new(big.Int).SetBytes(d)

	c := NewP256Sm2()

	//params := c.Params()
	n := new(big.Int).Sub(params.N, model.One)
	if k.Cmp(n) >= 0 {
		return nil, fmt.Errorf("privateKey is overflow")
	}

	pri := &model.PrivateKey{
		PublicKey: &model.PublicKey{},
		D:         nil,
	}

	pri.PublicKey.Curve = c
	pri.D = k
	pri.PublicKey.X, pri.PublicKey.Y = c.ScalarBaseMult(k.Bytes())

	return pri, nil
}

func HexToPublicKey(curve *Sm2P256Curve, d []byte) (*model.PublicKey, error) {

	if len(d) == 65 && d[0] == byte(0x04) {
		d = d[1:]
	}

	if len(d) != 64 {
		return nil, fmt.Errorf("publicKey is not 64 bytes: %d", len(d))
	}

	pub := new(model.PublicKey)

	pub.Curve = curve

	pub.X = new(big.Int).SetBytes(d[:32])
	pub.Y = new(big.Int).SetBytes(d[32:])

	return pub, nil
}

func PrivateKeyToHex(key *model.PrivateKey) string {
	return key.D.Text(16)
}

func PublicKeyToHex(key *model.PublicKey) string {
	x := key.X.Bytes()
	y := key.Y.Bytes()
	if n := len(x); n < 32 {
		x = append(zeroByteSlice()[:32-n], x...)
	}
	if n := len(y); n < 32 {
		y = append(zeroByteSlice()[:32-n], y...)
	}
	var c []byte
	c = append(c, x...)
	c = append(c, y...)
	c = append([]byte{0x04}, c...)
	return hex.EncodeToString(c)
}

// 32byte
func zeroByteSlice() []byte {
	return []byte{
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
	}
}

func HexToSignature(hexStr string) (s model.Signature, err error) {
	signData, err := hex.DecodeString(hexStr)
	if err != nil {
		return
	}
	rBy := make([]byte, 33)
	copy(rBy[1:], signData[:32])
	rBy[0] = 0x00
	s.R = new(big.Int).SetBytes(rBy)

	sBy := make([]byte, 33)
	copy(sBy[1:], signData[32:64])
	sBy[0] = 0x00
	s.S = new(big.Int).SetBytes(sBy)
	return
}
