package relaytester

import (
	"crypto/rand"
	"fmt"
	"time"

	"lol.mleku.dev/chk"
	"git.mleku.dev/mleku/nostr/interfaces/signer/p8k"
	"git.mleku.dev/mleku/nostr/encoders/bech32encoding"
	"git.mleku.dev/mleku/nostr/encoders/event"
	"git.mleku.dev/mleku/nostr/encoders/hex"
	"git.mleku.dev/mleku/nostr/encoders/kind"
	"git.mleku.dev/mleku/nostr/encoders/tag"
)

// KeyPair represents a test keypair.
type KeyPair struct {
	Secret *p8k.Signer
	Pubkey []byte
	Nsec   string
	Npub   string
}

// GenerateKeyPair generates a new keypair for testing.
func GenerateKeyPair() (kp *KeyPair, err error) {
	kp = &KeyPair{}
	if kp.Secret, err = p8k.New(); chk.E(err) {
		return
	}
	if err = kp.Secret.Generate(); chk.E(err) {
		return
	}
	kp.Pubkey = kp.Secret.Pub()
	nsecBytes, err := bech32encoding.BinToNsec(kp.Secret.Sec())
	if chk.E(err) {
		return
	}
	kp.Nsec = string(nsecBytes)
	npubBytes, err := bech32encoding.BinToNpub(kp.Pubkey)
	if chk.E(err) {
		return
	}
	kp.Npub = string(npubBytes)
	return
}

// CreateEvent creates a signed event with the given parameters.
func CreateEvent(signer *p8k.Signer, kindNum uint16, content string, tags *tag.S) (ev *event.E, err error) {
	ev = event.New()
	ev.CreatedAt = time.Now().Unix()
	ev.Kind = kindNum
	ev.Content = []byte(content)
	if tags != nil {
		ev.Tags = tags
	} else {
		ev.Tags = tag.NewS()
	}
	if err = ev.Sign(signer); chk.E(err) {
		return
	}
	return
}

// CreateEventWithTags creates an event with specific tags.
func CreateEventWithTags(signer *p8k.Signer, kindNum uint16, content string, tagPairs [][]string) (ev *event.E, err error) {
	tags := tag.NewS()
	for _, pair := range tagPairs {
		if len(pair) >= 2 {
			// Build tag fields as []byte variadic arguments
			tagFields := make([][]byte, len(pair))
			tagFields[0] = []byte(pair[0])
			for i := 1; i < len(pair); i++ {
				tagFields[i] = []byte(pair[i])
			}
			tags.Append(tag.NewFromBytesSlice(tagFields...))
		}
	}
	return CreateEvent(signer, kindNum, content, tags)
}

// CreateReplaceableEvent creates a replaceable event (kind 0-3, 10000-19999).
func CreateReplaceableEvent(signer *p8k.Signer, kindNum uint16, content string) (ev *event.E, err error) {
	return CreateEvent(signer, kindNum, content, nil)
}

// CreateEphemeralEvent creates an ephemeral event (kind 20000-29999).
func CreateEphemeralEvent(signer *p8k.Signer, kindNum uint16, content string) (ev *event.E, err error) {
	return CreateEvent(signer, kindNum, content, nil)
}

// CreateDeleteEvent creates a deletion event (kind 5).
func CreateDeleteEvent(signer *p8k.Signer, eventIDs [][]byte, reason string) (ev *event.E, err error) {
	tags := tag.NewS()
	for _, id := range eventIDs {
		// e tags must contain hex-encoded event IDs
		tags.Append(tag.NewFromBytesSlice([]byte("e"), []byte(hex.Enc(id))))
	}
	if reason != "" {
		tags.Append(tag.NewFromBytesSlice([]byte("content"), []byte(reason)))
	}
	return CreateEvent(signer, kind.EventDeletion.K, reason, tags)
}

// CreateParameterizedReplaceableEvent creates a parameterized replaceable event (kind 30000-39999).
func CreateParameterizedReplaceableEvent(signer *p8k.Signer, kindNum uint16, content string, dTag string) (ev *event.E, err error) {
	tags := tag.NewS()
	tags.Append(tag.NewFromBytesSlice([]byte("d"), []byte(dTag)))
	return CreateEvent(signer, kindNum, content, tags)
}

// RandomID generates a random 32-byte ID.
func RandomID() (id []byte, err error) {
	id = make([]byte, 32)
	if _, err = rand.Read(id); err != nil {
		return nil, fmt.Errorf("failed to generate random ID: %w", err)
	}
	return
}

// MustHex decodes a hex string or panics.
func MustHex(s string) []byte {
	b, err := hex.Dec(s)
	if err != nil {
		panic(fmt.Sprintf("invalid hex: %s", s))
	}
	return b
}

// HexID returns the hex-encoded event ID.
func HexID(ev *event.E) string {
	return hex.Enc(ev.ID)
}
