package directory_client

import (
	"sync"
	"time"

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

// TrustCalculator computes aggregate trust scores from multiple trust acts.
//
// It maintains a collection of trust acts and provides methods to calculate
// weighted trust scores for relay public keys.
type TrustCalculator struct {
	mu   sync.RWMutex
	acts map[string][]*directory.TrustAct
}

// NewTrustCalculator creates a new trust calculator instance.
func NewTrustCalculator() *TrustCalculator {
	return &TrustCalculator{
		acts: make(map[string][]*directory.TrustAct),
	}
}

// AddAct adds a trust act to the calculator.
func (tc *TrustCalculator) AddAct(act *directory.TrustAct) {
	if act == nil {
		return
	}

	tc.mu.Lock()
	defer tc.mu.Unlock()

	targetPubkey := act.TargetPubkey
	tc.acts[targetPubkey] = append(tc.acts[targetPubkey], act)
}

// CalculateTrust calculates an aggregate trust score for a public key.
//
// The score is computed as a weighted average where:
//   - high trust = 100
//   - medium trust = 50
//   - low trust = 25
//
// Expired trust acts are excluded from the calculation.
// Returns a score between 0 and 100.
func (tc *TrustCalculator) CalculateTrust(pubkey string) float64 {
	tc.mu.RLock()
	defer tc.mu.RUnlock()

	acts := tc.acts[pubkey]
	if len(acts) == 0 {
		return 0
	}

	now := time.Now()
	var total float64
	var count int

	// Weight mapping
	weights := map[directory.TrustLevel]float64{
		directory.TrustLevelHigh:   100,
		directory.TrustLevelMedium: 50,
		directory.TrustLevelLow:    25,
	}

	for _, act := range acts {
		// Skip expired acts
		if act.Expiry != nil && act.Expiry.Before(now) {
			continue
		}

		weight, ok := weights[act.TrustLevel]
		if !ok {
			continue
		}

		total += weight
		count++
	}

	if count == 0 {
		return 0
	}

	return total / float64(count)
}

// GetActs returns all trust acts for a specific public key.
func (tc *TrustCalculator) GetActs(pubkey string) []*directory.TrustAct {
	tc.mu.RLock()
	defer tc.mu.RUnlock()

	acts := tc.acts[pubkey]
	result := make([]*directory.TrustAct, len(acts))
	copy(result, acts)
	return result
}

// GetActiveTrustActs returns only non-expired trust acts for a public key.
func (tc *TrustCalculator) GetActiveTrustActs(pubkey string) []*directory.TrustAct {
	tc.mu.RLock()
	defer tc.mu.RUnlock()

	acts := tc.acts[pubkey]
	now := time.Now()
	result := make([]*directory.TrustAct, 0)

	for _, act := range acts {
		if act.Expiry == nil || act.Expiry.After(now) {
			result = append(result, act)
		}
	}

	return result
}

// Clear removes all trust acts from the calculator.
func (tc *TrustCalculator) Clear() {
	tc.mu.Lock()
	defer tc.mu.Unlock()

	tc.acts = make(map[string][]*directory.TrustAct)
}

// GetAllPubkeys returns all public keys that have trust acts.
func (tc *TrustCalculator) GetAllPubkeys() []string {
	tc.mu.RLock()
	defer tc.mu.RUnlock()

	pubkeys := make([]string, 0, len(tc.acts))
	for pubkey := range tc.acts {
		pubkeys = append(pubkeys, pubkey)
	}
	return pubkeys
}

// ReplicationFilter manages replication decisions based on trust scores.
//
// It uses a TrustCalculator to compute trust scores and determines which
// relays are trusted enough for replication based on a minimum threshold.
type ReplicationFilter struct {
	mu            sync.RWMutex
	trustCalc     *TrustCalculator
	minTrustScore float64
	trustedRelays map[string]bool
}

// NewReplicationFilter creates a new replication filter with a minimum trust score threshold.
func NewReplicationFilter(minTrustScore float64) *ReplicationFilter {
	return &ReplicationFilter{
		trustCalc:     NewTrustCalculator(),
		minTrustScore: minTrustScore,
		trustedRelays: make(map[string]bool),
	}
}

// AddTrustAct adds a trust act and updates the trusted relays set.
func (rf *ReplicationFilter) AddTrustAct(act *directory.TrustAct) {
	if act == nil {
		return
	}

	rf.trustCalc.AddAct(act)

	// Update trusted relays based on new trust score
	score := rf.trustCalc.CalculateTrust(act.TargetPubkey)

	rf.mu.Lock()
	defer rf.mu.Unlock()

	if score >= rf.minTrustScore {
		rf.trustedRelays[act.TargetPubkey] = true
	} else {
		delete(rf.trustedRelays, act.TargetPubkey)
	}
}

// ShouldReplicate checks if a relay is trusted enough for replication.
func (rf *ReplicationFilter) ShouldReplicate(pubkey string) bool {
	rf.mu.RLock()
	defer rf.mu.RUnlock()

	return rf.trustedRelays[pubkey]
}

// GetTrustedRelays returns all trusted relay public keys.
func (rf *ReplicationFilter) GetTrustedRelays() []string {
	rf.mu.RLock()
	defer rf.mu.RUnlock()

	relays := make([]string, 0, len(rf.trustedRelays))
	for pubkey := range rf.trustedRelays {
		relays = append(relays, pubkey)
	}
	return relays
}

// GetTrustScore returns the trust score for a relay.
func (rf *ReplicationFilter) GetTrustScore(pubkey string) float64 {
	return rf.trustCalc.CalculateTrust(pubkey)
}

// SetMinTrustScore updates the minimum trust score threshold and recalculates trusted relays.
func (rf *ReplicationFilter) SetMinTrustScore(minScore float64) {
	rf.mu.Lock()
	defer rf.mu.Unlock()

	rf.minTrustScore = minScore

	// Recalculate trusted relays with new threshold
	rf.trustedRelays = make(map[string]bool)
	for _, pubkey := range rf.trustCalc.GetAllPubkeys() {
		score := rf.trustCalc.CalculateTrust(pubkey)
		if score >= rf.minTrustScore {
			rf.trustedRelays[pubkey] = true
		}
	}
}

// GetMinTrustScore returns the current minimum trust score threshold.
func (rf *ReplicationFilter) GetMinTrustScore() float64 {
	rf.mu.RLock()
	defer rf.mu.RUnlock()

	return rf.minTrustScore
}

// FilterEvents filters events to only those from trusted relays.
func (rf *ReplicationFilter) FilterEvents(events []*event.E) []*event.E {
	rf.mu.RLock()
	defer rf.mu.RUnlock()

	filtered := make([]*event.E, 0)
	for _, ev := range events {
		if rf.trustedRelays[string(ev.Pubkey)] {
			filtered = append(filtered, ev)
		}
	}
	return filtered
}
