package temporal

import (
	"context"
	"crypto/sha256"
	"fmt"
	"math"
	"sort"
	"sync"
	"time"

	"chorus.services/bzzz/pkg/ucxl"
	slurpContext "chorus.services/bzzz/pkg/slurp/context"
	"chorus.services/bzzz/pkg/slurp/storage"
)

// temporalGraphImpl implements the TemporalGraph interface
type temporalGraphImpl struct {
	mu sync.RWMutex
	
	// Core storage
	storage storage.ContextStore
	
	// In-memory graph structures for fast access
	nodes           map[string]*TemporalNode           // nodeID -> TemporalNode
	addressToNodes  map[string][]*TemporalNode        // address -> list of temporal nodes
	influences      map[string][]string               // nodeID -> list of influenced nodeIDs
	influencedBy    map[string][]string               // nodeID -> list of influencer nodeIDs
	
	// Decision tracking
	decisions       map[string]*DecisionMetadata      // decisionID -> DecisionMetadata
	decisionToNodes map[string][]*TemporalNode        // decisionID -> list of affected nodes
	
	// Performance optimization
	pathCache       map[string][]*DecisionStep        // cache for decision paths
	metricsCache    map[string]interface{}            // cache for expensive metrics
	cacheTimeout    time.Duration
	lastCacheClean  time.Time
	
	// Configuration
	maxDepth        int           // Maximum depth for path finding
	stalenessWeight *StalenessWeights
}

// NewTemporalGraph creates a new temporal graph implementation
func NewTemporalGraph(storage storage.ContextStore) TemporalGraph {
	return &temporalGraphImpl{
		storage:         storage,
		nodes:           make(map[string]*TemporalNode),
		addressToNodes:  make(map[string][]*TemporalNode),
		influences:      make(map[string][]string),
		influencedBy:    make(map[string][]string),
		decisions:       make(map[string]*DecisionMetadata),
		decisionToNodes: make(map[string][]*TemporalNode),
		pathCache:       make(map[string][]*DecisionStep),
		metricsCache:    make(map[string]interface{}),
		cacheTimeout:    time.Minute * 15,
		lastCacheClean:  time.Now(),
		maxDepth:        100, // Default maximum depth
		stalenessWeight: &StalenessWeights{
			TimeWeight:       0.3,
			InfluenceWeight:  0.4,
			ActivityWeight:   0.2,
			ImportanceWeight: 0.1,
			ComplexityWeight: 0.1,
			DependencyWeight: 0.3,
		},
	}
}

// CreateInitialContext creates the first temporal version of context
func (tg *temporalGraphImpl) CreateInitialContext(ctx context.Context, address ucxl.Address, 
	contextData *slurpContext.ContextNode, creator string) (*TemporalNode, error) {
	
	tg.mu.Lock()
	defer tg.mu.Unlock()
	
	// Generate node ID
	nodeID := tg.generateNodeID(address, 1)
	
	// Create temporal node
	temporalNode := &TemporalNode{
		ID:           nodeID,
		UCXLAddress:  address,
		Version:      1,
		Context:      contextData,
		Timestamp:    time.Now(),
		DecisionID:   fmt.Sprintf("initial-%s", creator),
		ChangeReason: ReasonInitialCreation,
		ParentNode:   nil,
		ContextHash:  tg.calculateContextHash(contextData),
		Confidence:   contextData.RAGConfidence,
		Staleness:    0.0,
		Influences:   make([]ucxl.Address, 0),
		InfluencedBy: make([]ucxl.Address, 0),
		ValidatedBy:  []string{creator},
		LastValidated: time.Now(),
		ImpactScope:  ImpactLocal,
		PropagatedTo: make([]ucxl.Address, 0),
		Metadata:     make(map[string]interface{}),
	}
	
	// Store in memory structures
	tg.nodes[nodeID] = temporalNode
	addressKey := address.String()
	tg.addressToNodes[addressKey] = []*TemporalNode{temporalNode}
	
	// Initialize influence maps
	tg.influences[nodeID] = make([]string, 0)
	tg.influencedBy[nodeID] = make([]string, 0)
	
	// Store decision metadata
	decisionMeta := &DecisionMetadata{
		ID:               temporalNode.DecisionID,
		Maker:            creator,
		Rationale:        "Initial context creation",
		Scope:            ImpactLocal,
		ConfidenceLevel:  contextData.RAGConfidence,
		ExternalRefs:     make([]string, 0),
		CreatedAt:        time.Now(),
		ImplementationStatus: "complete",
		Metadata:         make(map[string]interface{}),
	}
	tg.decisions[temporalNode.DecisionID] = decisionMeta
	tg.decisionToNodes[temporalNode.DecisionID] = []*TemporalNode{temporalNode}
	
	// Persist to storage
	if err := tg.persistTemporalNode(ctx, temporalNode); err != nil {
		return nil, fmt.Errorf("failed to persist initial temporal node: %w", err)
	}
	
	return temporalNode, nil
}

