package temporal

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"chorus.services/bzzz/pkg/ucxl"
)

// querySystemImpl implements decision-hop based query operations
type querySystemImpl struct {
	mu sync.RWMutex
	
	// Reference to the temporal graph
	graph *temporalGraphImpl
	navigator DecisionNavigator
	analyzer InfluenceAnalyzer
	detector StalenessDetector
	
	// Query optimization
	queryCache map[string]interface{}
	cacheTimeout time.Duration
	lastCacheClean time.Time
	
	// Query statistics
	queryStats map[string]*QueryStatistics
}

// QueryStatistics represents statistics for different query types
type QueryStatistics struct {
	QueryType       string        `json:"query_type"`
	TotalQueries    int64         `json:"total_queries"`
	AverageTime     time.Duration `json:"average_time"`
	CacheHits       int64         `json:"cache_hits"`
	CacheMisses     int64         `json:"cache_misses"`
	LastQuery       time.Time     `json:"last_query"`
}

// HopQuery represents a decision-hop based query
type HopQuery struct {
	StartAddress    ucxl.Address    `json:"start_address"`    // Starting point
	MaxHops         int             `json:"max_hops"`         // Maximum hops to traverse
	Direction       string          `json:"direction"`        // "forward", "backward", "both"
	FilterCriteria  *HopFilter      `json:"filter_criteria"`  // Filtering options
	SortCriteria    *HopSort        `json:"sort_criteria"`    // Sorting options
	Limit           int             `json:"limit"`            // Maximum results
	IncludeMetadata bool            `json:"include_metadata"` // Include detailed metadata
}

// HopFilter represents filtering criteria for hop queries
type HopFilter struct {
	ChangeReasons       []ChangeReason  `json:"change_reasons"`       // Filter by change reasons
	ImpactScopes        []ImpactScope   `json:"impact_scopes"`        // Filter by impact scopes  
	MinConfidence       float64         `json:"min_confidence"`       // Minimum confidence threshold
	MaxAge              time.Duration   `json:"max_age"`              // Maximum age of decisions
	DecisionMakers      []string        `json:"decision_makers"`      // Filter by decision makers
	Tags                []string        `json:"tags"`                 // Filter by context tags
	Technologies        []string        `json:"technologies"`         // Filter by technologies
	MinInfluenceCount   int             `json:"min_influence_count"`  // Minimum number of influences
	ExcludeStale        bool            `json:"exclude_stale"`        // Exclude stale contexts
	OnlyMajorDecisions  bool            `json:"only_major_decisions"` // Only major decisions
}

// HopSort represents sorting criteria for hop queries  
type HopSort struct {
	SortBy        string `json:"sort_by"`        // "hops", "time", "confidence", "influence"
	SortDirection string `json:"sort_direction"` // "asc", "desc"
	SecondarySort string `json:"secondary_sort"` // Secondary sort field
}

// HopQueryResult represents the result of a hop-based query
type HopQueryResult struct {
	Query           *HopQuery         `json:"query"`            // Original query
	Results         []*HopResult      `json:"results"`          // Query results
	TotalFound      int               `json:"total_found"`      // Total results found
	ExecutionTime   time.Duration     `json:"execution_time"`   // Query execution time
	FromCache       bool              `json:"from_cache"`       // Whether result came from cache
	QueryPath       []*QueryPathStep  `json:"query_path"`       // Path of query execution
	Statistics      *QueryExecution   `json:"statistics"`       // Execution statistics
}

// HopResult represents a single result from a hop query
type HopResult struct {
	Address         ucxl.Address      `json:"address"`          // Context address
	HopDistance     int               `json:"hop_distance"`     // Decision hops from start
	TemporalNode    *TemporalNode     `json:"temporal_node"`    // Temporal node data
	Path            []*DecisionStep   `json:"path"`             // Path from start to this result
	Relationship    string            `json:"relationship"`     // Relationship type
	RelevanceScore  float64           `json:"relevance_score"`  // Relevance to query
	MatchReasons    []string          `json:"match_reasons"`    // Why this matched
	Metadata        map[string]interface{} `json:"metadata"`     // Additional metadata
}

