package temporal

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

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

// stalenessDetectorImpl implements the StalenessDetector interface
type stalenessDetectorImpl struct {
	mu sync.RWMutex
	
	// Reference to the temporal graph
	graph *temporalGraphImpl
	
	// Configuration
	weights            *StalenessWeights
	defaultThreshold   float64
	analysisWindow     time.Duration
	
	// Cached results
	lastDetectionRun   time.Time
	cachedStaleContexts []*StaleContext
	cachedStatistics   *StalenessStatistics
	cacheValidDuration time.Duration
	
	// Detection settings
	enableTimeBasedStaleness      bool
	enableInfluenceBasedStaleness bool
	enableActivityBasedStaleness  bool
	enableImportanceBasedStaleness bool
	enableComplexityBasedStaleness bool
	enableDependencyBasedStaleness bool
}

// NewStalenessDetector creates a new staleness detector
func NewStalenessDetector(graph *temporalGraphImpl) StalenessDetector {
	return &stalenessDetectorImpl{
		graph:            graph,
		weights:          graph.stalenessWeight,
		defaultThreshold: 0.6,
		analysisWindow:   30 * 24 * time.Hour, // 30 days
		cacheValidDuration: time.Minute * 15,
		
		// Enable all detection methods by default
		enableTimeBasedStaleness:       true,
		enableInfluenceBasedStaleness:  true,
		enableActivityBasedStaleness:   true,
		enableImportanceBasedStaleness: true,
		enableComplexityBasedStaleness: true,
		enableDependencyBasedStaleness: true,
	}
}

// CalculateStaleness calculates staleness score based on decision relationships
func (sd *stalenessDetectorImpl) CalculateStaleness(ctx context.Context, address ucxl.Address) (float64, error) {
	sd.mu.RLock()
	defer sd.mu.RUnlock()
	
	sd.graph.mu.RLock()
	defer sd.graph.mu.RUnlock()
	
	node, err := sd.graph.getLatestNodeUnsafe(address)
	if err != nil {
		return 0, fmt.Errorf("node not found: %w", err)
	}
	
	return sd.calculateNodeStaleness(node), nil
}

// DetectStaleContexts detects all stale contexts above threshold
func (sd *stalenessDetectorImpl) DetectStaleContexts(ctx context.Context, threshold float64) ([]*StaleContext, error) {
	sd.mu.Lock()
	defer sd.mu.Unlock()
	
	// Check cache validity
	if sd.cachedStaleContexts != nil && time.Since(sd.lastDetectionRun) < sd.cacheValidDuration {
		// Filter cached results by threshold
		filtered := make([]*StaleContext, 0)
		for _, stale := range sd.cachedStaleContexts {
			if stale.StalenessScore >= threshold {
				filtered = append(filtered, stale)
			}
		}
		return filtered, nil
	}
	
	sd.graph.mu.RLock()
	defer sd.graph.mu.RUnlock()
	
	staleContexts := make([]*StaleContext, 0)
	detectionStart := time.Now()
	
	// Analyze all nodes for staleness
	for _, node := range sd.graph.nodes {
		stalenessScore := sd.calculateNodeStaleness(node)
		
		if stalenessScore >= threshold {
			staleContext := &StaleContext{
				UCXLAddress:      node.UCXLAddress,
				TemporalNode:     node,
				StalenessScore:   stalenessScore,
				LastUpdated:      node.Timestamp,
				Reasons:          sd.analyzeStalenessReasons(node, stalenessScore),
				SuggestedActions: sd.generateRefreshActions(node),
				RelatedChanges:   sd.findRelatedChanges(node),
				Priority:         sd.calculatePriority(stalenessScore, node),
			}
			staleContexts = append(staleContexts, staleContext)
		}
	}
	
	// Sort by staleness score (highest first)
	sort.Slice(staleContexts, func(i, j int) bool {
		return staleContexts[i].StalenessScore > staleContexts[j].StalenessScore
	})
	
	// Update cache
	sd.cachedStaleContexts = staleContexts
	sd.lastDetectionRun = time.Now()
	
	// Update statistics
	sd.updateStatistics(len(sd.graph.nodes), len(staleContexts), time.Since(detectionStart))
	
	return staleContexts, nil
}

