package neo4j

import (
	"context"
	"fmt"
	"strings"

	"git.mleku.dev/mleku/nostr/encoders/hex"
	"next.orly.dev/pkg/protocol/graph"
)

// FindMentions finds events that mention a pubkey via p-tags.
// This returns events grouped by depth, where depth represents how the events relate:
//   - Depth 1: Events that directly mention the seed pubkey
//   - Depth 2+: Not typically used for mentions (reserved for future expansion)
//
// The kinds parameter filters which event kinds to include (e.g., [1] for notes only,
// [1,7] for notes and reactions, etc.)
//
// Uses Neo4j MENTIONS relationships created by SaveEvent when processing p-tags.
func (n *N) FindMentions(pubkey []byte, kinds []uint16) (graph.GraphResultI, error) {
	result := NewGraphResult()

	if len(pubkey) != 32 {
		return result, fmt.Errorf("invalid pubkey length: expected 32, got %d", len(pubkey))
	}

	pubkeyHex := strings.ToLower(hex.Enc(pubkey))
	ctx := context.Background()

	// Build kinds filter if specified
	var kindsFilter string
	params := map[string]any{
		"pubkey": pubkeyHex,
	}

	if len(kinds) > 0 {
		// Convert uint16 slice to int64 slice for Neo4j
		kindsInt := make([]int64, len(kinds))
		for i, k := range kinds {
			kindsInt[i] = int64(k)
		}
		params["kinds"] = kindsInt
		kindsFilter = "AND e.kind IN $kinds"
	}

	// Query for events that mention this pubkey
	// The MENTIONS relationship is created by SaveEvent when processing p-tags
	cypher := fmt.Sprintf(`
		MATCH (e:Event)-[:MENTIONS]->(u:NostrUser {pubkey: $pubkey})
		WHERE true %s
		RETURN e.id AS event_id
		ORDER BY e.created_at DESC
	`, kindsFilter)

	queryResult, err := n.ExecuteRead(ctx, cypher, params)
	if err != nil {
		return result, fmt.Errorf("failed to query mentions: %w", err)
	}

	// Add all found events at depth 1
	for queryResult.Next(ctx) {
		record := queryResult.Record()
		eventID, ok := record.Values[0].(string)
		if !ok || eventID == "" {
			continue
		}

		// Normalize to lowercase for consistency
		eventID = strings.ToLower(eventID)
		result.AddEventAtDepth(eventID, 1)
	}

	n.Logger.Debugf("FindMentions: found %d events mentioning pubkey %s", result.TotalEvents, safePrefix(pubkeyHex, 16))

	return result, nil
}

// FindMentionsFromHex is a convenience wrapper that accepts hex-encoded pubkey.
func (n *N) FindMentionsFromHex(pubkeyHex string, kinds []uint16) (*GraphResult, error) {
	pubkey, err := hex.Dec(pubkeyHex)
	if err != nil {
		return nil, err
	}
	result, err := n.FindMentions(pubkey, kinds)
	if err != nil {
		return nil, err
	}
	return result.(*GraphResult), nil
}

// FindMentionsByPubkeys returns events that mention any of the given pubkeys.
// Useful for finding mentions across a set of followed accounts.
func (n *N) FindMentionsByPubkeys(pubkeys []string, kinds []uint16) (*GraphResult, error) {
	result := NewGraphResult()

	if len(pubkeys) == 0 {
		return result, nil
	}

	ctx := context.Background()

	// Build kinds filter if specified
	var kindsFilter string
	params := map[string]any{
		"pubkeys": pubkeys,
	}

	if len(kinds) > 0 {
		kindsInt := make([]int64, len(kinds))
		for i, k := range kinds {
			kindsInt[i] = int64(k)
		}
		params["kinds"] = kindsInt
		kindsFilter = "AND e.kind IN $kinds"
	}

	// Query for events that mention any of the pubkeys
	cypher := fmt.Sprintf(`
		MATCH (e:Event)-[:MENTIONS]->(u:NostrUser)
		WHERE u.pubkey IN $pubkeys %s
		RETURN DISTINCT e.id AS event_id
		ORDER BY e.created_at DESC
	`, kindsFilter)

	queryResult, err := n.ExecuteRead(ctx, cypher, params)
	if err != nil {
		return result, fmt.Errorf("failed to query mentions: %w", err)
	}

	for queryResult.Next(ctx) {
		record := queryResult.Record()
		eventID, ok := record.Values[0].(string)
		if !ok || eventID == "" {
			continue
		}

		eventID = strings.ToLower(eventID)
		result.AddEventAtDepth(eventID, 1)
	}

	return result, nil
}
