//go:build !cgo

// Package btcec implements the signer.I interface for signatures and ECDH with nostr.
package btcec

import (
	"lol.mleku.dev/chk"
	"lol.mleku.dev/errorf"
	"next.orly.dev/pkg/crypto/ec/schnorr"
	"next.orly.dev/pkg/crypto/ec/secp256k1"
	"next.orly.dev/pkg/interfaces/signer"
)

// Signer is an implementation of signer.I that uses the btcec library.
type Signer struct {
	SecretKey *secp256k1.SecretKey
	PublicKey *secp256k1.PublicKey
	BTCECSec  *secp256k1.SecretKey
	pkb, skb  []byte
}

var _ signer.I = &Signer{}

// Generate creates a new Signer.
func (s *Signer) Generate() (err error) {
	if s.SecretKey, err = secp256k1.GenerateSecretKey(); chk.E(err) {
		return
	}
	s.skb = s.SecretKey.Serialize()
	s.BTCECSec = secp256k1.PrivKeyFromBytes(s.skb)
	s.PublicKey = s.SecretKey.PubKey()
	s.pkb = schnorr.SerializePubKey(s.PublicKey)
	return
}

// InitSec initialises a Signer using raw secret key bytes.
func (s *Signer) InitSec(sec []byte) (err error) {
	if len(sec) != secp256k1.SecKeyBytesLen {
		err = errorf.E("sec key must be %d bytes", secp256k1.SecKeyBytesLen)
		return
	}
	s.skb = sec
	s.SecretKey = secp256k1.SecKeyFromBytes(sec)
	s.PublicKey = s.SecretKey.PubKey()
	s.pkb = schnorr.SerializePubKey(s.PublicKey)
	s.BTCECSec = secp256k1.PrivKeyFromBytes(s.skb)
	return
}

// InitPub initializes a signature verifier Signer from raw public key bytes.
func (s *Signer) InitPub(pub []byte) (err error) {
	if s.PublicKey, err = schnorr.ParsePubKey(pub); chk.E(err) {
		return
	}
	s.pkb = pub
	return
}

// Sec returns the raw secret key bytes.
func (s *Signer) Sec() (b []byte) {
	if s == nil {
		return nil
	}
	return s.skb
}

// Pub returns the raw BIP-340 schnorr public key bytes.
func (s *Signer) Pub() (b []byte) {
	if s == nil {
		return nil
	}
	return s.pkb
}

// Sign a message with the Signer. Requires an initialised secret key.
func (s *Signer) Sign(msg []byte) (sig []byte, err error) {
	if s.SecretKey == nil {
		err = errorf.E("btcec: Signer not initialized")
		return
	}
	var si *schnorr.Signature
	if si, err = schnorr.Sign(s.SecretKey, msg); chk.E(err) {
		return
	}
	sig = si.Serialize()
	return
}

// Verify a message signature, only requires the public key is initialised.
func (s *Signer) Verify(msg, sig []byte) (valid bool, err error) {
	if s.PublicKey == nil {
		err = errorf.E("btcec: Pubkey not initialized")
		return
	}

	// First try to verify using the schnorr package
	var si *schnorr.Signature
	if si, err = schnorr.ParseSignature(sig); err == nil {
		valid = si.Verify(msg, s.PublicKey)
		return
	}

	// If parsing the signature failed, log it at debug level
	chk.D(err)

	// If the signature is exactly 64 bytes, try to verify it directly
	// This is to handle signatures created by p256k.Signer which uses libsecp256k1
	if len(sig) == schnorr.SignatureSize {
		// Create a new signature with the raw bytes
		var r secp256k1.FieldVal
		var sScalar secp256k1.ModNScalar

		// Split the signature into r and s components
		if overflow := r.SetByteSlice(sig[0:32]); !overflow {
			sScalar.SetByteSlice(sig[32:64])

			// Create a new signature and verify it
			newSig := schnorr.NewSignature(&r, &sScalar)
			valid = newSig.Verify(msg, s.PublicKey)
			return
		}
	}

	// If all verification methods failed, return an error
	err = errorf.E(
		"failed to verify signature:\n%d %s", len(sig), sig,
	)
	return
}

// Zero wipes the bytes of the secret key.
func (s *Signer) Zero() { s.SecretKey.Key.Zero() }

// ECDH creates a shared secret from a secret key and a provided public key bytes. It is advised
// to hash this result for security reasons.
func (s *Signer) ECDH(pubkeyBytes []byte) (secret []byte, err error) {
	var pub *secp256k1.PublicKey
	if pub, err = secp256k1.ParsePubKey(
		append(
			[]byte{0x02}, pubkeyBytes...,
		),
	); chk.E(err) {
		return
	}
	secret = secp256k1.GenerateSharedSecret(s.BTCECSec, pub)
	return
}

// Keygen implements a key generator. Used for such things as vanity npub mining.
type Keygen struct {
	Signer
}

// Generate a new key pair. If the result is suitable, the embedded Signer can have its contents
// extracted.
func (k *Keygen) Generate() (pubBytes []byte, err error) {
	if k.Signer.SecretKey, err = secp256k1.GenerateSecretKey(); chk.E(err) {
		return
	}
	k.Signer.PublicKey = k.SecretKey.PubKey()
	k.Signer.pkb = schnorr.SerializePubKey(k.Signer.PublicKey)
	pubBytes = k.Signer.pkb
	return
}

// KeyPairBytes returns the raw bytes of the embedded Signer.
func (k *Keygen) KeyPairBytes() (secBytes, cmprPubBytes []byte) {
	return k.Signer.SecretKey.Serialize(), k.Signer.PublicKey.SerializeCompressed()
}
