package lnwire

import (
	"fmt"
	"io"

	"github.com/btcsuite/btcd/btcec/v2"
	"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
	"github.com/lightningnetwork/lnd/tlv"
)

// NonceRecordTypeT is the TLV type used to encode a local musig2 nonce.
type NonceRecordTypeT = tlv.TlvType4

// nonceRecordType is the TLV (integer) type used to encode a local musig2
// nonce.
var nonceRecordType tlv.Type = (NonceRecordTypeT)(nil).TypeVal()

type (
	// Musig2Nonce represents a musig2 public nonce, which is the
	// concatenation of two EC points serialized in compressed format.
	Musig2Nonce [musig2.PubNonceSize]byte

	// Musig2NonceTLV is a TLV type that can be used to encode/decode a
	// musig2 nonce. This is an optional TLV.
	Musig2NonceTLV = tlv.RecordT[NonceRecordTypeT, Musig2Nonce]

	// OptMusig2NonceTLV is a TLV type that can be used to encode/decode a
	// musig2 nonce.
	OptMusig2NonceTLV = tlv.OptionalRecordT[NonceRecordTypeT, Musig2Nonce]
)

// Record returns a TLV record that can be used to encode/decode the musig2
// nonce from a given TLV stream.
func (m *Musig2Nonce) Record() tlv.Record {
	return tlv.MakeStaticRecord(
		nonceRecordType, m, musig2.PubNonceSize,
		nonceTypeEncoder, nonceTypeDecoder,
	)
}

// nonceTypeEncoder is a custom TLV encoder for the Musig2Nonce type.
func nonceTypeEncoder(w io.Writer, val interface{}, _ *[8]byte) error {
	if v, ok := val.(*Musig2Nonce); ok {
		_, err := w.Write(v[:])
		return err
	}

	return tlv.NewTypeForEncodingErr(val, "lnwire.Musig2Nonce")
}

// ValidateMusig2Nonce checks that a 66-byte MuSig2 public nonce contains two
// valid compressed secp256k1 points.
func ValidateMusig2Nonce(nonce Musig2Nonce) error {
	const compressedKeyLen = 33

	// A MuSig2 public nonce is two 33-byte compressed public keys (R1, R2).
	_, err := btcec.ParsePubKey(nonce[:compressedKeyLen])
	if err != nil {
		return fmt.Errorf("invalid first nonce point: %w", err)
	}

	_, err = btcec.ParsePubKey(nonce[compressedKeyLen:])
	if err != nil {
		return fmt.Errorf("invalid second nonce point: %w", err)
	}

	return nil
}

// nonceTypeDecoder is a custom TLV decoder for the Musig2Nonce record.
func nonceTypeDecoder(r io.Reader, val interface{}, _ *[8]byte,
	l uint64) error {

	if v, ok := val.(*Musig2Nonce); ok && l == musig2.PubNonceSize {
		_, err := io.ReadFull(r, v[:])
		if err != nil {
			return err
		}

		return ValidateMusig2Nonce(*v)
	}

	return tlv.NewTypeForDecodingErr(
		val, "lnwire.Musig2Nonce", l, musig2.PubNonceSize,
	)
}

// SomeMusig2Nonce is a helper function that creates a musig2 nonce TLV.
func SomeMusig2Nonce(nonce Musig2Nonce) OptMusig2NonceTLV {
	return tlv.SomeRecordT(
		tlv.NewRecordT[NonceRecordTypeT, Musig2Nonce](nonce),
	)
}