// EvolveContext creates a new temporal version due to a decision
func (tg *temporalGraphImpl) EvolveContext(ctx context.Context, address ucxl.Address, 
	newContext *slurpContext.ContextNode, reason ChangeReason, 
	decision *DecisionMetadata) (*TemporalNode, error) {
	
	tg.mu.Lock()
	defer tg.mu.Unlock()
	
	// Get latest version
	addressKey := address.String()
	nodes, exists := tg.addressToNodes[addressKey]
	if !exists || len(nodes) == 0 {
		return nil, fmt.Errorf("no existing context found for address %s", address.String())
	}
	
	// Find latest version
	latestNode := nodes[len(nodes)-1]
	newVersion := latestNode.Version + 1
	
	// Generate new node ID
	nodeID := tg.generateNodeID(address, newVersion)
	
	// Create new temporal node
	temporalNode := &TemporalNode{
		ID:           nodeID,
		UCXLAddress:  address,
		Version:      newVersion,
		Context:      newContext,
		Timestamp:    time.Now(),
		DecisionID:   decision.ID,
		ChangeReason: reason,
		ParentNode:   &latestNode.ID,
		ContextHash:  tg.calculateContextHash(newContext),
		Confidence:   newContext.RAGConfidence,
		Staleness:    0.0, // New version, not stale
		Influences:   make([]ucxl.Address, 0),
		InfluencedBy: make([]ucxl.Address, 0),
		ValidatedBy:  []string{decision.Maker},
		LastValidated: time.Now(),
		ImpactScope:  decision.Scope,
		PropagatedTo: make([]ucxl.Address, 0),
		Metadata:     make(map[string]interface{}),
	}
	
	// Copy influence relationships from parent
	if latestNodeInfluences, exists := tg.influences[latestNode.ID]; exists {
		tg.influences[nodeID] = make([]string, len(latestNodeInfluences))
		copy(tg.influences[nodeID], latestNodeInfluences)
	} else {
		tg.influences[nodeID] = make([]string, 0)
	}
	
	if latestNodeInfluencedBy, exists := tg.influencedBy[latestNode.ID]; exists {
		tg.influencedBy[nodeID] = make([]string, len(latestNodeInfluencedBy))
		copy(tg.influencedBy[nodeID], latestNodeInfluencedBy)
	} else {
		tg.influencedBy[nodeID] = make([]string, 0)
	}
	
	// Store in memory structures
	tg.nodes[nodeID] = temporalNode
	tg.addressToNodes[addressKey] = append(tg.addressToNodes[addressKey], temporalNode)
	
	// Store decision metadata
	tg.decisions[decision.ID] = decision
	if existing, exists := tg.decisionToNodes[decision.ID]; exists {
		tg.decisionToNodes[decision.ID] = append(existing, temporalNode)
	} else {
		tg.decisionToNodes[decision.ID] = []*TemporalNode{temporalNode}
	}
	
	// Update staleness for related contexts
	tg.updateStalenessAfterChange(temporalNode)
	
	// Clear relevant caches
	tg.clearCacheForAddress(address)
	
	// Persist to storage
	if err := tg.persistTemporalNode(ctx, temporalNode); err != nil {
		return nil, fmt.Errorf("failed to persist evolved temporal node: %w", err)
	}
	
	return temporalNode, nil
}

