package tss

import (
	"crypto/rand"

	"github.com/decred/dcrd/dcrec/secp256k1/v4"
)

// ConcatBytes concatenates multiple byte slices into a single byte slice.
func ConcatBytes(data ...[]byte) []byte {
	var res []byte
	for _, b := range data {
		res = append(res, b...)
	}

	return res
}

// GenerateKeyPairs generates a specified number of key pairs.
// It returns a slice of KeyPair values and an error, if any.
func GenerateKeyPairs(n uint64) (KeyPairs, error) {
	var kps KeyPairs
	for i := uint64(0); i < n; i++ {
		kp, err := GenerateKeyPair()
		if err != nil {
			return nil, NewError(err, "number: %d", i)
		}

		kps = append(kps, kp)
	}

	return kps, nil
}

// GenerateKeyPair generates a new key pair.
// It returns a KeyPair value and an error, if any.
func GenerateKeyPair() (KeyPair, error) {
	key, err := secp256k1.GeneratePrivateKey()
	if err != nil {
		return KeyPair{}, NewError(ErrGenerateKeyPairFailed, "%s", err.Error())
	}

	return KeyPair{
		PrivKey: key.Serialize(),
		PubKey:  key.PubKey().SerializeCompressed(),
	}, nil
}

// GenerateDKGNonce generates a DKG (Distributed Key Generation) nonce consisting of a scalar and a point.
func GenerateDKGNonce() (Scalar, Point, error) {
	// Generate a random scalar
	scalar, err := RandomScalar()
	if err != nil {
		return nil, nil, NewError(err, "random scalar")
	}

	// Parse the scalar into a nonce
	nonce := scalar.modNScalar()

	// Generate a JacobianPoint by performing scalar base multiplication
	var pubNonce secp256k1.JacobianPoint
	secp256k1.ScalarBaseMultNonConst(nonce, &pubNonce)

	// Return the parsed scalar and point along with nil as the error
	return NewScalarFromModNScalar(nonce), NewPointFromJacobianPoint(&pubNonce), nil
}

// GenerateSigningNonce is a function responsible for producing a signing nonce
// derived from a given private key. This nonce generation ensures a uniform distribution
// of hash values over the span of the elliptic curve's order.
func GenerateSigningNonce(secret Scalar) (Scalar, error) {
	var nonce Scalar
	// We omit the implementation of a timeout here as the probability of the hash exceeding
	// the curve's order is exceptionally small (1 in 2.67e+38).
	for {
		// Generate random bytes
		random, err := RandomBytes(32)
		if err != nil {
			return nil, err
		}

		// Hash the random bytes with the private key to obtain the nonce
		nonce, err = HashNonce(random, secret)
		if err != nil {
			continue
		}

		// Return the nonce if successfully generated
		return nonce, nil
	}
}

// RandomScalar is a function that creates a random scalar value.
// This method ensures a uniform distribution of generated scalars across the span of the elliptic curve's order.
func RandomScalar() (Scalar, error) {
	var nonce Scalar
	// We omit implementing a timeout here as the probability of the hash exceeding
	// the curve's order is exceptionally small (1 in 2.67e+38).
	for {
		// Generate random bytes
		random, err := RandomBytes(32)
		if err != nil {
			return nil, err
		}

		// Create a Scalar instance from the generated bytes
		// If error, try to regenerate random value
		nonce, err = NewScalar(random)
		if err != nil {
			continue
		}

		// Return the scalar if valid
		return nonce, nil
	}
}

// RandomBytes generates random bytes.
func RandomBytes(length int) ([]byte, error) {
	b := make([]byte, length)
	if _, err := rand.Read(b); err != nil {
		return nil, NewError(ErrRandomError, "read bytes")
	}
	return b, nil
}

// PaddingBytes pads a byte slice with zero bytes to a specified length.
func PaddingBytes(data []byte, length int) []byte {
	if len(data) >= length {
		return data
	}

	padding := make([]byte, length-len(data))
	return append(padding, data...)
}