// GetStalenessReasons gets reasons why context is considered stale
func (sd *stalenessDetectorImpl) GetStalenessReasons(ctx context.Context, address ucxl.Address) ([]string, error) {
	sd.mu.RLock()
	defer sd.mu.RUnlock()
	
	sd.graph.mu.RLock()
	defer sd.graph.mu.RUnlock()
	
	node, err := sd.graph.getLatestNodeUnsafe(address)
	if err != nil {
		return nil, fmt.Errorf("node not found: %w", err)
	}
	
	stalenessScore := sd.calculateNodeStaleness(node)
	return sd.analyzeStalenessReasons(node, stalenessScore), nil
}

// SuggestRefreshActions suggests actions to refresh stale context
func (sd *stalenessDetectorImpl) SuggestRefreshActions(ctx context.Context, address ucxl.Address) ([]*RefreshAction, error) {
	sd.mu.RLock()
	defer sd.mu.RUnlock()
	
	sd.graph.mu.RLock()
	defer sd.graph.mu.RUnlock()
	
	node, err := sd.graph.getLatestNodeUnsafe(address)
	if err != nil {
		return nil, fmt.Errorf("node not found: %w", err)
	}
	
	actions := sd.generateRefreshActions(node)
	
	// Convert to RefreshAction structs
	refreshActions := make([]*RefreshAction, len(actions))
	for i, action := range actions {
		refreshActions[i] = &RefreshAction{
			Type:            sd.categorizeAction(action),
			Description:     action,
			Priority:        sd.calculateActionPriority(action, node),
			EstimatedEffort: sd.estimateEffort(action),
			RequiredRoles:   sd.getRequiredRoles(action),
			Dependencies:    sd.getActionDependencies(action),
			Metadata:        make(map[string]interface{}),
		}
	}
	
	// Sort by priority
	sort.Slice(refreshActions, func(i, j int) bool {
		return refreshActions[i].Priority > refreshActions[j].Priority
	})
	
	return refreshActions, nil
}

// UpdateStalenessWeights updates weights used in staleness calculation
func (sd *stalenessDetectorImpl) UpdateStalenessWeights(weights *StalenessWeights) error {
	sd.mu.Lock()
	defer sd.mu.Unlock()
	
	// Validate weights
	if err := sd.validateWeights(weights); err != nil {
		return fmt.Errorf("invalid weights: %w", err)
	}
	
	sd.weights = weights
	sd.graph.stalenessWeight = weights
	
	// Clear cache to force recalculation
	sd.cachedStaleContexts = nil
	sd.cachedStatistics = nil
	
	return nil
}

// GetStalenessStats returns staleness detection statistics
func (sd *stalenessDetectorImpl) GetStalenessStats() (*StalenessStatistics, error) {
	sd.mu.RLock()
	defer sd.mu.RUnlock()
	
	if sd.cachedStatistics != nil {
		return sd.cachedStatistics, nil
	}
	
	// Generate fresh statistics
	sd.graph.mu.RLock()
	defer sd.graph.mu.RUnlock()
	
	totalContexts := int64(len(sd.graph.nodes))
	staleCount := int64(0)
	totalStaleness := 0.0
	maxStaleness := 0.0
	
	for _, node := range sd.graph.nodes {
		staleness := sd.calculateNodeStaleness(node)
		totalStaleness += staleness
		
		if staleness > maxStaleness {
			maxStaleness = staleness
		}
		
		if staleness >= sd.defaultThreshold {
			staleCount++
		}
	}
	
	avgStaleness := 0.0
	if totalContexts > 0 {
		avgStaleness = totalStaleness / float64(totalContexts)
	}
	
	stalenessRate := 0.0
	if totalContexts > 0 {
		stalenessRate = float64(staleCount) / float64(totalContexts) * 100.0
	}
	
	stats := &StalenessStatistics{
		TotalContexts:          totalContexts,
		StaleContexts:          staleCount,
		StalenessRate:          stalenessRate,
		AverageStaleness:       avgStaleness,
		MaxStaleness:           maxStaleness,
		LastDetectionRun:       sd.lastDetectionRun,
		DetectionDuration:      0, // Will be updated during actual detection
		RefreshRecommendations: staleCount, // One recommendation per stale context
	}
	
	sd.cachedStatistics = stats
	
	return stats, nil
}