// GetLatestVersion gets the most recent temporal node for an address
func (tg *temporalGraphImpl) GetLatestVersion(ctx context.Context, address ucxl.Address) (*TemporalNode, error) {
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	addressKey := address.String()
	nodes, exists := tg.addressToNodes[addressKey]
	if !exists || len(nodes) == 0 {
		return nil, fmt.Errorf("no temporal nodes found for address %s", address.String())
	}
	
	// Return the latest version (last in slice)
	return nodes[len(nodes)-1], nil
}

// GetVersionAtDecision gets context as it was at a specific decision hop
func (tg *temporalGraphImpl) GetVersionAtDecision(ctx context.Context, address ucxl.Address, 
	decisionHop int) (*TemporalNode, error) {
	
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	addressKey := address.String()
	nodes, exists := tg.addressToNodes[addressKey]
	if !exists || len(nodes) == 0 {
		return nil, fmt.Errorf("no temporal nodes found for address %s", address.String())
	}
	
	// Find node at specific decision hop (version)
	for _, node := range nodes {
		if node.Version == decisionHop {
			return node, nil
		}
	}
	
	return nil, fmt.Errorf("no temporal node found at decision hop %d for address %s", 
		decisionHop, address.String())
}

// GetEvolutionHistory gets complete evolution history ordered by decisions
func (tg *temporalGraphImpl) GetEvolutionHistory(ctx context.Context, address ucxl.Address) ([]*TemporalNode, error) {
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	addressKey := address.String()
	nodes, exists := tg.addressToNodes[addressKey]
	if !exists || len(nodes) == 0 {
		return []*TemporalNode{}, nil
	}
	
	// Sort by version to ensure proper order
	sortedNodes := make([]*TemporalNode, len(nodes))
	copy(sortedNodes, nodes)
	sort.Slice(sortedNodes, func(i, j int) bool {
		return sortedNodes[i].Version < sortedNodes[j].Version
	})
	
	return sortedNodes, nil
}

// AddInfluenceRelationship establishes that decisions in one context affect another
func (tg *temporalGraphImpl) AddInfluenceRelationship(ctx context.Context, influencer, influenced ucxl.Address) error {
	tg.mu.Lock()
	defer tg.mu.Unlock()
	
	// Get latest nodes for both addresses
	influencerNode, err := tg.getLatestNodeUnsafe(influencer)
	if err != nil {
		return fmt.Errorf("influencer node not found: %w", err)
	}
	
	influencedNode, err := tg.getLatestNodeUnsafe(influenced)
	if err != nil {
		return fmt.Errorf("influenced node not found: %w", err)
	}
	
	// Add to influence mappings
	influencerNodeID := influencerNode.ID
	influencedNodeID := influencedNode.ID
	
	// Add to influences map (influencer -> influenced)
	if influences, exists := tg.influences[influencerNodeID]; exists {
		// Check if relationship already exists
		for _, existingID := range influences {
			if existingID == influencedNodeID {
				return nil // Relationship already exists
			}
		}
		tg.influences[influencerNodeID] = append(influences, influencedNodeID)
	} else {
		tg.influences[influencerNodeID] = []string{influencedNodeID}
	}
	
	// Add to influencedBy map (influenced <- influencer)
	if influencedBy, exists := tg.influencedBy[influencedNodeID]; exists {
		// Check if relationship already exists
		for _, existingID := range influencedBy {
			if existingID == influencerNodeID {
				return nil // Relationship already exists
			}
		}
		tg.influencedBy[influencedNodeID] = append(influencedBy, influencerNodeID)
	} else {
		tg.influencedBy[influencedNodeID] = []string{influencerNodeID}
	}
	
	// Update temporal nodes with the influence relationship
	influencerNode.Influences = append(influencerNode.Influences, influenced)
	influencedNode.InfluencedBy = append(influencedNode.InfluencedBy, influencer)
	
	// Clear path cache as influence graph has changed
	tg.pathCache = make(map[string][]*DecisionStep)
	
	// Persist changes
	if err := tg.persistTemporalNode(ctx, influencerNode); err != nil {
		return fmt.Errorf("failed to persist influencer node: %w", err)
	}
	if err := tg.persistTemporalNode(ctx, influencedNode); err != nil {
		return fmt.Errorf("failed to persist influenced node: %w", err)
	}
	
	return nil
}

