package sdk

import (
	"crypto/elliptic"
	"encoding/binary"
	"math/big"
	"voucher/internal/pkg/cmb/sm2/util"
)

type BaseSdk struct {
	sm2P256xx *util.Sm2P256Curve
}

func NewBaseSdk() SDK {
	sm2P256 := util.NewP256Sm2()
	return &BaseSdk{sm2P256xx: &sm2P256}
}

func (b *BaseSdk) Kdf(c elliptic.Curve, x, y *big.Int, c2 []byte) error {
	data := elliptic.Marshal(c, x, y)
	ct := uint32(1)
	length := len(c2)
	end := (length + 31) / 32
	result := make([]byte, 0)
	for i := 1; i <= end; i++ {
		bytes, err := b.sm3hash(data, b.uint32ToBytes(ct))
		if err != nil {
			return err
		}
		result = append(result, bytes...)
		ct++
	}
	last, err := b.sm3hash(data, b.uint32ToBytes(ct))
	if err != nil {
		return err
	}
	if length%32 == 0 {
		result = append(result, last...)
	} else {
		result = append(result, last[:length%32]...)
	}
	for i := 0; i < length; i++ {
		c2[i] ^= result[i]
	}
	return nil
}

func (b *BaseSdk) CalculateHash(x *big.Int, c2 []byte, y *big.Int) []byte {
	digest := util.New()
	digest.Write(util.BigIntToByte(x))
	digest.Write(c2)
	digest.Write(util.BigIntToByte(y))
	result := digest.Sum(nil)[:32]
	return result
}

func (b *BaseSdk) GetZ(x *big.Int, y *big.Int, uid []byte) []byte {
	z := util.New()
	uidLen := len(uid) * 8
	z.Write([]byte{byte((uidLen >> 8) & 0xFF)})
	z.Write([]byte{byte(uidLen & 0xFF)})
	z.Write(uid)

	//sm2P256 := util.NewP256Sm2()
	sm2P256 := b.sm2P256xx

	z.Write(util.BigIntToByte(sm2P256.A))
	z.Write(util.BigIntToByte(sm2P256.B))
	z.Write(util.BigIntToByte(sm2P256.Gx))
	z.Write(util.BigIntToByte(sm2P256.Gy))

	z.Write(x.Bytes())
	z.Write(y.Bytes())
	return z.Sum(nil)
}

func (b *BaseSdk) GetE(z []byte, data []byte) *big.Int {
	e := util.New()
	e.Write(z)
	e.Write(data)
	return new(big.Int).SetBytes(e.Sum(nil)[:32])
}

func (b *BaseSdk) uint32ToBytes(x uint32) []byte {
	var buf = make([]byte, 4)
	binary.BigEndian.PutUint32(buf, x)
	return buf
}

func (b *BaseSdk) sm3hash(sources ...[]byte) ([]byte, error) {
	bytes, err := util.JoinBytes(sources...)
	if err != nil {
		return nil, err
	}
	md := make([]byte, 32)
	h := util.New()
	h.Write(bytes)
	h.Sum(md[:0])
	return md, nil
}
