package event

import (
	"fmt"
	"io"

	"github.com/templexxx/xhex"
	"lol.mleku.dev/chk"
	"lol.mleku.dev/errorf"
	"lol.mleku.dev/log"
	"next.orly.dev/pkg/crypto/ec/schnorr"
	"next.orly.dev/pkg/crypto/sha256"
	"next.orly.dev/pkg/encoders/ints"
	"next.orly.dev/pkg/encoders/kind"
	"next.orly.dev/pkg/encoders/tag"
	"next.orly.dev/pkg/encoders/text"
	"next.orly.dev/pkg/utils"
)

// E is the primary datatype of nostr. This is the form of the structure that
// defines its JSON string-based format.
//
// WARNING: DO NOT use json.Marshal with this type because it will not properly
// encode <, >, and & characters due to legacy bullcrap in the encoding/json
// library. Either call MarshalJSON directly or use a json.Encoder with html
// escaping disabled.
type E struct {

	// ID is the SHA256 hash of the canonical encoding of the event in binary
	// format
	ID []byte

	// Pubkey is the public key of the event creator in binary format
	Pubkey []byte

	// CreatedAt is the UNIX timestamp of the event according to the event
	// creator (never trust a timestamp!)
	CreatedAt int64

	// Kind is the nostr protocol code for the type of event. See kind.T
	Kind uint16

	// Tags are a list of tags, which are a list of strings usually structured
	// as a 3-layer scheme indicating specific features of an event.
	Tags *tag.S

	// Content is an arbitrary string that can contain anything, but usually
	// conforming to a specification relating to the Kind and the Tags.
	Content []byte

	// Sig is the signature on the ID hash that validates as coming from the
	// Pubkey in binary format.
	Sig []byte
}

var (
	jId        = []byte("id")
	jPubkey    = []byte("pubkey")
	jCreatedAt = []byte("created_at")
	jKind      = []byte("kind")
	jTags      = []byte("tags")
	jContent   = []byte("content")
	jSig       = []byte("sig")
)

// New returns a new event.E.
func New() *E {
	return &E{}
}

// Free nils all of the fields to hint to the GC that the event.E can be freed.
func (ev *E) Free() {
	ev.ID = nil
	ev.Pubkey = nil
	ev.Tags = nil
	ev.Content = nil
	ev.Sig = nil
}

// Clone creates a deep copy of the event with independent memory allocations.
// The clone does not use bufpool, ensuring it has a separate lifetime from
// the original event. This prevents corruption when the original is freed
// while the clone is still in use (e.g., in asynchronous delivery).
func (ev *E) Clone() *E {
	clone := &E{
		CreatedAt: ev.CreatedAt,
		Kind:      ev.Kind,
	}

	// Deep copy all byte slices with independent memory
	if ev.ID != nil {
		clone.ID = make([]byte, len(ev.ID))
		copy(clone.ID, ev.ID)
	}
	if ev.Pubkey != nil {
		clone.Pubkey = make([]byte, len(ev.Pubkey))
		copy(clone.Pubkey, ev.Pubkey)
	}
	if ev.Content != nil {
		clone.Content = make([]byte, len(ev.Content))
		copy(clone.Content, ev.Content)
	}
	if ev.Sig != nil {
		clone.Sig = make([]byte, len(ev.Sig))
		copy(clone.Sig, ev.Sig)
	}

	// Deep copy tags
	if ev.Tags != nil {
		clone.Tags = tag.NewS()
		for _, tg := range *ev.Tags {
			if tg != nil {
				// Create new tag with deep-copied elements
				newTag := tag.NewWithCap(len(tg.T))
				for _, element := range tg.T {
					newElement := make([]byte, len(element))
					copy(newElement, element)
					newTag.T = append(newTag.T, newElement)
				}
				clone.Tags.Append(newTag)
			}
		}
	}

	return clone
}

// EstimateSize returns a size for the event that allows for worst case scenario
// expansion of the escaped content and tags.
func (ev *E) EstimateSize() (size int) {
	size = len(ev.ID)*2 + len(ev.Pubkey)*2 + len(ev.Sig)*2 + len(ev.Content)*2
	if ev.Tags == nil {
		return
	}
	for _, v := range *ev.Tags {
		for _, w := range (*v).T {
			size += len(w) * 2
		}
	}
	return
}

