package sm9

import (
	"encoding/binary"
	"errors"
	"io"
	"math/bits"
	"sync"

	"slices"

	"github.com/emmansun/gmsm/internal/bigmod"
	"github.com/emmansun/gmsm/internal/randutil"
	"github.com/emmansun/gmsm/internal/sm9/bn256"
	"github.com/emmansun/gmsm/internal/subtle"
)

// SignMasterPrivateKey is a signature master private key, generated by KGC
type SignMasterPrivateKey struct {
	*SignMasterPublicKey        // master public key
	d                    []byte // master private key bytes
}

// SignMasterPublicKey is a signature master public key, generated by KGC
type SignMasterPublicKey struct {
	MasterPublicKey *bn256.G2 // master public key
	q               []byte    // uncompressed public key point output
	pairOnce        sync.Once
	basePoint       *bn256.GT // the result of Pair(Gen1, pub.MasterPublicKey)
	tableGenOnce    sync.Once
	table           *[32 * 2]bn256.GTFieldTable // precomputed basePoint^n
}

// SignPrivateKey is a signature private key, generated by KGC
type SignPrivateKey struct {
	PrivateKey           *bn256.G1 // user private key
	d                    []byte    // uncompressed private key point output
	*SignMasterPublicKey           // master public key
}

// EncryptMasterPrivateKey is an encryption master private key, generated by KGC
type EncryptMasterPrivateKey struct {
	*EncryptMasterPublicKey        // master public key
	d                       []byte // master private key bytes
}

// EncryptMasterPublicKey is an encryption master public key, generated by KGC
type EncryptMasterPublicKey struct {
	MasterPublicKey *bn256.G1 // public key
	q               []byte    // uncompressed public key point output
	pairOnce        sync.Once
	basePoint       *bn256.GT // the result of Pair(pub.MasterPublicKey, Gen2)
	tableGenOnce    sync.Once
	table           *[32 * 2]bn256.GTFieldTable // precomputed basePoint^n
}

// EncryptPrivateKey is an encryption private key, generated by KGC
type EncryptPrivateKey struct {
	PrivateKey              *bn256.G2 // user private key
	d                       []byte    // uncompressed private key point output
	*EncryptMasterPublicKey           // master public key
}

// GenerateSignMasterKey generates a signature master key pair for DSA usage.
func GenerateSignMasterKey(rand io.Reader) (*SignMasterPrivateKey, error) {
	key := make([]byte, len(bn256.OrderMinus1Bytes))
	randutil.MaybeReadByte(rand)

	for {
		if _, err := io.ReadFull(rand, key); err != nil {
			return nil, err
		}
		// In tests, rand will return all zeros and NewPrivateKey will reject
		// the zero key as it generates the identity as a public key. This also
		// makes this function consistent with crypto/elliptic.GenerateKey.
		key[1] ^= 0x42

		k, err := NewSignMasterPrivateKey(key)
		if err == errInvalidPrivateKey {
			continue
		}
		return k, err
	}
}

// NewSignMasterPrivateKey creates a new SignMasterPrivateKey from the given byte slice.
// The provided key must have the same length as bn256.OrderMinus1Bytes and must be less than bn256.OrderMinus1Bytes.
// If the key is invalid, an error is returned.
//
// Parameters:
//   - key: A byte slice representing the master private key.
//
// Returns:
//   - *SignMasterPrivateKey: A pointer to the newly created SignMasterPrivateKey.
//   - error: An error if the key is invalid or if there is an issue during key generation.
func NewSignMasterPrivateKey(key []byte) (*SignMasterPrivateKey, error) {
	if len(key) > len(bn256.OrderMinus1Bytes) {
		return nil, errInvalidPrivateKey
	}
	key = bn256.NormalizeScalar(key)
	if subtle.ConstantTimeAllZero(key) == 1 || !isLess(key, bn256.OrderMinus1Bytes) {
		return nil, errInvalidPrivateKey
	}
	p, err := new(bn256.G2).ScalarBaseMult(key)
	if err != nil {
		return nil, err
	}
	priv := new(SignMasterPrivateKey)
	priv.d = slices.Clone(key)
	priv.SignMasterPublicKey = new(SignMasterPublicKey)
	priv.MasterPublicKey = p
	priv.q = p.MarshalUncompressed()
	return priv, nil
}

