//go:build cgo

package p256k

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

func init() {
	log.T.Ln("using bitcoin/secp256k1 signature library")
}

// Signer implements the signer.I interface.
//
// Either the Sec or Pub must be populated, the former is for generating
// signatures, the latter is for verifying them.
//
// When using this library only for verification, a constructor that converts
// from bytes to PubKey is needed prior to calling Verify.
type Signer struct {
	// SecretKey is the secret key.
	SecretKey *SecKey
	// PublicKey is the public key.
	PublicKey *PubKey
	// BTCECSec is needed for ECDH as currently the CGO bindings don't include it
	BTCECSec *btcec.SecretKey
	skb, pkb []byte
}

var _ signer.I = &Signer{}

// Generate a new Signer key pair using the CGO bindings to libsecp256k1
func (s *Signer) Generate() (err error) {
	var cs *Sec
	var cx *XPublicKey
	if s.skb, s.pkb, cs, cx, err = Generate(); chk.E(err) {
		return
	}
	s.SecretKey = &cs.Key
	s.PublicKey = cx.Key
	s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
	return
}

func (s *Signer) InitSec(skb []byte) (err error) {
	var cs *Sec
	var cx *XPublicKey
	// var cp *PublicKey
	if s.pkb, cs, cx, err = FromSecretBytes(skb); chk.E(err) {
		if err.Error() != "provided secret generates a public key with odd Y coordinate, fixed version returned" {
			log.E.Ln(err)
			return
		}
	}
	s.skb = skb
	s.SecretKey = &cs.Key
	s.PublicKey = cx.Key
	// s.ECPublicKey = cp.Key
	// needed for ecdh
	s.BTCECSec, _ = btcec.PrivKeyFromBytes(s.skb)
	return
}

func (s *Signer) InitPub(pub []byte) (err error) {
	var up *Pub
	if up, err = PubFromBytes(pub); chk.E(err) {
		return
	}
	s.PublicKey = &up.Key
	s.pkb = up.PubB()
	return
}

func (s *Signer) Sec() (b []byte) {
	if s == nil {
		return nil
	}
	return s.skb
}
func (s *Signer) Pub() (b []byte) {
	if s == nil {
		return nil
	}
	return s.pkb
}

// func (s *Signer) ECPub() (b []byte) { return s.pkb }

func (s *Signer) Sign(msg []byte) (sig []byte, err error) {
	if s.SecretKey == nil {
		err = errorf.E("p256k: I secret not initialized")
		return
	}
	u := ToUchar(msg)
	if sig, err = Sign(u, s.SecretKey); chk.E(err) {
		return
	}
	return
}

func (s *Signer) Verify(msg, sig []byte) (valid bool, err error) {
	if s.PublicKey == nil {
		err = errorf.E("p256k: Pubkey not initialized")
		return
	}
	var uMsg, uSig *Uchar
	if uMsg, err = Msg(msg); chk.E(err) {
		return
	}
	if uSig, err = Sig(sig); chk.E(err) {
		return
	}
	valid = Verify(uMsg, uSig, s.PublicKey)
	if !valid {
		err = errorf.E("p256k: invalid signature")
	}
	return
}

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 = btcec.GenerateSharedSecret(s.BTCECSec, pub)
	return
}

func (s *Signer) Zero() { Zero(s.SecretKey) }