// RemoveInfluenceRelationship removes an influence relationship
func (tg *temporalGraphImpl) RemoveInfluenceRelationship(ctx context.Context, influencer, influenced ucxl.Address) error {
	tg.mu.Lock()
	defer tg.mu.Unlock()
	
	// Get latest nodes for both addresses
	influencerNode, err := tg.getLatestNodeUnsafe(influencer)
	if err != nil {
		return fmt.Errorf("influencer node not found: %w", err)
	}
	
	influencedNode, err := tg.getLatestNodeUnsafe(influenced)
	if err != nil {
		return fmt.Errorf("influenced node not found: %w", err)
	}
	
	// Remove from influence mappings
	influencerNodeID := influencerNode.ID
	influencedNodeID := influencedNode.ID
	
	// Remove from influences map
	if influences, exists := tg.influences[influencerNodeID]; exists {
		tg.influences[influencerNodeID] = tg.removeFromSlice(influences, influencedNodeID)
	}
	
	// Remove from influencedBy map
	if influencedBy, exists := tg.influencedBy[influencedNodeID]; exists {
		tg.influencedBy[influencedNodeID] = tg.removeFromSlice(influencedBy, influencerNodeID)
	}
	
	// Update temporal nodes
	influencerNode.Influences = tg.removeAddressFromSlice(influencerNode.Influences, influenced)
	influencedNode.InfluencedBy = tg.removeAddressFromSlice(influencedNode.InfluencedBy, influencer)
	
	// Clear path cache
	tg.pathCache = make(map[string][]*DecisionStep)
	
	// Persist changes
	if err := tg.persistTemporalNode(ctx, influencerNode); err != nil {
		return fmt.Errorf("failed to persist influencer node: %w", err)
	}
	if err := tg.persistTemporalNode(ctx, influencedNode); err != nil {
		return fmt.Errorf("failed to persist influenced node: %w", err)
	}
	
	return nil
}

// GetInfluenceRelationships gets all influence relationships for a context
func (tg *temporalGraphImpl) GetInfluenceRelationships(ctx context.Context, address ucxl.Address) ([]ucxl.Address, []ucxl.Address, error) {
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	node, err := tg.getLatestNodeUnsafe(address)
	if err != nil {
		return nil, nil, fmt.Errorf("node not found: %w", err)
	}
	
	influences := make([]ucxl.Address, len(node.Influences))
	copy(influences, node.Influences)
	
	influencedBy := make([]ucxl.Address, len(node.InfluencedBy))
	copy(influencedBy, node.InfluencedBy)
	
	return influences, influencedBy, nil
}