// Bytes returns the byte representation of the SignMasterPrivateKey.
// It creates a new byte slice and appends the private key bytes to it,
// ensuring that the original private key data is not modified.
func (master *SignMasterPrivateKey) Bytes() []byte {
	return master.d
}

// GenerateUserKey generate an user dsa key.
func (master *SignMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*SignPrivateKey, error) {
	var id []byte
	id = append(append(id, uid...), hid)

	t1Nat := hashH1(id)

	d, err := bigmod.NewNat().SetBytes(master.d, orderNat)
	if err != nil {
		return nil, err
	}

	t1Nat.Add(d, orderNat)
	if t1Nat.IsZero() == 1 {
		return nil, errors.New("sm9: need to re-generate signature master private key")
	}

	t1Nat = bigmod.NewNat().Exp(t1Nat, bn256.OrderMinus2Bytes, orderNat)
	t1Nat.Mul(d, orderNat)

	priv := new(SignPrivateKey)
	priv.SignMasterPublicKey = master.SignMasterPublicKey
	g1, err := new(bn256.G1).ScalarBaseMult(t1Nat.Bytes(orderNat))
	if err != nil {
		return nil, err
	}
	priv.PrivateKey = g1
	priv.d = g1.MarshalUncompressed()

	return priv, nil
}

// Public returns the public key corresponding to the private key.
func (master *SignMasterPrivateKey) PublicKey() *SignMasterPublicKey {
	return master.SignMasterPublicKey
}

// Bytes returns the byte representation of the SignMasterPublicKey
// by marshaling the MasterPublicKey in an uncompressed format.
func (pub *SignMasterPublicKey) Bytes() []byte {
	return pub.q
}

// pair generate the basepoint once
func (pub *SignMasterPublicKey) pair() *bn256.GT {
	pub.pairOnce.Do(func() {
		pub.basePoint = bn256.Pair(bn256.Gen1, pub.MasterPublicKey)
	})
	return pub.basePoint
}

func (pub *SignMasterPublicKey) generatorTable() *[32 * 2]bn256.GTFieldTable {
	pub.tableGenOnce.Do(func() {
		pub.table = bn256.GenerateGTFieldTable(pub.pair())
	})
	return pub.table
}

// ScalarBaseMult compute basepoint^r with precomputed table
// The base point = pair(Gen1, <master public key>)
func (pub *SignMasterPublicKey) ScalarBaseMult(scalar []byte) (*bn256.GT, error) {
	tables := pub.generatorTable()
	return bn256.ScalarBaseMultGT(tables, scalar)
}

// GenerateUserPublicKey generate a signature public key for given user.
func (pub *SignMasterPublicKey) GenerateUserPublicKey(uid []byte, hid byte) *bn256.G2 {
	var buffer []byte
	buffer = append(append(buffer, uid...), hid)
	h1 := hashH1(buffer)
	p, err := new(bn256.G2).ScalarBaseMult(h1.Bytes(orderNat))
	if err != nil {
		panic(err)
	}
	p.Add(p, pub.MasterPublicKey)
	return p
}

// Bytes returns the byte representation of the SignPrivateKey.
// It marshals the private key in an uncompressed format.
func (priv *SignPrivateKey) Bytes() []byte {
	return priv.d
}

// MasterPublic returns the master public key corresponding to the private key.
func (priv *SignPrivateKey) MasterPublic() *SignMasterPublicKey {
	return priv.SignMasterPublicKey
}

// SetMasterPublicKey bind the signature master public key to it.
func (priv *SignPrivateKey) SetMasterPublicKey(pub *SignMasterPublicKey) {
	if priv.SignMasterPublicKey == nil || priv.SignMasterPublicKey.MasterPublicKey == nil {
		priv.SignMasterPublicKey = pub
	}
}

func unmarshalG2(bytes []byte) (*bn256.G2, error) {
	g2 := new(bn256.G2)
	switch bytes[0] {
	case 4:
		_, err := g2.Unmarshal(bytes[1:])
		if err != nil {
			return nil, err
		}
	case 2, 3:
		_, err := g2.UnmarshalCompressed(bytes)
		if err != nil {
			return nil, err
		}
	default:
		return nil, errors.New("sm9: invalid point identity byte")
	}
	return g2, nil
}

// UnmarshalRaw unmarsal raw bytes data to signature master public key
func (pub *SignMasterPublicKey) UnmarshalRaw(bytes []byte) error {
	g2, err := unmarshalG2(bytes)
	if err != nil {
		return err
	}
	pub.MasterPublicKey = g2
	pub.q = g2.MarshalUncompressed()
	return nil
}