// Core staleness calculation logic

func (sd *stalenessDetectorImpl) calculateNodeStaleness(node *TemporalNode) float64 {
	staleness := 0.0
	
	// Time-based staleness
	if sd.enableTimeBasedStaleness {
		timeStaleness := sd.calculateTimeStaleness(node)
		staleness += timeStaleness * sd.weights.TimeWeight
	}
	
	// Influence-based staleness
	if sd.enableInfluenceBasedStaleness {
		influenceStaleness := sd.calculateInfluenceStaleness(node)
		staleness += influenceStaleness * sd.weights.InfluenceWeight
	}
	
	// Activity-based staleness
	if sd.enableActivityBasedStaleness {
		activityStaleness := sd.calculateActivityStaleness(node)
		staleness += activityStaleness * sd.weights.ActivityWeight
	}
	
	// Importance-based staleness
	if sd.enableImportanceBasedStaleness {
		importanceStaleness := sd.calculateImportanceStaleness(node)
		staleness += importanceStaleness * sd.weights.ImportanceWeight
	}
	
	// Complexity-based staleness
	if sd.enableComplexityBasedStaleness {
		complexityStaleness := sd.calculateComplexityStaleness(node)
		staleness += complexityStaleness * sd.weights.ComplexityWeight
	}
	
	// Dependency-based staleness
	if sd.enableDependencyBasedStaleness {
		dependencyStaleness := sd.calculateDependencyStaleness(node)
		staleness += dependencyStaleness * sd.weights.DependencyWeight
	}
	
	// Ensure staleness is between 0 and 1
	return math.Max(0, math.Min(1.0, staleness))
}

func (sd *stalenessDetectorImpl) calculateTimeStaleness(node *TemporalNode) float64 {
	timeSinceUpdate := time.Since(node.Timestamp)
	
	// Define staleness curve: contexts become stale over time
	// Fresh (0-7 days): 0-0.2 staleness
	// Moderate (7-30 days): 0.2-0.6 staleness
	// Stale (30-90 days): 0.6-0.9 staleness
	// Very stale (90+ days): 0.9-1.0 staleness
	
	days := timeSinceUpdate.Hours() / 24.0
	
	if days <= 7 {
		return days / 35.0 // 0-0.2 over 7 days
	} else if days <= 30 {
		return 0.2 + ((days-7)/23.0)*0.4 // 0.2-0.6 over 23 days
	} else if days <= 90 {
		return 0.6 + ((days-30)/60.0)*0.3 // 0.6-0.9 over 60 days
	} else {
		return 0.9 + math.Min(0.1, (days-90)/365.0*0.1) // 0.9-1.0 over 365 days
	}
}

func (sd *stalenessDetectorImpl) calculateInfluenceStaleness(node *TemporalNode) float64 {
	// Context becomes stale if its influencers have changed significantly
	staleness := 0.0
	
	// Check if influencers have changed recently
	cutoff := time.Now().Add(-sd.analysisWindow)
	recentChanges := 0
	totalInfluencers := len(node.InfluencedBy)
	
	if totalInfluencers == 0 {
		return 0.0 // No influencers means no influence-based staleness
	}
	
	for _, influencerAddr := range node.InfluencedBy {
		if influencerNode := sd.findLatestNodeByAddress(influencerAddr); influencerNode != nil {
			if influencerNode.Timestamp.After(cutoff) {
				recentChanges++
			}
		}
	}
	
	// Higher staleness if many influencers have changed
	if totalInfluencers > 0 {
		staleness = float64(recentChanges) / float64(totalInfluencers)
	}
	
	// Boost staleness if this node hasn't been updated despite influencer changes
	if recentChanges > 0 && node.Timestamp.Before(cutoff) {
		staleness *= 1.5 // Amplify staleness
	}
	
	return math.Min(1.0, staleness)
}