// FindRelatedDecisions finds decisions within N decision hops
func (tg *temporalGraphImpl) FindRelatedDecisions(ctx context.Context, address ucxl.Address, 
	maxHops int) ([]*DecisionPath, error) {
	
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	// Check cache first
	cacheKey := fmt.Sprintf("related-%s-%d", address.String(), maxHops)
	if cached, exists := tg.pathCache[cacheKey]; exists {
		paths := make([]*DecisionPath, len(cached))
		for i, step := range cached {
			paths[i] = &DecisionPath{
				From:      address,
				To:        step.Address,
				Steps:     []*DecisionStep{step},
				TotalHops: step.HopDistance,
				PathType:  "direct",
			}
		}
		return paths, nil
	}
	
	startNode, err := tg.getLatestNodeUnsafe(address)
	if err != nil {
		return nil, fmt.Errorf("start node not found: %w", err)
	}
	
	// Use BFS to find all nodes within maxHops
	visited := make(map[string]bool)
	queue := []*bfsItem{{node: startNode, distance: 0, path: []*DecisionStep{}}}
	relatedPaths := make([]*DecisionPath, 0)
	
	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]
		
		nodeID := current.node.ID
		if visited[nodeID] || current.distance > maxHops {
			continue
		}
		visited[nodeID] = true
		
		// If this is not the starting node, add it to results
		if current.distance > 0 {
			step := &DecisionStep{
				Address:      current.node.UCXLAddress,
				TemporalNode: current.node,
				HopDistance:  current.distance,
				Relationship: "influence",
			}
			
			path := &DecisionPath{
				From:      address,
				To:        current.node.UCXLAddress,
				Steps:     append(current.path, step),
				TotalHops: current.distance,
				PathType:  "influence",
			}
			relatedPaths = append(relatedPaths, path)
		}
		
		// Add influenced nodes to queue
		if influences, exists := tg.influences[nodeID]; exists {
			for _, influencedID := range influences {
				if !visited[influencedID] && current.distance < maxHops {
					if influencedNode, exists := tg.nodes[influencedID]; exists {
						newStep := &DecisionStep{
							Address:      current.node.UCXLAddress,
							TemporalNode: current.node,
							HopDistance:  current.distance,
							Relationship: "influences",
						}
						newPath := append(current.path, newStep)
						queue = append(queue, &bfsItem{
							node:     influencedNode,
							distance: current.distance + 1,
							path:     newPath,
						})
					}
				}
			}
		}
		
		// Add influencer nodes to queue
		if influencedBy, exists := tg.influencedBy[nodeID]; exists {
			for _, influencerID := range influencedBy {
				if !visited[influencerID] && current.distance < maxHops {
					if influencerNode, exists := tg.nodes[influencerID]; exists {
						newStep := &DecisionStep{
							Address:      current.node.UCXLAddress,
							TemporalNode: current.node,
							HopDistance:  current.distance,
							Relationship: "influenced_by",
						}
						newPath := append(current.path, newStep)
						queue = append(queue, &bfsItem{
							node:     influencerNode,
							distance: current.distance + 1,
							path:     newPath,
						})
					}
				}
			}
		}
	}
	
	return relatedPaths, nil
}

// FindDecisionPath finds shortest decision path between two addresses
func (tg *temporalGraphImpl) FindDecisionPath(ctx context.Context, from, to ucxl.Address) ([]*DecisionStep, error) {
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	// Check cache first
	cacheKey := fmt.Sprintf("path-%s-%s", from.String(), to.String())
	if cached, exists := tg.pathCache[cacheKey]; exists {
		return cached, nil
	}
	
	fromNode, err := tg.getLatestNodeUnsafe(from)
	if err != nil {
		return nil, fmt.Errorf("from node not found: %w", err)
	}
	
	toNode, err := tg.getLatestNodeUnsafe(to)
	if err != nil {
		return nil, fmt.Errorf("to node not found: %w", err)
	}
	
	// Use BFS to find shortest path
	visited := make(map[string]bool)
	queue := []*pathItem{{node: fromNode, path: []*DecisionStep{}}}
	
	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]
		
		nodeID := current.node.ID
		if visited[nodeID] {
			continue
		}
		visited[nodeID] = true
		
		// Check if we reached the target
		if current.node.UCXLAddress.String() == to.String() {
			// Cache the result
			tg.pathCache[cacheKey] = current.path
			return current.path, nil
		}
		
		// Explore influenced nodes
		if influences, exists := tg.influences[nodeID]; exists {
			for _, influencedID := range influences {
				if !visited[influencedID] {
					if influencedNode, exists := tg.nodes[influencedID]; exists {
						step := &DecisionStep{
							Address:      current.node.UCXLAddress,
							TemporalNode: current.node,
							HopDistance:  len(current.path),
							Relationship: "influences",
						}
						newPath := append(current.path, step)
						queue = append(queue, &pathItem{
							node: influencedNode,
							path: newPath,
						})
					}
				}
			}
		}
		
		// Explore influencer nodes
		if influencedBy, exists := tg.influencedBy[nodeID]; exists {
			for _, influencerID := range influencedBy {
				if !visited[influencerID] {
					if influencerNode, exists := tg.nodes[influencerID]; exists {
						step := &DecisionStep{
							Address:      current.node.UCXLAddress,
							TemporalNode: current.node,
							HopDistance:  len(current.path),
							Relationship: "influenced_by",
						}
						newPath := append(current.path, step)
						queue = append(queue, &pathItem{
							node: influencerNode,
							path: newPath,
						})
					}
				}
			}
		}
	}
	
	return nil, fmt.Errorf("no path found from %s to %s", from.String(), to.String())
}