func (ev *E) Marshal(dst []byte) (b []byte) {
	b = dst
	b = append(b, '{')
	b = append(b, '"')
	b = append(b, jId...)
	b = append(b, `":"`...)
	b = append(b, make([]byte, 2*sha256.Size)...)
	xhex.Encode(b[len(b)-2*sha256.Size:], ev.ID)
	b = append(b, `","`...)
	b = append(b, jPubkey...)
	b = append(b, `":"`...)
	b = b[:len(b)+2*schnorr.PubKeyBytesLen]
	xhex.Encode(b[len(b)-2*schnorr.PubKeyBytesLen:], ev.Pubkey)
	b = append(b, `","`...)
	b = append(b, jCreatedAt...)
	b = append(b, `":`...)
	b = ints.New(ev.CreatedAt).Marshal(b)
	b = append(b, `,"`...)
	b = append(b, jKind...)
	b = append(b, `":`...)
	b = ints.New(ev.Kind).Marshal(b)
	b = append(b, `,"`...)
	b = append(b, jTags...)
	b = append(b, `":`...)
	if ev.Tags != nil {
		b = ev.Tags.Marshal(b)
	} else {
		// Emit empty array for nil tags to keep JSON valid
		b = append(b, '[', ']')
	}
	b = append(b, `,"`...)
	b = append(b, jContent...)
	b = append(b, `":"`...)
	b = text.NostrEscape(b, ev.Content)
	b = append(b, `","`...)
	b = append(b, jSig...)
	b = append(b, `":"`...)
	b = append(b, make([]byte, 2*schnorr.SignatureSize)...)
	xhex.Encode(b[len(b)-2*schnorr.SignatureSize:], ev.Sig)
	b = append(b, `"}`...)
	return
}

// MarshalJSON marshals an event.E into a JSON byte string.
//
// WARNING: if json.Marshal is called in the hopes of invoking this function on
// an event, if it has <, > or * in the content or tags they are escaped into
// unicode escapes and break the event ID. Call this function directly in order
// to bypass this issue.
func (ev *E) MarshalJSON() (b []byte, err error) {
	b = ev.Marshal(nil)
	return
}

func (ev *E) Serialize() (b []byte) {
	b = ev.Marshal(nil)
	return
}