func (sd *stalenessDetectorImpl) calculateActivityStaleness(node *TemporalNode) float64 {
	// Context becomes stale if there's been a lot of related activity
	activityScore := 0.0
	cutoff := time.Now().Add(-7 * 24 * time.Hour) // Look at last week
	
	// Count recent decisions in the influence network
	recentDecisions := 0
	totalConnections := len(node.Influences) + len(node.InfluencedBy)
	
	if totalConnections == 0 {
		return 0.0
	}
	
	// Check influences
	for _, influencedAddr := range node.Influences {
		if influencedNode := sd.findLatestNodeByAddress(influencedAddr); influencedNode != nil {
			if influencedNode.Timestamp.After(cutoff) {
				recentDecisions++
			}
		}
	}
	
	// Check influencers
	for _, influencerAddr := range node.InfluencedBy {
		if influencerNode := sd.findLatestNodeByAddress(influencerAddr); influencerNode != nil {
			if influencerNode.Timestamp.After(cutoff) {
				recentDecisions++
			}
		}
	}
	
	// High activity in network while this node is unchanged suggests staleness
	activityScore = float64(recentDecisions) / float64(totalConnections)
	
	// Amplify if this node is particularly old relative to the activity
	nodeAge := time.Since(node.Timestamp).Hours() / 24.0
	if nodeAge > 7 && activityScore > 0.3 {
		activityScore *= 1.3
	}
	
	return math.Min(1.0, activityScore)
}

func (sd *stalenessDetectorImpl) calculateImportanceStaleness(node *TemporalNode) float64 {
	// Important contexts (high influence, broad scope) become stale faster
	importanceMultiplier := 1.0
	
	// Factor in impact scope
	switch node.ImpactScope {
	case ImpactSystem:
		importanceMultiplier *= 1.4
	case ImpactProject:
		importanceMultiplier *= 1.2
	case ImpactModule:
		importanceMultiplier *= 1.1
	case ImpactLocal:
		importanceMultiplier *= 1.0
	}
	
	// Factor in influence count
	influenceCount := len(node.Influences)
	if influenceCount > 5 {
		importanceMultiplier *= 1.3
	} else if influenceCount > 2 {
		importanceMultiplier *= 1.1
	}
	
	// Factor in confidence (low confidence = higher staleness importance)
	if node.Confidence < 0.6 {
		importanceMultiplier *= 1.2
	}
	
	// Base staleness from time, amplified by importance
	timeStaleness := sd.calculateTimeStaleness(node)
	
	return math.Min(1.0, timeStaleness * importanceMultiplier)
}

func (sd *stalenessDetectorImpl) calculateComplexityStaleness(node *TemporalNode) float64 {
	// Complex contexts (many technologies, long descriptions) become stale faster
	complexityScore := 0.0
	
	if node.Context != nil {
		// Factor in technology count
		techCount := len(node.Context.Technologies)
		complexityScore += math.Min(0.3, float64(techCount)/10.0)
		
		// Factor in insight count
		insightCount := len(node.Context.Insights)
		complexityScore += math.Min(0.2, float64(insightCount)/5.0)
		
		// Factor in summary length (longer = more complex)
		summaryLength := len(node.Context.Summary)
		complexityScore += math.Min(0.2, float64(summaryLength)/500.0)
		
		// Factor in purpose length
		purposeLength := len(node.Context.Purpose)
		complexityScore += math.Min(0.2, float64(purposeLength)/300.0)
		
		// Factor in tag count
		tagCount := len(node.Context.Tags)
		complexityScore += math.Min(0.1, float64(tagCount)/5.0)
	}
	
	// Complex contexts need more frequent updates
	timeFactor := sd.calculateTimeStaleness(node)
	
	return math.Min(1.0, complexityScore * timeFactor * 1.5)
}

func (sd *stalenessDetectorImpl) calculateDependencyStaleness(node *TemporalNode) float64 {
	// Context becomes stale if its dependencies have changed
	staleness := 0.0
	
	// Check if any dependencies (influencers) have evolved significantly
	if len(node.InfluencedBy) == 0 {
		return 0.0
	}
	
	significantChanges := 0
	for _, depAddr := range node.InfluencedBy {
		if depNode := sd.findLatestNodeByAddress(depAddr); depNode != nil {
			// Check if dependency has had major changes
			if sd.hasSignificantChange(depNode, node.Timestamp) {
				significantChanges++
			}
		}
	}
	
	staleness = float64(significantChanges) / float64(len(node.InfluencedBy))
	
	// Amplify if the changes are architectural or requirements-related
	for _, depAddr := range node.InfluencedBy {
		if depNode := sd.findLatestNodeByAddress(depAddr); depNode != nil {
			if depNode.ChangeReason == ReasonArchitectureChange || 
			   depNode.ChangeReason == ReasonRequirementsChange {
				staleness *= 1.3
				break
			}
		}
	}
	
	return math.Min(1.0, staleness)
}

