package find

import (
	"fmt"
	"sync"
	"time"

	"git.mleku.dev/mleku/nostr/encoders/hex"
)

// TrustGraph manages trust relationships between registry services
type TrustGraph struct {
	mu           sync.RWMutex
	entries      map[string][]TrustEntry // pubkey -> trust entries
	selfPubkey   []byte                  // This registry service's pubkey
	lastUpdated  map[string]time.Time    // pubkey -> last update time
	decayFactors map[int]float64         // hop distance -> decay factor
}

// NewTrustGraph creates a new trust graph
func NewTrustGraph(selfPubkey []byte) *TrustGraph {
	return &TrustGraph{
		entries:     make(map[string][]TrustEntry),
		selfPubkey:  selfPubkey,
		lastUpdated: make(map[string]time.Time),
		decayFactors: map[int]float64{
			0: 1.0,  // Direct trust (0-hop)
			1: 0.8,  // 1-hop trust
			2: 0.6,  // 2-hop trust
			3: 0.4,  // 3-hop trust
			4: 0.0,  // 4+ hops not counted
		},
	}
}

// AddTrustGraph adds a trust graph from another registry service
func (tg *TrustGraph) AddTrustGraph(graph *TrustGraph) error {
	tg.mu.Lock()
	defer tg.mu.Unlock()

	sourcePubkey := hex.Enc(graph.selfPubkey)

	// Copy entries from the source graph
	for pubkey, entries := range graph.entries {
		// Store the trust entries
		tg.entries[pubkey] = make([]TrustEntry, len(entries))
		copy(tg.entries[pubkey], entries)
	}

	// Update last modified time
	tg.lastUpdated[sourcePubkey] = time.Now()

	return nil
}

// AddEntry adds a trust entry to the graph
func (tg *TrustGraph) AddEntry(entry TrustEntry) error {
	if err := ValidateTrustScore(entry.TrustScore); err != nil {
		return err
	}

	tg.mu.Lock()
	defer tg.mu.Unlock()

	selfPubkey := hex.Enc(tg.selfPubkey)
	tg.entries[selfPubkey] = append(tg.entries[selfPubkey], entry)
	tg.lastUpdated[selfPubkey] = time.Now()

	return nil
}

// GetTrustLevel returns the trust level for a given pubkey (0.0 to 1.0)
// This computes both direct trust and inherited trust through the web of trust
func (tg *TrustGraph) GetTrustLevel(pubkey []byte) float64 {
	tg.mu.RLock()
	defer tg.mu.RUnlock()

	pubkeyStr := hex.Enc(pubkey)
	selfPubkeyStr := hex.Enc(tg.selfPubkey)

	// Check for direct trust first (0-hop)
	if entries, ok := tg.entries[selfPubkeyStr]; ok {
		for _, entry := range entries {
			if entry.Pubkey == pubkeyStr {
				return entry.TrustScore
			}
		}
	}

	// Compute inherited trust through web of trust
	// Use breadth-first search to find shortest trust path
	maxHops := 3 // Maximum path length (configurable)
	visited := make(map[string]bool)
	queue := []trustPath{{pubkey: selfPubkeyStr, trust: 1.0, hops: 0}}
	visited[selfPubkeyStr] = true

	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]

		// Stop if we've exceeded max hops
		if current.hops > maxHops {
			continue
		}

		// Check if we found the target
		if current.pubkey == pubkeyStr {
			// Apply hop-based decay
			decayFactor := tg.decayFactors[current.hops]
			return current.trust * decayFactor
		}

		// Expand to neighbors
		if entries, ok := tg.entries[current.pubkey]; ok {
			for _, entry := range entries {
				if !visited[entry.Pubkey] {
					visited[entry.Pubkey] = true
					queue = append(queue, trustPath{
						pubkey: entry.Pubkey,
						trust:  current.trust * entry.TrustScore,
						hops:   current.hops + 1,
					})
				}
			}
		}
	}

	// No trust path found - return default minimal trust for unknown services
	return 0.0
}

// trustPath represents a path in the trust graph during BFS
type trustPath struct {
	pubkey string
	trust  float64
	hops   int
}

// GetDirectTrust returns direct trust relationships (0-hop only)
func (tg *TrustGraph) GetDirectTrust() []TrustEntry {
	tg.mu.RLock()
	defer tg.mu.RUnlock()

	selfPubkeyStr := hex.Enc(tg.selfPubkey)
	if entries, ok := tg.entries[selfPubkeyStr]; ok {
		result := make([]TrustEntry, len(entries))
		copy(result, entries)
		return result
	}

	return []TrustEntry{}
}

// RemoveEntry removes a trust entry for a given pubkey
func (tg *TrustGraph) RemoveEntry(pubkey string) {
	tg.mu.Lock()
	defer tg.mu.Unlock()

	selfPubkeyStr := hex.Enc(tg.selfPubkey)
	if entries, ok := tg.entries[selfPubkeyStr]; ok {
		filtered := make([]TrustEntry, 0, len(entries))
		for _, entry := range entries {
			if entry.Pubkey != pubkey {
				filtered = append(filtered, entry)
			}
		}
		tg.entries[selfPubkeyStr] = filtered
		tg.lastUpdated[selfPubkeyStr] = time.Now()
	}
}

// UpdateEntry updates an existing trust entry
func (tg *TrustGraph) UpdateEntry(pubkey string, newScore float64) error {
	if err := ValidateTrustScore(newScore); err != nil {
		return err
	}

	tg.mu.Lock()
	defer tg.mu.Unlock()

	selfPubkeyStr := hex.Enc(tg.selfPubkey)
	if entries, ok := tg.entries[selfPubkeyStr]; ok {
		for i, entry := range entries {
			if entry.Pubkey == pubkey {
				tg.entries[selfPubkeyStr][i].TrustScore = newScore
				tg.lastUpdated[selfPubkeyStr] = time.Now()
				return nil
			}
		}
	}

	return fmt.Errorf("trust entry not found for pubkey: %s", pubkey)
}