// Unmarshal unmarshalls a JSON string into an event.E.
func (ev *E) Unmarshal(b []byte) (rem []byte, err error) {
	key := make([]byte, 0, 9)
	for ; len(b) > 0; b = b[1:] {
		// Skip whitespace
		if isWhitespace(b[0]) {
			continue
		}
		if b[0] == '{' {
			b = b[1:]
			goto BetweenKeys
		}
	}
	goto eof
BetweenKeys:
	for ; len(b) > 0; b = b[1:] {
		// Skip whitespace
		if isWhitespace(b[0]) {
			continue
		}
		if b[0] == '"' {
			b = b[1:]
			goto InKey
		}
	}
	goto eof
InKey:
	for ; len(b) > 0; b = b[1:] {
		if b[0] == '"' {
			b = b[1:]
			goto InKV
		}
		key = append(key, b[0])
	}
	goto eof
InKV:
	for ; len(b) > 0; b = b[1:] {
		// Skip whitespace
		if isWhitespace(b[0]) {
			continue
		}
		if b[0] == ':' {
			b = b[1:]
			goto InVal
		}
	}
	goto eof
InVal:
	// Skip whitespace before value
	for len(b) > 0 && isWhitespace(b[0]) {
		b = b[1:]
	}
	switch key[0] {
	case jId[0]:
		if !utils.FastEqual(jId, key) {
			goto invalid
		}
		var id []byte
		if id, b, err = text.UnmarshalHex(b); chk.E(err) {
			return
		}
		if len(id) != sha256.Size {
			err = errorf.E(
				"invalid Subscription, require %d got %d", sha256.Size,
				len(id),
			)
			return
		}
		ev.ID = id
		goto BetweenKV
	case jPubkey[0]:
		if !utils.FastEqual(jPubkey, key) {
			goto invalid
		}
		var pk []byte
		if pk, b, err = text.UnmarshalHex(b); chk.E(err) {
			return
		}
		if len(pk) != schnorr.PubKeyBytesLen {
			err = errorf.E(
				"invalid pubkey, require %d got %d",
				schnorr.PubKeyBytesLen, len(pk),
			)
			return
		}
		ev.Pubkey = pk
		goto BetweenKV
	case jKind[0]:
		if !utils.FastEqual(jKind, key) {
			goto invalid
		}
		k := kind.New(0)
		if b, err = k.Unmarshal(b); chk.E(err) {
			return
		}
		ev.Kind = k.ToU16()
		goto BetweenKV
	case jTags[0]:
		if !utils.FastEqual(jTags, key) {
			goto invalid
		}
		ev.Tags = new(tag.S)
		if b, err = ev.Tags.Unmarshal(b); chk.E(err) {
			return
		}
		goto BetweenKV
	case jSig[0]:
		if !utils.FastEqual(jSig, key) {
			goto invalid
		}
		var sig []byte
		if sig, b, err = text.UnmarshalHex(b); chk.E(err) {
			return
		}
		if len(sig) != schnorr.SignatureSize {
			err = errorf.E(
				"invalid sig length, require %d got %d '%s'\n%s",
				schnorr.SignatureSize, len(sig), b, b,
			)
			return
		}
		ev.Sig = sig
		goto BetweenKV
	case jContent[0]:
		if key[1] == jContent[1] {
			if !utils.FastEqual(jContent, key) {
				goto invalid
			}
			if ev.Content, b, err = text.UnmarshalQuoted(b); chk.T(err) {
				return
			}
			goto BetweenKV
		} else if key[1] == jCreatedAt[1] {
			if !utils.FastEqual(jCreatedAt, key) {
				goto invalid
			}
			i := ints.New(0)
			if b, err = i.Unmarshal(b); chk.T(err) {
				return
			}
			ev.CreatedAt = i.Int64()
			goto BetweenKV
		} else {
			goto invalid
		}
	default:
		goto invalid
	}
BetweenKV:
	key = key[:0]
	for ; len(b) > 0; b = b[1:] {
		// Skip whitespace
		if isWhitespace(b[0]) {
			continue
		}
		switch {
		case len(b) == 0:
			return
		case b[0] == '}':
			b = b[1:]
			goto AfterClose
		case b[0] == ',':
			b = b[1:]
			goto BetweenKeys
		case b[0] == '"':
			b = b[1:]
			goto InKey
		}
	}
	// If we reach here, the buffer ended unexpectedly. Treat as end-of-object
	goto AfterClose
AfterClose:
	rem = b
	return
invalid:
	err = fmt.Errorf(
		"invalid key,\n'%s'\n'%s'\n'%s'", string(b), string(b[:len(b)]),
		string(b),
	)
	return
eof:
	err = io.EOF
	return
}

// UnmarshalJSON unmarshalls a JSON string into an event.E.
//
// Call ev.Free() to return the provided buffer to the bufpool afterwards.
func (ev *E) UnmarshalJSON(b []byte) (err error) {
	log.I.F("UnmarshalJSON: '%s'", b)
	_, err = ev.Unmarshal(b)
	return
}

// isWhitespace returns true if the byte is a whitespace character (space, tab, newline, carriage return).
func isWhitespace(b byte) bool {
	return b == ' ' || b == '\t' || b == '\n' || b == '\r'
}

// S is an array of event.E that sorts in reverse chronological order.
type S []*E

// Len returns the length of the event.Es.
func (ev S) Len() int { return len(ev) }

// Less returns whether the first is newer than the second (larger unix
// timestamp).
func (ev S) Less(i, j int) bool { return ev[i].CreatedAt > ev[j].CreatedAt }

// Swap two indexes of the event.Es with each other.
func (ev S) Swap(i, j int) { ev[i], ev[j] = ev[j], ev[i] }

// C is a channel that carries event.E.
type C chan *E