// Helper methods for staleness analysis

func (sd *stalenessDetectorImpl) analyzeStalenessReasons(node *TemporalNode, stalenessScore float64) []string {
	reasons := make([]string, 0)
	
	// Time-based reasons
	timeSinceUpdate := time.Since(node.Timestamp)
	if timeSinceUpdate > 30*24*time.Hour {
		reasons = append(reasons, fmt.Sprintf("not updated in %d days", int(timeSinceUpdate.Hours()/24)))
	} else if timeSinceUpdate > 7*24*time.Hour {
		reasons = append(reasons, fmt.Sprintf("not updated in %d days", int(timeSinceUpdate.Hours()/24)))
	}
	
	// Influence-based reasons
	recentInfluencerChanges := sd.countRecentInfluencerChanges(node)
	if recentInfluencerChanges > 0 {
		reasons = append(reasons, fmt.Sprintf("%d influencing contexts have changed recently", recentInfluencerChanges))
	}
	
	// Activity-based reasons
	networkActivity := sd.calculateNetworkActivity(node)
	if networkActivity > 0.5 {
		reasons = append(reasons, "high activity in related contexts")
	}
	
	// Confidence-based reasons
	if node.Confidence < 0.6 {
		reasons = append(reasons, fmt.Sprintf("low confidence score (%.2f)", node.Confidence))
	}
	
	// Dependency-based reasons
	dependencyChanges := sd.countDependencyChanges(node)
	if dependencyChanges > 0 {
		reasons = append(reasons, fmt.Sprintf("%d dependencies have changed", dependencyChanges))
	}
	
	// Scope-based reasons
	if node.ImpactScope == ImpactSystem || node.ImpactScope == ImpactProject {
		reasons = append(reasons, "high impact scope requires frequent updates")
	}
	
	return reasons
}

func (sd *stalenessDetectorImpl) generateRefreshActions(node *TemporalNode) []string {
	actions := make([]string, 0)
	
	// Always suggest basic review
	actions = append(actions, "review context accuracy and completeness")
	
	// Time-based actions
	if time.Since(node.Timestamp) > 7*24*time.Hour {
		actions = append(actions, "update context with recent changes")
	}
	
	// Influence-based actions
	if sd.countRecentInfluencerChanges(node) > 0 {
		actions = append(actions, "review influencing contexts for impact")
		actions = append(actions, "validate dependencies are still accurate")
	}
	
	// Confidence-based actions
	if node.Confidence < 0.7 {
		actions = append(actions, "improve context confidence through additional analysis")
		actions = append(actions, "validate context information with subject matter experts")
	}
	
	// Technology-based actions
	if node.Context != nil && len(node.Context.Technologies) > 5 {
		actions = append(actions, "review technology stack for changes")
		actions = append(actions, "update technology versions and compatibility")
	}
	
	// Impact-based actions
	if node.ImpactScope == ImpactSystem || node.ImpactScope == ImpactProject {
		actions = append(actions, "conduct architectural review")
		actions = append(actions, "validate system-wide impact assumptions")
	}
	
	// Network-based actions
	if len(node.Influences) > 3 {
		actions = append(actions, "review all influenced contexts for consistency")
	}
	
	return actions
}

func (sd *stalenessDetectorImpl) findRelatedChanges(node *TemporalNode) []ucxl.Address {
	relatedChanges := make([]ucxl.Address, 0)
	cutoff := time.Now().Add(-7 * 24 * time.Hour)
	
	// Find recent changes in the influence network
	for _, addr := range node.Influences {
		if relatedNode := sd.findLatestNodeByAddress(addr); relatedNode != nil {
			if relatedNode.Timestamp.After(cutoff) {
				relatedChanges = append(relatedChanges, addr)
			}
		}
	}
	
	for _, addr := range node.InfluencedBy {
		if relatedNode := sd.findLatestNodeByAddress(addr); relatedNode != nil {
			if relatedNode.Timestamp.After(cutoff) {
				relatedChanges = append(relatedChanges, addr)
			}
		}
	}
	
	return relatedChanges
}