// GetAllEntries returns all trust entries in the graph (for debugging/export)
func (tg *TrustGraph) GetAllEntries() map[string][]TrustEntry {
	tg.mu.RLock()
	defer tg.mu.RUnlock()

	result := make(map[string][]TrustEntry)
	for pubkey, entries := range tg.entries {
		result[pubkey] = make([]TrustEntry, len(entries))
		copy(result[pubkey], entries)
	}

	return result
}

// GetTrustedServices returns a list of all directly trusted service pubkeys
func (tg *TrustGraph) GetTrustedServices() []string {
	tg.mu.RLock()
	defer tg.mu.RUnlock()

	selfPubkeyStr := hex.Enc(tg.selfPubkey)
	if entries, ok := tg.entries[selfPubkeyStr]; ok {
		pubkeys := make([]string, 0, len(entries))
		for _, entry := range entries {
			pubkeys = append(pubkeys, entry.Pubkey)
		}
		return pubkeys
	}

	return []string{}
}

// GetInheritedTrust computes inherited trust from one service to another
// This is useful for debugging and understanding trust propagation
func (tg *TrustGraph) GetInheritedTrust(fromPubkey, toPubkey string) (float64, []string) {
	tg.mu.RLock()
	defer tg.mu.RUnlock()

	// BFS to find shortest path and trust level
	type pathNode struct {
		pubkey string
		trust  float64
		hops   int
		path   []string
	}

	visited := make(map[string]bool)
	queue := []pathNode{{pubkey: fromPubkey, trust: 1.0, hops: 0, path: []string{fromPubkey}}}
	visited[fromPubkey] = true

	maxHops := 3

	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]

		if current.hops > maxHops {
			continue
		}

		// Found target
		if current.pubkey == toPubkey {
			decayFactor := tg.decayFactors[current.hops]
			return current.trust * decayFactor, current.path
		}

		// Expand neighbors
		if entries, ok := tg.entries[current.pubkey]; ok {
			for _, entry := range entries {
				if !visited[entry.Pubkey] {
					visited[entry.Pubkey] = true
					newPath := make([]string, len(current.path))
					copy(newPath, current.path)
					newPath = append(newPath, entry.Pubkey)

					queue = append(queue, pathNode{
						pubkey: entry.Pubkey,
						trust:  current.trust * entry.TrustScore,
						hops:   current.hops + 1,
						path:   newPath,
					})
				}
			}
		}
	}

	// No path found
	return 0.0, nil
}

// ExportTrustGraph exports the trust graph for this service as a TrustGraphEvent
func (tg *TrustGraph) ExportTrustGraph() *TrustGraphEvent {
	tg.mu.RLock()
	defer tg.mu.RUnlock()

	selfPubkeyStr := hex.Enc(tg.selfPubkey)
	entries := tg.entries[selfPubkeyStr]

	exported := &TrustGraphEvent{
		Event:      nil, // TODO: Create event
		Entries:    make([]TrustEntry, len(entries)),
		Expiration: time.Now().Add(TrustGraphExpiry),
	}

	copy(exported.Entries, entries)

	return exported
}

// CalculateTrustMetrics computes metrics about the trust graph
func (tg *TrustGraph) CalculateTrustMetrics() *TrustMetrics {
	tg.mu.RLock()
	defer tg.mu.RUnlock()

	metrics := &TrustMetrics{
		TotalServices:  len(tg.entries),
		DirectTrust:    0,
		IndirectTrust:  0,
		AverageTrust:   0.0,
		TrustDistribution: make(map[string]int),
	}

	selfPubkeyStr := hex.Enc(tg.selfPubkey)
	if entries, ok := tg.entries[selfPubkeyStr]; ok {
		metrics.DirectTrust = len(entries)

		var trustSum float64
		for _, entry := range entries {
			trustSum += entry.TrustScore

			// Categorize trust level
			if entry.TrustScore >= 0.8 {
				metrics.TrustDistribution["high"]++
			} else if entry.TrustScore >= 0.5 {
				metrics.TrustDistribution["medium"]++
			} else if entry.TrustScore >= 0.2 {
				metrics.TrustDistribution["low"]++
			} else {
				metrics.TrustDistribution["minimal"]++
			}
		}

		if len(entries) > 0 {
			metrics.AverageTrust = trustSum / float64(len(entries))
		}
	}

	// Calculate indirect trust (services reachable via multi-hop)
	// This is approximate - counts unique services reachable within 3 hops
	reachable := make(map[string]bool)
	queue := []string{selfPubkeyStr}
	visited := make(map[string]int) // pubkey -> hop count
	visited[selfPubkeyStr] = 0

	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]

		currentHops := visited[current]
		if currentHops >= 3 {
			continue
		}

		if entries, ok := tg.entries[current]; ok {
			for _, entry := range entries {
				if _, seen := visited[entry.Pubkey]; !seen {
					visited[entry.Pubkey] = currentHops + 1
					queue = append(queue, entry.Pubkey)
					reachable[entry.Pubkey] = true
				}
			}
		}
	}

	metrics.IndirectTrust = len(reachable) - metrics.DirectTrust
	if metrics.IndirectTrust < 0 {
		metrics.IndirectTrust = 0
	}

	return metrics
}

// TrustMetrics holds metrics about the trust graph
type TrustMetrics struct {
	TotalServices     int
	DirectTrust       int
	IndirectTrust     int
	AverageTrust      float64
	TrustDistribution map[string]int // high/medium/low/minimal counts
}