// AnalyzeDecisionPatterns analyzes decision-making patterns over time
func (tg *temporalGraphImpl) AnalyzeDecisionPatterns(ctx context.Context) (*DecisionAnalysis, error) {
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	analysis := &DecisionAnalysis{
		TimeRange:                24 * time.Hour, // Analyze last 24 hours by default
		TotalDecisions:           len(tg.decisions),
		DecisionVelocity:         0,
		InfluenceNetworkSize:     len(tg.nodes),
		AverageInfluenceDistance: 0,
		MostInfluentialDecisions: make([]*InfluentialDecision, 0),
		DecisionClusters:         make([]*DecisionCluster, 0),
		Patterns:                 make([]*DecisionPattern, 0),
		Anomalies:               make([]*AnomalousDecision, 0),
		AnalyzedAt:              time.Now(),
	}
	
	// Calculate decision velocity
	cutoff := time.Now().Add(-analysis.TimeRange)
	recentDecisions := 0
	for _, decision := range tg.decisions {
		if decision.CreatedAt.After(cutoff) {
			recentDecisions++
		}
	}
	analysis.DecisionVelocity = float64(recentDecisions) / analysis.TimeRange.Hours()
	
	// Calculate average influence distance
	totalDistance := 0.0
	connections := 0
	for nodeID := range tg.influences {
		if influences, exists := tg.influences[nodeID]; exists {
			for range influences {
				totalDistance += 1.0 // Each connection is 1 hop
				connections++
			}
		}
	}
	if connections > 0 {
		analysis.AverageInfluenceDistance = totalDistance / float64(connections)
	}
	
	// Find most influential decisions (simplified)
	influenceScores := make(map[string]float64)
	for nodeID, node := range tg.nodes {
		score := float64(len(tg.influences[nodeID])) * 1.0 // Direct influences
		score += float64(len(tg.influencedBy[nodeID])) * 0.5 // Being influenced
		influenceScores[nodeID] = score
		
		if score > 3.0 { // Threshold for "influential"
			influential := &InfluentialDecision{
				Address:           node.UCXLAddress,
				DecisionHop:       node.Version,
				InfluenceScore:    score,
				AffectedContexts:  node.Influences,
				DecisionMetadata:  tg.decisions[node.DecisionID],
				InfluenceReasons:  []string{"high_connectivity", "multiple_influences"},
			}
			analysis.MostInfluentialDecisions = append(analysis.MostInfluentialDecisions, influential)
		}
	}
	
	// Sort influential decisions by score
	sort.Slice(analysis.MostInfluentialDecisions, func(i, j int) bool {
		return analysis.MostInfluentialDecisions[i].InfluenceScore > analysis.MostInfluentialDecisions[j].InfluenceScore
	})
	
	// Limit to top 10
	if len(analysis.MostInfluentialDecisions) > 10 {
		analysis.MostInfluentialDecisions = analysis.MostInfluentialDecisions[:10]
	}
	
	return analysis, nil
}