func unmarshalG1(bytes []byte) (*bn256.G1, error) {
	g := new(bn256.G1)
	switch bytes[0] {
	case 4:
		_, err := g.Unmarshal(bytes[1:])
		if err != nil {
			return nil, err
		}
	case 2, 3:
		_, err := g.UnmarshalCompressed(bytes)
		if err != nil {
			return nil, err
		}
	default:
		return nil, errors.New("sm9: invalid point encoding")
	}
	return g, nil
}

// UnmarshalRaw unmarsal raw bytes data to the signature private key
// Note, priv's SignMasterPublicKey should be handled separately.
func (priv *SignPrivateKey) UnmarshalRaw(bytes []byte) error {
	g, err := unmarshalG1(bytes)
	if err != nil {
		return err
	}
	priv.PrivateKey = g
	priv.d = g.MarshalUncompressed()
	return nil
}

// GenerateEncryptMasterKey generates an encryption master key pair.
func GenerateEncryptMasterKey(rand io.Reader) (*EncryptMasterPrivateKey, error) {
	key := make([]byte, len(bn256.OrderMinus1Bytes))
	randutil.MaybeReadByte(rand)

	for {
		if _, err := io.ReadFull(rand, key); err != nil {
			return nil, err
		}
		// In tests, rand will return all zeros and NewPrivateKey will reject
		// the zero key as it generates the identity as a public key. This also
		// makes this function consistent with crypto/elliptic.GenerateKey.
		key[1] ^= 0x42

		k, err := NewEncryptMasterPrivateKey(key)
		if err == errInvalidPrivateKey {
			continue
		}
		return k, err
	}
}

// NewEncryptMasterPrivateKey creates a new EncryptMasterPrivateKey from the given key bytes.
// The key must have a length equal to bn256.OrderMinus1Bytes. If the key is all zeros or not
// less than bn256.OrderMinus1Bytes, an error is returned.
//
// Parameters:
//   - key: A byte slice representing the master private key.
//
// Returns:
//   - *EncryptMasterPrivateKey: A pointer to the newly created EncryptMasterPrivateKey.
//   - error: An error if the key is invalid or if there is an issue during key generation.
func NewEncryptMasterPrivateKey(key []byte) (*EncryptMasterPrivateKey, error) {
	if len(key) > len(bn256.OrderMinus1Bytes) {
		return nil, errInvalidPrivateKey
	}
	key = bn256.NormalizeScalar(key)
	if subtle.ConstantTimeAllZero(key) == 1 || !isLess(key, bn256.OrderMinus1Bytes) {
		return nil, errInvalidPrivateKey
	}
	p, err := new(bn256.G1).ScalarBaseMult(key)
	if err != nil {
		return nil, err
	}
	priv := new(EncryptMasterPrivateKey)
	priv.d = slices.Clone(key)
	priv.EncryptMasterPublicKey = new(EncryptMasterPublicKey)
	priv.MasterPublicKey = p
	priv.q = p.MarshalUncompressed()
	return priv, nil
}

// Bytes returns a copy of the private key bytes from the EncryptMasterPrivateKey.
// This method ensures that the original private key data is not modified by
// returning a new slice containing the same data.
func (master *EncryptMasterPrivateKey) Bytes() []byte {
	return master.d
}

// GenerateUserKey generate an encryption private key for the given user.
func (master *EncryptMasterPrivateKey) GenerateUserKey(uid []byte, hid byte) (*EncryptPrivateKey, error) {
	var id []byte
	id = append(append(id, uid...), hid)

	t1Nat := hashH1(id)

	d, err := bigmod.NewNat().SetBytes(master.d, orderNat)
	if err != nil {
		return nil, err
	}

	t1Nat.Add(d, orderNat)
	if t1Nat.IsZero() == 1 {
		return nil, errors.New("sm9: need to re-generate encryption master private key")
	}

	t1Nat = bigmod.NewNat().Exp(t1Nat, bn256.OrderMinus2Bytes, orderNat)
	t1Nat.Mul(d, orderNat)

	priv := new(EncryptPrivateKey)
	priv.EncryptMasterPublicKey = master.EncryptMasterPublicKey
	p, err := new(bn256.G2).ScalarBaseMult(t1Nat.Bytes(orderNat))
	if err != nil {
		panic(err)
	}
	priv.PrivateKey = p
	priv.d = p.MarshalUncompressed()

	return priv, nil
}