func (sd *stalenessDetectorImpl) calculatePriority(stalenessScore float64, node *TemporalNode) StalePriority {
	// Start with staleness score
	priority := stalenessScore
	
	// Adjust based on impact scope
	switch node.ImpactScope {
	case ImpactSystem:
		priority += 0.3
	case ImpactProject:
		priority += 0.2
	case ImpactModule:
		priority += 0.1
	}
	
	// Adjust based on influence count
	influenceCount := len(node.Influences)
	if influenceCount > 5 {
		priority += 0.2
	} else if influenceCount > 2 {
		priority += 0.1
	}
	
	// Adjust based on age
	age := time.Since(node.Timestamp)
	if age > 90*24*time.Hour {
		priority += 0.1
	}
	
	// Convert to priority level
	if priority >= 0.9 {
		return PriorityCritical
	} else if priority >= 0.7 {
		return PriorityHigh
	} else if priority >= 0.5 {
		return PriorityMedium
	}
	return PriorityLow
}

// Additional helper methods

func (sd *stalenessDetectorImpl) findLatestNodeByAddress(address ucxl.Address) *TemporalNode {
	addressKey := address.String()
	if nodes, exists := sd.graph.addressToNodes[addressKey]; exists && len(nodes) > 0 {
		return nodes[len(nodes)-1]
	}
	return nil
}

func (sd *stalenessDetectorImpl) hasSignificantChange(node *TemporalNode, since time.Time) bool {
	if node.Timestamp.Before(since) {
		return false
	}
	
	// Consider architectural and requirements changes as significant
	return node.ChangeReason == ReasonArchitectureChange ||
		   node.ChangeReason == ReasonRequirementsChange ||
		   node.ChangeReason == ReasonDesignDecision
}

func (sd *stalenessDetectorImpl) countRecentInfluencerChanges(node *TemporalNode) int {
	cutoff := time.Now().Add(-7 * 24 * time.Hour)
	changes := 0
	
	for _, addr := range node.InfluencedBy {
		if influencerNode := sd.findLatestNodeByAddress(addr); influencerNode != nil {
			if influencerNode.Timestamp.After(cutoff) {
				changes++
			}
		}
	}
	
	return changes
}

func (sd *stalenessDetectorImpl) calculateNetworkActivity(node *TemporalNode) float64 {
	cutoff := time.Now().Add(-7 * 24 * time.Hour)
	recentChanges := 0
	totalConnections := len(node.Influences) + len(node.InfluencedBy)
	
	if totalConnections == 0 {
		return 0
	}
	
	for _, addr := range node.Influences {
		if relatedNode := sd.findLatestNodeByAddress(addr); relatedNode != nil {
			if relatedNode.Timestamp.After(cutoff) {
				recentChanges++
			}
		}
	}
	
	for _, addr := range node.InfluencedBy {
		if relatedNode := sd.findLatestNodeByAddress(addr); relatedNode != nil {
			if relatedNode.Timestamp.After(cutoff) {
				recentChanges++
			}
		}
	}
	
	return float64(recentChanges) / float64(totalConnections)
}

func (sd *stalenessDetectorImpl) countDependencyChanges(node *TemporalNode) int {
	changes := 0
	
	for _, addr := range node.InfluencedBy {
		if depNode := sd.findLatestNodeByAddress(addr); depNode != nil {
			if sd.hasSignificantChange(depNode, node.Timestamp) {
				changes++
			}
		}
	}
	
	return changes
}

func (sd *stalenessDetectorImpl) validateWeights(weights *StalenessWeights) error {
	if weights.TimeWeight < 0 || weights.TimeWeight > 1 {
		return fmt.Errorf("TimeWeight must be between 0 and 1")
	}
	if weights.InfluenceWeight < 0 || weights.InfluenceWeight > 1 {
		return fmt.Errorf("InfluenceWeight must be between 0 and 1")
	}
	if weights.ActivityWeight < 0 || weights.ActivityWeight > 1 {
		return fmt.Errorf("ActivityWeight must be between 0 and 1")
	}
	if weights.ImportanceWeight < 0 || weights.ImportanceWeight > 1 {
		return fmt.Errorf("ImportanceWeight must be between 0 and 1")
	}
	if weights.ComplexityWeight < 0 || weights.ComplexityWeight > 1 {
		return fmt.Errorf("ComplexityWeight must be between 0 and 1")
	}
	if weights.DependencyWeight < 0 || weights.DependencyWeight > 1 {
		return fmt.Errorf("DependencyWeight must be between 0 and 1")
	}
	
	// Note: We don't require weights to sum to 1.0 as they may be used in different combinations
	
	return nil
}