// ValidateTemporalIntegrity validates temporal graph integrity
func (tg *temporalGraphImpl) ValidateTemporalIntegrity(ctx context.Context) error {
	tg.mu.RLock()
	defer tg.mu.RUnlock()
	
	errors := make([]string, 0)
	
	// Check for orphaned nodes
	for nodeID, node := range tg.nodes {
		if node.ParentNode != nil {
			if _, exists := tg.nodes[*node.ParentNode]; !exists {
				errors = append(errors, fmt.Sprintf("orphaned node %s has non-existent parent %s", 
					nodeID, *node.ParentNode))
			}
		}
	}
	
	// Check influence consistency
	for nodeID := range tg.influences {
		if influences, exists := tg.influences[nodeID]; exists {
			for _, influencedID := range influences {
				// Check if the influenced node has this node in its influencedBy list
				if influencedByList, exists := tg.influencedBy[influencedID]; exists {
					found := false
					for _, influencerID := range influencedByList {
						if influencerID == nodeID {
							found = true
							break
						}
					}
					if !found {
						errors = append(errors, fmt.Sprintf("influence inconsistency: %s -> %s not reflected in influencedBy", 
							nodeID, influencedID))
					}
				}
			}
		}
	}
	
	// Check version sequence integrity
	for address, nodes := range tg.addressToNodes {
		sort.Slice(nodes, func(i, j int) bool {
			return nodes[i].Version < nodes[j].Version
		})
		
		for i, node := range nodes {
			expectedVersion := i + 1
			if node.Version != expectedVersion {
				errors = append(errors, fmt.Sprintf("version sequence error for address %s: expected %d, got %d", 
					address, expectedVersion, node.Version))
			}
		}
	}
	
	if len(errors) > 0 {
		return fmt.Errorf("temporal integrity violations: %v", errors)
	}
	
	return nil
}

// CompactHistory compacts old temporal data to save space
func (tg *temporalGraphImpl) CompactHistory(ctx context.Context, beforeTime time.Time) error {
	tg.mu.Lock()
	defer tg.mu.Unlock()
	
	compacted := 0
	
	// For each address, keep only the latest version and major milestones before the cutoff
	for address, nodes := range tg.addressToNodes {
		toKeep := make([]*TemporalNode, 0)
		toRemove := make([]*TemporalNode, 0)
		
		for _, node := range nodes {
			// Always keep nodes after the cutoff time
			if node.Timestamp.After(beforeTime) {
				toKeep = append(toKeep, node)
				continue
			}
			
			// Keep major changes and influential decisions
			if tg.isMajorChange(node) || tg.isInfluentialDecision(node) {
				toKeep = append(toKeep, node)
			} else {
				toRemove = append(toRemove, node)
			}
		}
		
		// Update the address mapping
		tg.addressToNodes[address] = toKeep
		
		// Remove old nodes from main maps
		for _, node := range toRemove {
			delete(tg.nodes, node.ID)
			delete(tg.influences, node.ID)
			delete(tg.influencedBy, node.ID)
			compacted++
		}
	}
	
	// Clear caches after compaction
	tg.pathCache = make(map[string][]*DecisionStep)
	tg.metricsCache = make(map[string]interface{})
	
	return nil
}

// Helper methods

func (tg *temporalGraphImpl) generateNodeID(address ucxl.Address, version int) string {
	return fmt.Sprintf("%s-v%d", address.String(), version)
}

func (tg *temporalGraphImpl) calculateContextHash(context *slurpContext.ContextNode) string {
	hasher := sha256.New()
	hasher.Write([]byte(fmt.Sprintf("%+v", context)))
	return fmt.Sprintf("%x", hasher.Sum(nil))[:16]
}