// QueryPathStep represents a step in query execution path
type QueryPathStep struct {
	Step            int               `json:"step"`             // Step number
	Operation       string            `json:"operation"`        // Operation performed
	NodesExamined   int               `json:"nodes_examined"`   // Nodes examined in this step
	NodesFiltered   int               `json:"nodes_filtered"`   // Nodes filtered out
	Duration        time.Duration     `json:"duration"`         // Step duration
	Description     string            `json:"description"`      // Step description
}

// QueryExecution represents query execution statistics
type QueryExecution struct {
	StartTime       time.Time         `json:"start_time"`       // Query start time
	EndTime         time.Time         `json:"end_time"`         // Query end time
	Duration        time.Duration     `json:"duration"`         // Total duration
	NodesVisited    int               `json:"nodes_visited"`    // Total nodes visited
	EdgesTraversed  int               `json:"edges_traversed"`  // Total edges traversed
	CacheAccesses   int               `json:"cache_accesses"`   // Cache access count
	FilterSteps     int               `json:"filter_steps"`     // Number of filter steps
	SortOperations  int               `json:"sort_operations"`  // Number of sort operations
	MemoryUsed      int64             `json:"memory_used"`      // Estimated memory used
}

// NewQuerySystem creates a new decision-hop query system
func NewQuerySystem(graph *temporalGraphImpl, navigator DecisionNavigator, 
	analyzer InfluenceAnalyzer, detector StalenessDetector) *querySystemImpl {
	return &querySystemImpl{
		graph:          graph,
		navigator:      navigator,
		analyzer:       analyzer,
		detector:       detector,
		queryCache:     make(map[string]interface{}),
		cacheTimeout:   time.Minute * 10,
		lastCacheClean: time.Now(),
		queryStats:     make(map[string]*QueryStatistics),
	}
}

// ExecuteHopQuery executes a decision-hop based query
func (qs *querySystemImpl) ExecuteHopQuery(ctx context.Context, query *HopQuery) (*HopQueryResult, error) {
	startTime := time.Now()
	
	// Validate query
	if err := qs.validateQuery(query); err != nil {
		return nil, fmt.Errorf("invalid query: %w", err)
	}
	
	// Check cache
	cacheKey := qs.generateCacheKey(query)
	if cached, found := qs.getFromCache(cacheKey); found {
		if result, ok := cached.(*HopQueryResult); ok {
			result.FromCache = true
			qs.updateQueryStats("hop_query", time.Since(startTime), true)
			return result, nil
		}
	}
	
	// Execute query
	result, err := qs.executeHopQueryInternal(ctx, query)
	if err != nil {
		return nil, err
	}
	
	// Set execution time and cache result
	result.ExecutionTime = time.Since(startTime)
	result.FromCache = false
	qs.setCache(cacheKey, result)
	qs.updateQueryStats("hop_query", result.ExecutionTime, false)
	
	return result, nil
}

// FindDecisionsWithinHops finds all decisions within N hops of a given address
func (qs *querySystemImpl) FindDecisionsWithinHops(ctx context.Context, address ucxl.Address, 
	maxHops int, filter *HopFilter) ([]*HopResult, error) {
	
	query := &HopQuery{
		StartAddress:    address,
		MaxHops:         maxHops,
		Direction:       "both",
		FilterCriteria:  filter,
		SortCriteria:    &HopSort{SortBy: "hops", SortDirection: "asc"},
		IncludeMetadata: false,
	}
	
	result, err := qs.ExecuteHopQuery(ctx, query)
	if err != nil {
		return nil, err
	}
	
	return result.Results, nil
}

// FindInfluenceChain finds the chain of influence between two decisions
func (qs *querySystemImpl) FindInfluenceChain(ctx context.Context, from, to ucxl.Address) ([]*DecisionStep, error) {
	// Use the temporal graph's path finding
	return qs.graph.FindDecisionPath(ctx, from, to)
}

