//go:build !(js && wasm)

package database

import (
	"lol.mleku.dev/log"
	"next.orly.dev/pkg/database/indexes/types"
)

// AddInboundRefsToResult collects inbound references (events that reference discovered items)
// for events at a specific depth in the result.
//
// For example, if you have a follows graph result and want to find all kind-7 reactions
// to posts by users at depth 1, this collects those reactions and adds them to result.InboundRefs.
//
// Parameters:
// - result: The graph result to augment with ref data
// - depth: The depth at which to collect refs (0 = all depths)
// - kinds: Event kinds to collect (e.g., [7] for reactions, [6] for reposts)
func (d *D) AddInboundRefsToResult(result *GraphResult, depth int, kinds []uint16) error {
	// Determine which events to find refs for
	var targetEventIDs []string

	if depth == 0 {
		// Collect for all depths
		targetEventIDs = result.GetAllEvents()
	} else {
		targetEventIDs = result.GetEventsAtDepth(depth)
	}

	// Also collect refs for events authored by pubkeys in the result
	// This is common for "find reactions to posts by my follows" queries
	pubkeys := result.GetAllPubkeys()
	for _, pubkeyHex := range pubkeys {
		pubkeySerial, err := d.PubkeyHexToSerial(pubkeyHex)
		if err != nil {
			continue
		}

		// Get events authored by this pubkey
		// For efficiency, limit to relevant event kinds that might have reactions
		authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, []uint16{1, 30023}) // notes and articles
		if err != nil {
			continue
		}

		for _, eventSerial := range authoredEvents {
			eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
			if err != nil {
				continue
			}
			// Add to target list if not already tracking
			if !result.HasEvent(eventIDHex) {
				targetEventIDs = append(targetEventIDs, eventIDHex)
			}
		}
	}

	// For each target event, find referencing events
	for _, eventIDHex := range targetEventIDs {
		eventSerial, err := d.EventIDHexToSerial(eventIDHex)
		if err != nil {
			continue
		}

		refSerials, err := d.GetReferencingEvents(eventSerial, kinds)
		if err != nil {
			continue
		}

		for _, refSerial := range refSerials {
			refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
			if err != nil {
				continue
			}

			// Get the kind of the referencing event
			// For now, use the first kind in the filter (assumes single kind queries)
			// TODO: Look up actual event kind from index if needed
			if len(kinds) > 0 {
				result.AddInboundRef(kinds[0], eventIDHex, refEventIDHex)
			}
		}
	}

	log.D.F("AddInboundRefsToResult: collected refs for %d target events", len(targetEventIDs))

	return nil
}

// AddOutboundRefsToResult collects outbound references (events referenced by discovered items).
//
// For example, find all events that posts by users at depth 1 reference (quoted posts, replied-to posts).
func (d *D) AddOutboundRefsToResult(result *GraphResult, depth int, kinds []uint16) error {
	// Determine source events
	var sourceEventIDs []string

	if depth == 0 {
		sourceEventIDs = result.GetAllEvents()
	} else {
		sourceEventIDs = result.GetEventsAtDepth(depth)
	}

	// Also include events authored by pubkeys in result
	pubkeys := result.GetAllPubkeys()
	for _, pubkeyHex := range pubkeys {
		pubkeySerial, err := d.PubkeyHexToSerial(pubkeyHex)
		if err != nil {
			continue
		}

		authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, kinds)
		if err != nil {
			continue
		}

		for _, eventSerial := range authoredEvents {
			eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
			if err != nil {
				continue
			}
			if !result.HasEvent(eventIDHex) {
				sourceEventIDs = append(sourceEventIDs, eventIDHex)
			}
		}
	}

	// For each source event, find referenced events
	for _, eventIDHex := range sourceEventIDs {
		eventSerial, err := d.EventIDHexToSerial(eventIDHex)
		if err != nil {
			continue
		}

		refSerials, err := d.GetETagsFromEventSerial(eventSerial)
		if err != nil {
			continue
		}

		for _, refSerial := range refSerials {
			refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
			if err != nil {
				continue
			}

			// Use first kind for categorization
			if len(kinds) > 0 {
				result.AddOutboundRef(kinds[0], eventIDHex, refEventIDHex)
			}
		}
	}

	log.D.F("AddOutboundRefsToResult: collected refs from %d source events", len(sourceEventIDs))

	return nil
}

// CollectRefsForPubkeys collects inbound references to events by specific pubkeys.
// This is useful for "find all reactions to posts by these users" queries.
//
// Parameters:
// - pubkeySerials: The pubkeys whose events should be checked for refs
// - refKinds: Event kinds to collect (e.g., [7] for reactions)
// - eventKinds: Event kinds to check for refs (e.g., [1] for notes)
func (d *D) CollectRefsForPubkeys(
	pubkeySerials []*types.Uint40,
	refKinds []uint16,
	eventKinds []uint16,
) (*GraphResult, error) {
	result := NewGraphResult()

	for _, pubkeySerial := range pubkeySerials {
		// Get events by this author
		authoredEvents, err := d.GetEventsByAuthor(pubkeySerial, eventKinds)
		if err != nil {
			continue
		}

		for _, eventSerial := range authoredEvents {
			eventIDHex, err := d.GetEventIDFromSerial(eventSerial)
			if err != nil {
				continue
			}

			// Find refs to this event
			refSerials, err := d.GetReferencingEvents(eventSerial, refKinds)
			if err != nil {
				continue
			}

			for _, refSerial := range refSerials {
				refEventIDHex, err := d.GetEventIDFromSerial(refSerial)
				if err != nil {
					continue
				}

				// Add to result
				if len(refKinds) > 0 {
					result.AddInboundRef(refKinds[0], eventIDHex, refEventIDHex)
				}
			}
		}
	}

	return result, nil
}