// Public returns the public key corresponding to the private key.
func (master *EncryptMasterPrivateKey) PublicKey() *EncryptMasterPublicKey {
	return master.EncryptMasterPublicKey
}

func (pub *EncryptMasterPublicKey) Bytes() []byte {
	return pub.q
}

// pair generate the basepoint once
func (pub *EncryptMasterPublicKey) pair() *bn256.GT {
	pub.pairOnce.Do(func() {
		pub.basePoint = bn256.Pair(pub.MasterPublicKey, bn256.Gen2)
	})
	return pub.basePoint
}

func (pub *EncryptMasterPublicKey) generatorTable() *[32 * 2]bn256.GTFieldTable {
	pub.tableGenOnce.Do(func() {
		pub.table = bn256.GenerateGTFieldTable(pub.pair())
	})
	return pub.table
}

// ScalarBaseMult compute basepoint^r with precomputed table.
// The base point = pair(<master public key>, Gen2)
func (pub *EncryptMasterPublicKey) ScalarBaseMult(scalar []byte) (*bn256.GT, error) {
	tables := pub.generatorTable()
	return bn256.ScalarBaseMultGT(tables, scalar)
}

// GenerateUserPublicKey generate an encryption public key for the given user.
func (pub *EncryptMasterPublicKey) GenerateUserPublicKey(uid []byte, hid byte) *bn256.G1 {
	var buffer []byte
	buffer = append(append(buffer, uid...), hid)
	h1 := hashH1(buffer)
	p, err := new(bn256.G1).ScalarBaseMult(h1.Bytes(orderNat))
	if err != nil {
		panic(err)
	}
	p.Add(p, pub.MasterPublicKey)
	return p
}

// Bytes returns the byte representation of the EncryptPrivateKey.
// It marshals the private key in an uncompressed format.
func (priv *EncryptPrivateKey) Bytes() []byte {
	return priv.d
}

// MasterPublic returns the master public key corresponding to priv.
func (priv *EncryptPrivateKey) MasterPublic() *EncryptMasterPublicKey {
	return priv.EncryptMasterPublicKey
}

// SetMasterPublicKey bind the encryption master public key to it.
func (priv *EncryptPrivateKey) SetMasterPublicKey(pub *EncryptMasterPublicKey) {
	if priv.EncryptMasterPublicKey == nil || priv.EncryptMasterPublicKey.MasterPublicKey == nil {
		priv.EncryptMasterPublicKey = pub
	}
}

// UnmarshalRaw unmarsal raw bytes data to the encryption master public key
func (pub *EncryptMasterPublicKey) UnmarshalRaw(bytes []byte) error {
	g, err := unmarshalG1(bytes)
	if err != nil {
		return err
	}
	pub.MasterPublicKey = g
	pub.q = g.MarshalUncompressed()
	return nil
}

// UnmarshalRaw unmarsal raw bytes data to the encryption private key
// Note, priv's EncryptMasterPublicKey should be handled separately.
func (priv *EncryptPrivateKey) UnmarshalRaw(bytes []byte) error {
	g, err := unmarshalG2(bytes)
	if err != nil {
		return err
	}
	priv.PrivateKey = g
	priv.d = g.MarshalUncompressed()
	return nil
}

// isLess returns whether a < b, where a and b are big-endian buffers of the
// same length and shorter than 72 bytes.
func isLess(a, b []byte) bool {
	if len(a) != len(b) {
		panic("sm9: internal error: mismatched isLess inputs")
	}

	// Copy the values into a fixed-size preallocated little-endian buffer.
	// 72 bytes is enough for every scalar in this package, and having a fixed
	// size lets us avoid heap allocations.
	if len(a) > 72 {
		panic("sm9: internal error: isLess input too large")
	}
	bufA, bufB := make([]byte, 72), make([]byte, 72)
	for i := range a {
		bufA[i], bufB[i] = a[len(a)-i-1], b[len(b)-i-1]
	}

	// Perform a subtraction with borrow.
	var borrow uint64
	for i := 0; i < len(bufA); i += 8 {
		limbA, limbB := binary.LittleEndian.Uint64(bufA[i:]), binary.LittleEndian.Uint64(bufB[i:])
		_, borrow = bits.Sub64(limbA, limbB, borrow)
	}

	// If there is a borrow at the end of the operation, then a < b.
	return borrow == 1
}

var errInvalidPrivateKey = errors.New("sm9: invalid private key")