// AnalyzeDecisionGenealogy analyzes the genealogy of decisions for a context
func (qs *querySystemImpl) AnalyzeDecisionGenealogy(ctx context.Context, address ucxl.Address) (*DecisionGenealogy, error) {
	qs.mu.RLock()
	defer qs.mu.RUnlock()
	
	// Get evolution history
	history, err := qs.graph.GetEvolutionHistory(ctx, address)
	if err != nil {
		return nil, fmt.Errorf("failed to get evolution history: %w", err)
	}
	
	// Get decision timeline
	timeline, err := qs.navigator.GetDecisionTimeline(ctx, address, true, 10)
	if err != nil {
		return nil, fmt.Errorf("failed to get decision timeline: %w", err)
	}
	
	// Analyze ancestry
	ancestry := qs.analyzeAncestry(history)
	
	// Analyze descendants  
	descendants := qs.analyzeDescendants(address, 5)
	
	// Find influential ancestors
	influentialAncestors := qs.findInfluentialAncestors(history)
	
	// Calculate genealogy metrics
	metrics := qs.calculateGenealogyMetrics(history, descendants)
	
	genealogy := &DecisionGenealogy{
		Address:              address,
		DirectAncestors:      ancestry.DirectAncestors,
		AllAncestors:         ancestry.AllAncestors,
		DirectDescendants:    descendants.DirectDescendants,
		AllDescendants:       descendants.AllDescendants,
		InfluentialAncestors: influentialAncestors,
		GenealogyDepth:       ancestry.MaxDepth,
		BranchingFactor:      descendants.BranchingFactor,
		DecisionTimeline:     timeline,
		Metrics:             metrics,
		AnalyzedAt:          time.Now(),
	}
	
	return genealogy, nil
}

// FindSimilarDecisionPatterns finds decisions with similar patterns
func (qs *querySystemImpl) FindSimilarDecisionPatterns(ctx context.Context, referenceAddress ucxl.Address, 
	maxResults int) ([]*SimilarDecisionMatch, error) {
	
	qs.mu.RLock()
	defer qs.mu.RUnlock()
	
	// Get reference node
	refNode, err := qs.graph.getLatestNodeUnsafe(referenceAddress)
	if err != nil {
		return nil, fmt.Errorf("reference node not found: %w", err)
	}
	
	matches := make([]*SimilarDecisionMatch, 0)
	
	// Compare with all other nodes
	for _, node := range qs.graph.nodes {
		if node.UCXLAddress.String() == referenceAddress.String() {
			continue // Skip self
		}
		
		similarity := qs.calculateDecisionSimilarity(refNode, node)
		if similarity > 0.3 { // Threshold for meaningful similarity
			match := &SimilarDecisionMatch{
				Address:         node.UCXLAddress,
				TemporalNode:    node,
				SimilarityScore: similarity,
				SimilarityReasons: qs.getSimilarityReasons(refNode, node),
				PatternType:     qs.identifyPatternType(refNode, node),
				Confidence:      similarity * 0.9, // Slightly lower confidence
			}
			matches = append(matches, match)
		}
	}
	
	// Sort by similarity score
	sort.Slice(matches, func(i, j int) bool {
		return matches[i].SimilarityScore > matches[j].SimilarityScore
	})
	
	// Limit results
	if maxResults > 0 && len(matches) > maxResults {
		matches = matches[:maxResults]
	}
	
	return matches, nil
}

// DiscoverDecisionClusters discovers clusters of related decisions
func (qs *querySystemImpl) DiscoverDecisionClusters(ctx context.Context, minClusterSize int) ([]*DecisionCluster, error) {
	qs.mu.RLock()
	defer qs.mu.RUnlock()
	
	// Use influence analyzer to get clusters
	analysis, err := qs.analyzer.AnalyzeInfluenceNetwork(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to analyze influence network: %w", err)
	}
	
	// Filter clusters by minimum size
	clusters := make([]*DecisionCluster, 0)
	for _, community := range analysis.Communities {
		if len(community.Nodes) >= minClusterSize {
			cluster := qs.convertCommunityToCluster(community)
			clusters = append(clusters, cluster)
		}
	}
	
	return clusters, nil
}

// Internal query execution

