package sdk

import (
	"regexp"
	"strconv"
	"strings"

	"github.com/nbd-wtf/go-nostr"
	"github.com/nbd-wtf/go-nostr/nip19"
)

type Reference struct {
	Text    string
	Profile *nostr.ProfilePointer
	Event   *nostr.EventPointer
	Entity  *nostr.EntityPointer
}

var mentionRegex = regexp.MustCompile(`\bnostr:((note|npub|naddr|nevent|nprofile)1\w+)\b|#\[(\d+)\]`)

// ParseReferences parses both NIP-08 and NIP-27 references in a single unifying interface.
func ParseReferences(evt *nostr.Event) []*Reference {
	var references []*Reference
	for _, ref := range mentionRegex.FindAllStringSubmatch(evt.Content, -1) {
		if ref[2] != "" {
			// it's a NIP-27 mention
			if prefix, data, err := nip19.Decode(ref[1]); err == nil {
				switch prefix {
				case "npub":
					references = append(references, &Reference{
						Text: ref[0],
						Profile: &nostr.ProfilePointer{
							PublicKey: data.(string), Relays: []string{},
						},
					})
				case "nprofile":
					pp := data.(nostr.ProfilePointer)
					references = append(references, &Reference{
						Text:    ref[0],
						Profile: &pp,
					})
				case "note":
					references = append(references, &Reference{
						Text:  ref[0],
						Event: &nostr.EventPointer{ID: data.(string), Relays: []string{}},
					})
				case "nevent":
					evp := data.(nostr.EventPointer)
					references = append(references, &Reference{
						Text:  ref[0],
						Event: &evp,
					})
				case "naddr":
					addr := data.(nostr.EntityPointer)
					references = append(references, &Reference{
						Text:   ref[0],
						Entity: &addr,
					})
				}
			}
		} else if ref[3] != "" {
			// it's a NIP-10 mention
			idx, err := strconv.Atoi(ref[3])
			if err != nil || len(evt.Tags) <= idx {
				continue
			}
			if tag := evt.Tags[idx]; tag != nil {
				switch tag[0] {
				case "p":
					relays := make([]string, 0, 1)
					if len(tag) > 2 && tag[2] != "" {
						relays = append(relays, tag[2])
					}
					references = append(references, &Reference{
						Text: ref[0],
						Profile: &nostr.ProfilePointer{
							PublicKey: tag[1],
							Relays:    relays,
						},
					})
				case "e":
					relays := make([]string, 0, 1)
					if len(tag) > 2 && tag[2] != "" {
						relays = append(relays, tag[2])
					}
					references = append(references, &Reference{
						Text: ref[0],
						Event: &nostr.EventPointer{
							ID:     tag[1],
							Relays: relays,
						},
					})
				case "a":
					if parts := strings.Split(ref[1], ":"); len(parts) == 3 {
						kind, _ := strconv.Atoi(parts[0])
						relays := make([]string, 0, 1)
						if len(tag) > 2 && tag[2] != "" {
							relays = append(relays, tag[2])
						}
						references = append(references, &Reference{
							Text: ref[0],
							Entity: &nostr.EntityPointer{
								Identifier: parts[2],
								PublicKey:  parts[1],
								Kind:       kind,
								Relays:     relays,
							},
						})
					}
				}
			}
		}
	}

	return references
}