func (tg *temporalGraphImpl) getLatestNodeUnsafe(address ucxl.Address) (*TemporalNode, error) {
	addressKey := address.String()
	nodes, exists := tg.addressToNodes[addressKey]
	if !exists || len(nodes) == 0 {
		return nil, fmt.Errorf("no temporal nodes found for address %s", address.String())
	}
	return nodes[len(nodes)-1], nil
}

func (tg *temporalGraphImpl) removeFromSlice(slice []string, item string) []string {
	result := make([]string, 0, len(slice))
	for _, s := range slice {
		if s != item {
			result = append(result, s)
		}
	}
	return result
}

func (tg *temporalGraphImpl) removeAddressFromSlice(slice []ucxl.Address, item ucxl.Address) []ucxl.Address {
	result := make([]ucxl.Address, 0, len(slice))
	for _, addr := range slice {
		if addr.String() != item.String() {
			result = append(result, addr)
		}
	}
	return result
}

func (tg *temporalGraphImpl) updateStalenessAfterChange(changedNode *TemporalNode) {
	// Update staleness for all influenced contexts
	if influences, exists := tg.influences[changedNode.ID]; exists {
		for _, influencedID := range influences {
			if influencedNode, exists := tg.nodes[influencedID]; exists {
				// Calculate new staleness based on the change
				staleness := tg.calculateStaleness(influencedNode, changedNode)
				influencedNode.Staleness = math.Max(influencedNode.Staleness, staleness)
			}
		}
	}
}

func (tg *temporalGraphImpl) calculateStaleness(node *TemporalNode, changedNode *TemporalNode) float64 {
	// Simple staleness calculation based on time since last update and influence strength
	timeSinceUpdate := time.Since(node.Timestamp)
	timeWeight := math.Min(timeSinceUpdate.Hours()/168.0, 1.0) // Max staleness from time: 1 week
	
	// Influence weight based on connection strength
	influenceWeight := 0.0
	if len(node.InfluencedBy) > 0 {
		influenceWeight = 1.0 / float64(len(node.InfluencedBy)) // Stronger if fewer influencers
	}
	
	// Impact scope weight
	impactWeight := 0.0
	switch changedNode.ImpactScope {
	case ImpactSystem:
		impactWeight = 1.0
	case ImpactProject:
		impactWeight = 0.8
	case ImpactModule:
		impactWeight = 0.6
	case ImpactLocal:
		impactWeight = 0.4
	}
	
	return math.Min(
		tg.stalenessWeight.TimeWeight*timeWeight+
		tg.stalenessWeight.InfluenceWeight*influenceWeight+
		tg.stalenessWeight.ImportanceWeight*impactWeight, 1.0)
}

func (tg *temporalGraphImpl) clearCacheForAddress(address ucxl.Address) {
	addressStr := address.String()
	keysToDelete := make([]string, 0)
	
	for key := range tg.pathCache {
		if contains(key, addressStr) {
			keysToDelete = append(keysToDelete, key)
		}
	}
	
	for _, key := range keysToDelete {
		delete(tg.pathCache, key)
	}
}

func (tg *temporalGraphImpl) isMajorChange(node *TemporalNode) bool {
	return node.ChangeReason == ReasonArchitectureChange ||
		node.ChangeReason == ReasonDesignDecision ||
		node.ChangeReason == ReasonRequirementsChange
}

func (tg *temporalGraphImpl) isInfluentialDecision(node *TemporalNode) bool {
	influences := len(tg.influences[node.ID])
	influencedBy := len(tg.influencedBy[node.ID])
	return influences >= 3 || influencedBy >= 3 // Arbitrary threshold for "influential"
}

func (tg *temporalGraphImpl) persistTemporalNode(ctx context.Context, node *TemporalNode) error {
	// Convert to storage format and persist
	// This would integrate with the storage system
	// For now, we'll assume persistence happens in memory
	return nil
}

func contains(s, substr string) bool {
	return len(s) >= len(substr) && (s == substr || 
		(len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr)))
}

// Supporting types for BFS traversal

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

type pathItem struct {
	node *TemporalNode
	path []*DecisionStep
}