func (qs *querySystemImpl) executeHopQueryInternal(ctx context.Context, query *HopQuery) (*HopQueryResult, error) {
	execution := &QueryExecution{
		StartTime: time.Now(),
	}
	
	queryPath := make([]*QueryPathStep, 0)
	
	// Step 1: Get starting node
	step1Start := time.Now()
	startNode, err := qs.graph.getLatestNodeUnsafe(query.StartAddress)
	if err != nil {
		return nil, fmt.Errorf("start node not found: %w", err)
	}
	
	queryPath = append(queryPath, &QueryPathStep{
		Step:          1,
		Operation:     "get_start_node",
		NodesExamined: 1,
		NodesFiltered: 0,
		Duration:      time.Since(step1Start),
		Description:   "Retrieved starting node",
	})
	
	// Step 2: Traverse decision graph
	step2Start := time.Now()
	candidates := qs.traverseDecisionGraph(startNode, query.MaxHops, query.Direction)
	execution.NodesVisited = len(candidates)
	
	queryPath = append(queryPath, &QueryPathStep{
		Step:          2,
		Operation:     "traverse_graph",
		NodesExamined: len(candidates),
		NodesFiltered: 0,
		Duration:      time.Since(step2Start),
		Description:   fmt.Sprintf("Traversed decision graph up to %d hops", query.MaxHops),
	})
	
	// Step 3: Apply filters
	step3Start := time.Now()
	filtered := qs.applyFilters(candidates, query.FilterCriteria)
	execution.FilterSteps = 1
	
	queryPath = append(queryPath, &QueryPathStep{
		Step:          3,
		Operation:     "apply_filters",
		NodesExamined: len(candidates),
		NodesFiltered: len(candidates) - len(filtered),
		Duration:      time.Since(step3Start),
		Description:   fmt.Sprintf("Applied filters, removed %d candidates", len(candidates)-len(filtered)),
	})
	
	// Step 4: Calculate relevance scores
	step4Start := time.Now()
	results := qs.calculateRelevanceScores(filtered, startNode, query)
	
	queryPath = append(queryPath, &QueryPathStep{
		Step:          4,
		Operation:     "calculate_relevance",
		NodesExamined: len(filtered),
		NodesFiltered: 0,
		Duration:      time.Since(step4Start),
		Description:   "Calculated relevance scores",
	})
	
	// Step 5: Sort results
	step5Start := time.Time{}
	if query.SortCriteria != nil {
		step5Start = time.Now()
		qs.sortResults(results, query.SortCriteria)
		execution.SortOperations = 1
		
		queryPath = append(queryPath, &QueryPathStep{
			Step:          5,
			Operation:     "sort_results",
			NodesExamined: len(results),
			NodesFiltered: 0,
			Duration:      time.Since(step5Start),
			Description:   fmt.Sprintf("Sorted by %s %s", query.SortCriteria.SortBy, query.SortCriteria.SortDirection),
		})
	}
	
	// Step 6: Apply limit
	totalFound := len(results)
	if query.Limit > 0 && len(results) > query.Limit {
		results = results[:query.Limit]
	}
	
	// Complete execution statistics
	execution.EndTime = time.Now()
	execution.Duration = execution.EndTime.Sub(execution.StartTime)
	
	result := &HopQueryResult{
		Query:         query,
		Results:       results,
		TotalFound:    totalFound,
		ExecutionTime: execution.Duration,
		FromCache:     false,
		QueryPath:     queryPath,
		Statistics:    execution,
	}
	
	return result, nil
}

func (qs *querySystemImpl) traverseDecisionGraph(startNode *TemporalNode, maxHops int, direction string) []*hopCandidate {
	candidates := make([]*hopCandidate, 0)
	visited := make(map[string]bool)
	
	// BFS traversal
	queue := []*hopCandidate{{
		node:     startNode,
		distance: 0,
		path:     []*DecisionStep{},
	}}
	
	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]
		
		nodeID := current.node.ID
		if visited[nodeID] || current.distance > maxHops {
			continue
		}
		visited[nodeID] = true
		
		// Add to candidates (except start node)
		if current.distance > 0 {
			candidates = append(candidates, current)
		}
		
		// Add neighbors based on direction
		if direction == "forward" || direction == "both" {
			qs.addForwardNeighbors(current, &queue, maxHops)
		}
		
		if direction == "backward" || direction == "both" {
			qs.addBackwardNeighbors(current, &queue, maxHops)
		}
	}
	
	return candidates
}

func (qs *querySystemImpl) applyFilters(candidates []*hopCandidate, filter *HopFilter) []*hopCandidate {
	if filter == nil {
		return candidates
	}
	
	filtered := make([]*hopCandidate, 0)
	
	for _, candidate := range candidates {
		if qs.passesFilter(candidate, filter) {
			filtered = append(filtered, candidate)
		}
	}
	
	return filtered
}

