package directory_client

import (
	"git.mleku.dev/mleku/nostr/encoders/event"
	"next.orly.dev/pkg/protocol/directory"
)

// EventCollector provides utility methods for collecting specific types of
// directory events from a slice.
type EventCollector struct {
	events []*event.E
}

// NewEventCollector creates a new event collector for the given events.
func NewEventCollector(events []*event.E) *EventCollector {
	return &EventCollector{events: events}
}

// RelayIdentities returns all relay identity declarations.
func (ec *EventCollector) RelayIdentities() (identities []*directory.RelayIdentityAnnouncement) {
	identities = make([]*directory.RelayIdentityAnnouncement, 0)
	for _, ev := range ec.events {
		if uint16(ev.Kind) == 39100 {
			if identity, err := directory.ParseRelayIdentityAnnouncement(ev); err == nil {
				identities = append(identities, identity)
			}
		}
	}
	return
}

// TrustActs returns all trust acts.
func (ec *EventCollector) TrustActs() (acts []*directory.TrustAct) {
	acts = make([]*directory.TrustAct, 0)
	for _, ev := range ec.events {
		if uint16(ev.Kind) == 39101 {
			if act, err := directory.ParseTrustAct(ev); err == nil {
				acts = append(acts, act)
			}
		}
	}
	return
}

// GroupTagActs returns all group tag acts.
func (ec *EventCollector) GroupTagActs() (acts []*directory.GroupTagAct) {
	acts = make([]*directory.GroupTagAct, 0)
	for _, ev := range ec.events {
		if uint16(ev.Kind) == 39102 {
			if act, err := directory.ParseGroupTagAct(ev); err == nil {
				acts = append(acts, act)
			}
		}
	}
	return
}

// PublicKeyAdvertisements returns all public key advertisements.
func (ec *EventCollector) PublicKeyAdvertisements() (ads []*directory.PublicKeyAdvertisement) {
	ads = make([]*directory.PublicKeyAdvertisement, 0)
	for _, ev := range ec.events {
		if uint16(ev.Kind) == 39103 {
			if ad, err := directory.ParsePublicKeyAdvertisement(ev); err == nil {
				ads = append(ads, ad)
			}
		}
	}
	return
}

// ReplicationRequests returns all replication requests.
func (ec *EventCollector) ReplicationRequests() (requests []*directory.DirectoryEventReplicationRequest) {
	requests = make([]*directory.DirectoryEventReplicationRequest, 0)
	for _, ev := range ec.events {
		if uint16(ev.Kind) == 39104 {
			if req, err := directory.ParseDirectoryEventReplicationRequest(ev); err == nil {
				requests = append(requests, req)
			}
		}
	}
	return
}

// ReplicationResponses returns all replication responses.
func (ec *EventCollector) ReplicationResponses() (responses []*directory.DirectoryEventReplicationResponse) {
	responses = make([]*directory.DirectoryEventReplicationResponse, 0)
	for _, ev := range ec.events {
		if uint16(ev.Kind) == 39105 {
			if resp, err := directory.ParseDirectoryEventReplicationResponse(ev); err == nil {
				responses = append(responses, resp)
			}
		}
	}
	return
}

// FindRelayIdentity finds a relay identity by relay URL.
func FindRelayIdentity(events []*event.E, relayURL string) (*directory.RelayIdentityAnnouncement, bool) {
	normalizedURL := NormalizeRelayURL(relayURL)
	for _, ev := range events {
		if uint16(ev.Kind) == 39100 {
			if identity, err := directory.ParseRelayIdentityAnnouncement(ev); err == nil {
				if NormalizeRelayURL(identity.RelayURL) == normalizedURL {
					return identity, true
				}
			}
		}
	}
	return nil, false
}

// FindTrustActsForRelay finds all trust acts targeting a specific relay.
func FindTrustActsForRelay(events []*event.E, targetPubkey string) (acts []*directory.TrustAct) {
	acts = make([]*directory.TrustAct, 0)
	for _, ev := range events {
		if uint16(ev.Kind) == 39101 {
			if act, err := directory.ParseTrustAct(ev); err == nil {
				if act.TargetPubkey == targetPubkey {
					acts = append(acts, act)
				}
			}
		}
	}
	return
}

// FindGroupTagActsForRelay finds all group tag acts targeting a specific relay.
// Note: This function needs to be updated based on the actual GroupTagAct structure
// which doesn't have a Target field. The filtering logic should be clarified.
func FindGroupTagActsForRelay(events []*event.E, targetPubkey string) (acts []*directory.GroupTagAct) {
	acts = make([]*directory.GroupTagAct, 0)
	for _, ev := range events {
		if uint16(ev.Kind) == 39102 {
			if act, err := directory.ParseGroupTagAct(ev); err == nil {
				// Filter by actor since GroupTagAct doesn't have a Target field
				if act.Actor == targetPubkey {
					acts = append(acts, act)
				}
			}
		}
	}
	return
}

// FindGroupTagActsByGroup finds all group tag acts for a specific group.
func FindGroupTagActsByGroup(events []*event.E, groupID string) (acts []*directory.GroupTagAct) {
	acts = make([]*directory.GroupTagAct, 0)
	for _, ev := range events {
		if uint16(ev.Kind) == 39102 {
			if act, err := directory.ParseGroupTagAct(ev); err == nil {
				if act.GroupID == groupID {
					acts = append(acts, act)
				}
			}
		}
	}
	return
}

// TrustGraph represents a directed graph of trust relationships.
type TrustGraph struct {
	// edges maps source pubkey -> list of trust acts
	edges map[string][]*directory.TrustAct
}

// NewTrustGraph creates a new trust graph instance.
func NewTrustGraph() *TrustGraph {
	return &TrustGraph{
		edges: make(map[string][]*directory.TrustAct),
	}
}

// AddTrustAct adds a trust act to the graph.
func (tg *TrustGraph) AddTrustAct(act *directory.TrustAct) {
	if act == nil {
		return
	}
	source := string(act.Event.Pubkey)
	tg.edges[source] = append(tg.edges[source], act)
}

// GetTrustActs returns all trust acts from a source pubkey.
func (tg *TrustGraph) GetTrustActs(source string) []*directory.TrustAct {
	return tg.edges[source]
}

// GetTrustedBy returns all pubkeys that trust the given target.
func (tg *TrustGraph) GetTrustedBy(target string) []string {
	trustedBy := make([]string, 0)
	for source, acts := range tg.edges {
		for _, act := range acts {
			if act.TargetPubkey == target {
				trustedBy = append(trustedBy, source)
				break
			}
		}
	}
	return trustedBy
}

// GetTrustTargets returns all pubkeys trusted by the given source.
func (tg *TrustGraph) GetTrustTargets(source string) []string {
	acts := tg.edges[source]
	targets := make(map[string]bool)
	for _, act := range acts {
		targets[act.TargetPubkey] = true
	}

	result := make([]string, 0, len(targets))
	for target := range targets {
		result = append(result, target)
	}
	return result
}

// BuildTrustGraph builds a trust graph from a collection of events.
func BuildTrustGraph(events []*event.E) *TrustGraph {
	graph := NewTrustGraph()
	for _, ev := range events {
		if uint16(ev.Kind) == 39101 {
			if act, err := directory.ParseTrustAct(ev); err == nil {
				graph.AddTrustAct(act)
			}
		}
	}
	return graph
}