func (sd *stalenessDetectorImpl) updateStatistics(totalContexts, staleContexts int, duration time.Duration) {
	avgStaleness := 0.0
	maxStaleness := 0.0
	
	if totalContexts > 0 {
		totalStaleness := 0.0
		for _, node := range sd.graph.nodes {
			staleness := sd.calculateNodeStaleness(node)
			totalStaleness += staleness
			if staleness > maxStaleness {
				maxStaleness = staleness
			}
		}
		avgStaleness = totalStaleness / float64(totalContexts)
	}
	
	stalenessRate := 0.0
	if totalContexts > 0 {
		stalenessRate = float64(staleContexts) / float64(totalContexts) * 100.0
	}
	
	sd.cachedStatistics = &StalenessStatistics{
		TotalContexts:          int64(totalContexts),
		StaleContexts:          int64(staleContexts),
		StalenessRate:          stalenessRate,
		AverageStaleness:       avgStaleness,
		MaxStaleness:           maxStaleness,
		LastDetectionRun:       time.Now(),
		DetectionDuration:      duration,
		RefreshRecommendations: int64(staleContexts),
	}
}

// Action categorization and estimation methods

func (sd *stalenessDetectorImpl) categorizeAction(action string) string {
	switch {
	case contains(action, "review"):
		return "review"
	case contains(action, "update"):
		return "update"
	case contains(action, "validate"):
		return "validation"
	case contains(action, "improve"):
		return "improvement"
	case contains(action, "technology"):
		return "technical"
	case contains(action, "architectural"):
		return "architectural"
	default:
		return "general"
	}
}

func (sd *stalenessDetectorImpl) calculateActionPriority(action string, node *TemporalNode) int {
	priority := 5 // Base priority
	
	// Increase priority for system/project scope
	if node.ImpactScope == ImpactSystem {
		priority += 3
	} else if node.ImpactScope == ImpactProject {
		priority += 2
	}
	
	// Increase priority for high-influence nodes
	if len(node.Influences) > 5 {
		priority += 2
	}
	
	// Increase priority for architectural actions
	if contains(action, "architectural") {
		priority += 2
	}
	
	// Increase priority for validation actions
	if contains(action, "validate") {
		priority += 1
	}
	
	return priority
}

func (sd *stalenessDetectorImpl) estimateEffort(action string) string {
	switch {
	case contains(action, "review context accuracy"):
		return "medium"
	case contains(action, "architectural review"):
		return "high"
	case contains(action, "validate dependencies"):
		return "medium"
	case contains(action, "update context"):
		return "low"
	case contains(action, "improve confidence"):
		return "high"
	case contains(action, "technology"):
		return "medium"
	default:
		return "medium"
	}
}

func (sd *stalenessDetectorImpl) getRequiredRoles(action string) []string {
	switch {
	case contains(action, "architectural"):
		return []string{"architect", "technical_lead"}
	case contains(action, "technology"):
		return []string{"developer", "technical_lead"}
	case contains(action, "validate"):
		return []string{"analyst", "subject_matter_expert"}
	case contains(action, "review"):
		return []string{"analyst", "developer"}
	default:
		return []string{"analyst"}
	}
}

func (sd *stalenessDetectorImpl) getActionDependencies(action string) []string {
	dependencies := make([]string, 0)
	
	if contains(action, "architectural") {
		dependencies = append(dependencies, "stakeholder_availability", "documentation_access")
	}
	
	if contains(action, "validate dependencies") {
		dependencies = append(dependencies, "dependency_analysis", "influence_mapping")
	}
	
	if contains(action, "improve confidence") {
		dependencies = append(dependencies, "expert_review", "additional_analysis")
	}
	
	return dependencies
}