func (qs *querySystemImpl) passesFilter(candidate *hopCandidate, filter *HopFilter) bool {
	node := candidate.node
	
	// Change reason filter
	if len(filter.ChangeReasons) > 0 {
		found := false
		for _, reason := range filter.ChangeReasons {
			if node.ChangeReason == reason {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	
	// Impact scope filter
	if len(filter.ImpactScopes) > 0 {
		found := false
		for _, scope := range filter.ImpactScopes {
			if node.ImpactScope == scope {
				found = true
				break
			}
		}
		if !found {
			return false
		}
	}
	
	// Confidence filter
	if filter.MinConfidence > 0 && node.Confidence < filter.MinConfidence {
		return false
	}
	
	// Age filter
	if filter.MaxAge > 0 && time.Since(node.Timestamp) > filter.MaxAge {
		return false
	}
	
	// Decision maker filter
	if len(filter.DecisionMakers) > 0 {
		if decision, exists := qs.graph.decisions[node.DecisionID]; exists {
			found := false
			for _, maker := range filter.DecisionMakers {
				if decision.Maker == maker {
					found = true
					break
				}
			}
			if !found {
				return false
			}
		} else {
			return false // No decision metadata
		}
	}
	
	// Technology filter
	if len(filter.Technologies) > 0 && node.Context != nil {
		found := false
		for _, filterTech := range filter.Technologies {
			for _, nodeTech := range node.Context.Technologies {
				if nodeTech == filterTech {
					found = true
					break
				}
			}
			if found {
				break
			}
		}
		if !found {
			return false
		}
	}
	
	// Tag filter
	if len(filter.Tags) > 0 && node.Context != nil {
		found := false
		for _, filterTag := range filter.Tags {
			for _, nodeTag := range node.Context.Tags {
				if nodeTag == filterTag {
					found = true
					break
				}
			}
			if found {
				break
			}
		}
		if !found {
			return false
		}
	}
	
	// Influence count filter
	if filter.MinInfluenceCount > 0 && len(node.Influences) < filter.MinInfluenceCount {
		return false
	}
	
	// Staleness filter
	if filter.ExcludeStale && node.Staleness > 0.6 {
		return false
	}
	
	// Major decisions filter
	if filter.OnlyMajorDecisions && !qs.isMajorDecision(node) {
		return false
	}
	
	return true
}

func (qs *querySystemImpl) calculateRelevanceScores(candidates []*hopCandidate, startNode *TemporalNode, query *HopQuery) []*HopResult {
	results := make([]*HopResult, len(candidates))
	
	for i, candidate := range candidates {
		relevanceScore := qs.calculateRelevance(candidate, startNode, query)
		matchReasons := qs.getMatchReasons(candidate, query.FilterCriteria)
		
		results[i] = &HopResult{
			Address:        candidate.node.UCXLAddress,
			HopDistance:    candidate.distance,
			TemporalNode:   candidate.node,
			Path:           candidate.path,
			Relationship:   qs.determineRelationship(candidate, startNode),
			RelevanceScore: relevanceScore,
			MatchReasons:   matchReasons,
			Metadata:       qs.buildMetadata(candidate, query.IncludeMetadata),
		}
	}
	
	return results
}

func (qs *querySystemImpl) calculateRelevance(candidate *hopCandidate, startNode *TemporalNode, query *HopQuery) float64 {
	score := 1.0
	
	// Distance-based relevance (closer = more relevant)
	distanceScore := 1.0 - (float64(candidate.distance-1) / float64(query.MaxHops))
	score *= distanceScore
	
	// Confidence-based relevance
	confidenceScore := candidate.node.Confidence
	score *= confidenceScore
	
	// Recency-based relevance
	age := time.Since(candidate.node.Timestamp)
	recencyScore := math.Max(0.1, 1.0-age.Hours()/(30*24)) // Decay over 30 days
	score *= recencyScore
	
	// Impact-based relevance
	var impactScore float64
	switch candidate.node.ImpactScope {
	case ImpactSystem:
		impactScore = 1.0
	case ImpactProject:
		impactScore = 0.8
	case ImpactModule:
		impactScore = 0.6
	case ImpactLocal:
		impactScore = 0.4
	}
	score *= impactScore
	
	return math.Min(1.0, score)
}

func (qs *querySystemImpl) sortResults(results []*HopResult, sortCriteria *HopSort) {
	sort.Slice(results, func(i, j int) bool {
		var aVal, bVal float64
		
		switch sortCriteria.SortBy {
		case "hops":
			aVal, bVal = float64(results[i].HopDistance), float64(results[j].HopDistance)
		case "time":
			aVal, bVal = float64(results[i].TemporalNode.Timestamp.Unix()), float64(results[j].TemporalNode.Timestamp.Unix())
		case "confidence":
			aVal, bVal = results[i].TemporalNode.Confidence, results[j].TemporalNode.Confidence
		case "influence":
			aVal, bVal = float64(len(results[i].TemporalNode.Influences)), float64(len(results[j].TemporalNode.Influences))
		case "relevance":
			aVal, bVal = results[i].RelevanceScore, results[j].RelevanceScore
		default:
			aVal, bVal = results[i].RelevanceScore, results[j].RelevanceScore
		}
		
		if sortCriteria.SortDirection == "desc" {
			return aVal > bVal
		}
		return aVal < bVal
	})
}

// Helper methods and types

type hopCandidate struct {
	node     *TemporalNode
	distance int
	path     []*DecisionStep
}

func (qs *querySystemImpl) addForwardNeighbors(current *hopCandidate, queue *[]*hopCandidate, maxHops int) {
	if current.distance >= maxHops {
		return
	}
	
	nodeID := current.node.ID
	if influences, exists := qs.graph.influences[nodeID]; exists {
		for _, influencedID := range influences {
			if influencedNode, exists := qs.graph.nodes[influencedID]; exists {
				step := &DecisionStep{
					Address:      current.node.UCXLAddress,
					TemporalNode: current.node,
					HopDistance:  current.distance,
					Relationship: "influences",
				}
				newPath := append(current.path, step)
				
				*queue = append(*queue, &hopCandidate{
					node:     influencedNode,
					distance: current.distance + 1,
					path:     newPath,
				})
			}
		}
	}
}

func (qs *querySystemImpl) addBackwardNeighbors(current *hopCandidate, queue *[]*hopCandidate, maxHops int) {
	if current.distance >= maxHops {
		return
	}
	
	nodeID := current.node.ID
	if influencedBy, exists := qs.graph.influencedBy[nodeID]; exists {
		for _, influencerID := range influencedBy {
			if influencerNode, exists := qs.graph.nodes[influencerID]; exists {
				step := &DecisionStep{
					Address:      current.node.UCXLAddress,
					TemporalNode: current.node,
					HopDistance:  current.distance,
					Relationship: "influenced_by",
				}
				newPath := append(current.path, step)
				
				*queue = append(*queue, &hopCandidate{
					node:     influencerNode,
					distance: current.distance + 1,
					path:     newPath,
				})
			}
		}
	}
}

func (qs *querySystemImpl) isMajorDecision(node *TemporalNode) bool {
	return node.ChangeReason == ReasonArchitectureChange ||
		   node.ChangeReason == ReasonDesignDecision ||
		   node.ChangeReason == ReasonRequirementsChange ||
		   node.ImpactScope == ImpactSystem ||
		   node.ImpactScope == ImpactProject
}

func (qs *querySystemImpl) getMatchReasons(candidate *hopCandidate, filter *HopFilter) []string {
	reasons := make([]string, 0)
	
	if filter == nil {
		reasons = append(reasons, "no_filters_applied")
		return reasons
	}
	
	node := candidate.node
	
	if len(filter.ChangeReasons) > 0 {
		for _, reason := range filter.ChangeReasons {
			if node.ChangeReason == reason {
				reasons = append(reasons, fmt.Sprintf("change_reason: %s", reason))
			}
		}
	}
	
	if len(filter.ImpactScopes) > 0 {
		for _, scope := range filter.ImpactScopes {
			if node.ImpactScope == scope {
				reasons = append(reasons, fmt.Sprintf("impact_scope: %s", scope))
			}
		}
	}
	
	if filter.MinConfidence > 0 && node.Confidence >= filter.MinConfidence {
		reasons = append(reasons, fmt.Sprintf("confidence: %.2f >= %.2f", node.Confidence, filter.MinConfidence))
	}
	
	if filter.MinInfluenceCount > 0 && len(node.Influences) >= filter.MinInfluenceCount {
		reasons = append(reasons, fmt.Sprintf("influence_count: %d >= %d", len(node.Influences), filter.MinInfluenceCount))
	}
	
	return reasons
}

func (qs *querySystemImpl) determineRelationship(candidate *hopCandidate, startNode *TemporalNode) string {
	if len(candidate.path) == 0 {
		return "self"
	}
	
	// Look at the last step in the path
	lastStep := candidate.path[len(candidate.path)-1]
	return lastStep.Relationship
}

func (qs *querySystemImpl) buildMetadata(candidate *hopCandidate, includeDetailed bool) map[string]interface{} {
	metadata := make(map[string]interface{})
	
	metadata["hop_distance"] = candidate.distance
	metadata["path_length"] = len(candidate.path)
	metadata["node_id"] = candidate.node.ID
	metadata["decision_id"] = candidate.node.DecisionID
	
	if includeDetailed {
		metadata["timestamp"] = candidate.node.Timestamp
		metadata["change_reason"] = candidate.node.ChangeReason
		metadata["impact_scope"] = candidate.node.ImpactScope
		metadata["confidence"] = candidate.node.Confidence
		metadata["staleness"] = candidate.node.Staleness
		metadata["influence_count"] = len(candidate.node.Influences)
		metadata["influenced_by_count"] = len(candidate.node.InfluencedBy)
		
		if candidate.node.Context != nil {
			metadata["context_summary"] = candidate.node.Context.Summary
			metadata["technologies"] = candidate.node.Context.Technologies
			metadata["tags"] = candidate.node.Context.Tags
		}
		
		if decision, exists := qs.graph.decisions[candidate.node.DecisionID]; exists {
			metadata["decision_maker"] = decision.Maker
			metadata["decision_rationale"] = decision.Rationale
		}
	}
	
	return metadata
}

// Query validation and caching

func (qs *querySystemImpl) validateQuery(query *HopQuery) error {
	if err := query.StartAddress.Validate(); err != nil {
		return fmt.Errorf("invalid start address: %w", err)
	}
	
	if query.MaxHops < 1 || query.MaxHops > 20 {
		return fmt.Errorf("max hops must be between 1 and 20")
	}
	
	if query.Direction != "" && query.Direction != "forward" && query.Direction != "backward" && query.Direction != "both" {
		return fmt.Errorf("direction must be 'forward', 'backward', or 'both'")
	}
	
	if query.Limit < 0 {
		return fmt.Errorf("limit cannot be negative")
	}
	
	return nil
}

func (qs *querySystemImpl) generateCacheKey(query *HopQuery) string {
	return fmt.Sprintf("hop_query_%s_%d_%s_%v", 
		query.StartAddress.String(), 
		query.MaxHops, 
		query.Direction,
		query.FilterCriteria != nil)
}

func (qs *querySystemImpl) getFromCache(key string) (interface{}, bool) {
	qs.mu.RLock()
	defer qs.mu.RUnlock()
	
	value, exists := qs.queryCache[key]
	return value, exists
}

func (qs *querySystemImpl) setCache(key string, value interface{}) {
	qs.mu.Lock()
	defer qs.mu.Unlock()
	
	// Clean cache if needed
	if time.Since(qs.lastCacheClean) > qs.cacheTimeout {
		qs.queryCache = make(map[string]interface{})
		qs.lastCacheClean = time.Now()
	}
	
	qs.queryCache[key] = value
}

func (qs *querySystemImpl) updateQueryStats(queryType string, duration time.Duration, cacheHit bool) {
	qs.mu.Lock()
	defer qs.mu.Unlock()
	
	stats, exists := qs.queryStats[queryType]
	if !exists {
		stats = &QueryStatistics{QueryType: queryType}
		qs.queryStats[queryType] = stats
	}
	
	stats.TotalQueries++
	stats.LastQuery = time.Now()
	
	// Update average time
	if stats.AverageTime == 0 {
		stats.AverageTime = duration
	} else {
		stats.AverageTime = (stats.AverageTime + duration) / 2
	}
	
	if cacheHit {
		stats.CacheHits++
	} else {
		stats.CacheMisses++
	}
}

// Additional analysis methods would continue...
// This includes genealogy analysis, similarity matching, clustering, etc.
// The implementation is getting quite long, so I'll include key supporting types:

// DecisionGenealogy represents the genealogy of decisions for a context
type DecisionGenealogy struct {
	Address              ucxl.Address             `json:"address"`
	DirectAncestors      []ucxl.Address           `json:"direct_ancestors"`
	AllAncestors         []ucxl.Address           `json:"all_ancestors"`
	DirectDescendants    []ucxl.Address           `json:"direct_descendants"`
	AllDescendants       []ucxl.Address           `json:"all_descendants"`
	InfluentialAncestors []*InfluentialAncestor   `json:"influential_ancestors"`
	GenealogyDepth       int                      `json:"genealogy_depth"`
	BranchingFactor      float64                  `json:"branching_factor"`
	DecisionTimeline     *DecisionTimeline        `json:"decision_timeline"`
	Metrics              *GenealogyMetrics        `json:"metrics"`
	AnalyzedAt           time.Time                `json:"analyzed_at"`
}

// Additional supporting types for genealogy and similarity analysis...
type InfluentialAncestor struct {
	Address       ucxl.Address  `json:"address"`
	InfluenceScore float64      `json:"influence_score"`
	GenerationsBack int         `json:"generations_back"`
	InfluenceType string        `json:"influence_type"`
}

type GenealogyMetrics struct {
	TotalAncestors    int     `json:"total_ancestors"`
	TotalDescendants  int     `json:"total_descendants"`
	MaxDepth          int     `json:"max_depth"`
	AverageBranching  float64 `json:"average_branching"`
	InfluenceSpread   float64 `json:"influence_spread"`
}

type SimilarDecisionMatch struct {
	Address           ucxl.Address   `json:"address"`
	TemporalNode      *TemporalNode  `json:"temporal_node"`
	SimilarityScore   float64        `json:"similarity_score"`
	SimilarityReasons []string       `json:"similarity_reasons"`
	PatternType       string         `json:"pattern_type"`
	Confidence        float64        `json:"confidence"`
}

// Placeholder implementations for the analysis methods
func (qs *querySystemImpl) analyzeAncestry(history []*TemporalNode) *ancestryAnalysis {
	// Implementation would trace back through parent nodes
	return &ancestryAnalysis{}
}

func (qs *querySystemImpl) analyzeDescendants(address ucxl.Address, maxDepth int) *descendantAnalysis {
	// Implementation would trace forward through influenced nodes
	return &descendantAnalysis{}
}

func (qs *querySystemImpl) findInfluentialAncestors(history []*TemporalNode) []*InfluentialAncestor {
	// Implementation would identify most influential historical decisions
	return make([]*InfluentialAncestor, 0)
}

func (qs *querySystemImpl) calculateGenealogyMetrics(history []*TemporalNode, descendants *descendantAnalysis) *GenealogyMetrics {
	// Implementation would calculate genealogy statistics
	return &GenealogyMetrics{}
}

func (qs *querySystemImpl) calculateDecisionSimilarity(node1, node2 *TemporalNode) float64 {
	// Implementation would compare decision patterns, technologies, etc.
	return 0.0
}

func (qs *querySystemImpl) getSimilarityReasons(node1, node2 *TemporalNode) []string {
	// Implementation would identify why decisions are similar
	return make([]string, 0)
}

func (qs *querySystemImpl) identifyPatternType(node1, node2 *TemporalNode) string {
	// Implementation would classify the type of similarity pattern
	return "unknown"
}

func (qs *querySystemImpl) convertCommunityToCluster(community Community) *DecisionCluster {
	// Implementation would convert community to decision cluster
	return &DecisionCluster{
		ID: community.ID,
		Decisions: community.Nodes,
		ClusterSize: len(community.Nodes),
		Cohesion: community.Modularity,
	}
}

// Supporting analysis types
type ancestryAnalysis struct {
	DirectAncestors []ucxl.Address
	AllAncestors    []ucxl.Address
	MaxDepth        int
}

type descendantAnalysis struct {
	DirectDescendants []ucxl.Address
	AllDescendants    []ucxl.Address
	BranchingFactor   float64
}