package context

import (
	"context"
	"fmt"
	"time"

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

// ContextResolver defines the interface for hierarchical context resolution
//
// The resolver implements bounded hierarchy traversal with caching and
// role-based access control, providing efficient context resolution for
// UCXL addresses through cascading inheritance patterns.
type ContextResolver interface {
	// Resolve resolves context for a UCXL address using bounded hierarchy traversal
	// with default depth limits and role-based access control
	Resolve(ctx context.Context, address ucxl.Address, role string) (*ResolvedContext, error)
	
	// ResolveWithDepth resolves context with custom bounded depth limit
	// providing fine-grained control over hierarchy traversal depth for
	// performance optimization and resource management
	ResolveWithDepth(ctx context.Context, address ucxl.Address, role string, maxDepth int) (*ResolvedContext, error)
	
	// BatchResolve efficiently resolves multiple UCXL addresses in parallel
	// uses request deduplication, shared caching, and role-based filtering
	// for optimal performance with bulk operations
	BatchResolve(ctx context.Context, request *BatchResolutionRequest) (*BatchResolutionResult, error)
	
	// AddGlobalContext adds a global context that applies to all addresses
	// global contexts are automatically merged into all resolution results
	AddGlobalContext(ctx context.Context, globalCtx *ContextNode) error
	
	// SetHierarchyDepthLimit sets the maximum hierarchy depth for bounded traversal
	// prevents infinite loops and controls resource usage during resolution
	SetHierarchyDepthLimit(maxDepth int)
	
	// GetResolutionStatistics returns resolver performance and operational statistics
	GetStatistics() *ResolutionStatistics
	
	// InvalidateCache invalidates cached resolutions for an address pattern
	// useful for cache invalidation when contexts change
	InvalidateCache(pattern string) error
	
	// ClearCache clears all cached resolutions
	ClearCache() error
}

// HierarchyManager manages the context hierarchy with bounded traversal
//
// Provides operations for maintaining the hierarchical structure of
// context nodes while enforcing depth limits and consistency constraints.
type HierarchyManager interface {
	// LoadHierarchy loads the context hierarchy from storage
	LoadHierarchy(ctx context.Context) error
	
	// AddNode adds a context node to the hierarchy with validation
	AddNode(ctx context.Context, node *ContextNode) error
	
	// UpdateNode updates an existing context node
	UpdateNode(ctx context.Context, node *ContextNode) error
	
	// RemoveNode removes a context node and handles orphaned children
	RemoveNode(ctx context.Context, path string) error
	
	// GetNode retrieves a context node by path
	GetNode(ctx context.Context, path string) (*ContextNode, error)
	
	// TraverseUp traverses up the hierarchy with bounded depth
	TraverseUp(ctx context.Context, startPath string, maxDepth int) ([]*ContextNode, error)
	
	// TraverseDown traverses down the hierarchy with bounded depth
	TraverseDown(ctx context.Context, startPath string, maxDepth int) ([]*ContextNode, error)
	
	// GetChildren gets immediate children of a node
	GetChildren(ctx context.Context, path string) ([]*ContextNode, error)
	
	// GetParent gets the immediate parent of a node
	GetParent(ctx context.Context, path string) (*ContextNode, error)
	
	// ValidateHierarchy validates hierarchy integrity and constraints
	ValidateHierarchy(ctx context.Context) error
	
	// GetHierarchyStats returns statistics about the hierarchy
	GetHierarchyStats(ctx context.Context) (*HierarchyStats, error)
}

// GlobalContextManager manages global contexts that apply everywhere
//
// Global contexts provide system-wide applicable metadata that is
// automatically included in all context resolutions regardless of
// hierarchy position.
type GlobalContextManager interface {
	// AddGlobalContext adds a context that applies globally
	AddGlobalContext(ctx context.Context, globalCtx *ContextNode) error
	
	// RemoveGlobalContext removes a global context
	RemoveGlobalContext(ctx context.Context, contextID string) error
	
	// UpdateGlobalContext updates an existing global context
	UpdateGlobalContext(ctx context.Context, globalCtx *ContextNode) error
	
	// ListGlobalContexts lists all global contexts ordered by priority
	ListGlobalContexts(ctx context.Context) ([]*ContextNode, error)
	
	// GetGlobalContext retrieves a specific global context
	GetGlobalContext(ctx context.Context, contextID string) (*ContextNode, error)
	
	// ApplyGlobalContexts applies global contexts to a resolution
	ApplyGlobalContexts(ctx context.Context, resolved *ResolvedContext) error
	
	// EnableGlobalContext enables/disables a global context
	EnableGlobalContext(ctx context.Context, contextID string, enabled bool) error
	
	// SetGlobalContextPriority sets priority for global context application
	SetGlobalContextPriority(ctx context.Context, contextID string, priority int) error
}

// CacheManager manages caching for context resolution performance
type CacheManager interface {
	// Get retrieves a cached resolution
	Get(ctx context.Context, key string) (*ResolvedContext, error)
	
	// Set stores a resolution in cache with TTL
	Set(ctx context.Context, key string, resolved *ResolvedContext, ttl time.Duration) error
	
	// Delete removes a specific cache entry
	Delete(ctx context.Context, key string) error
	
	// DeletePattern removes cache entries matching a pattern
	DeletePattern(ctx context.Context, pattern string) error
	
	// Clear clears all cached entries
	Clear(ctx context.Context) error
	
	// GetStats returns cache performance statistics
	GetStats() *CacheStats
}

// CacheStats represents cache performance statistics
type CacheStats struct {
	HitRate      float64   `json:"hit_rate"`       // Cache hit rate (0-1)
	MissRate     float64   `json:"miss_rate"`      // Cache miss rate (0-1)
	TotalHits    int64     `json:"total_hits"`     // Total cache hits
	TotalMisses  int64     `json:"total_misses"`   // Total cache misses
	CurrentSize  int64     `json:"current_size"`   // Current cache size
	MaxSize      int64     `json:"max_size"`       // Maximum cache size
	Evictions    int64     `json:"evictions"`      // Number of cache evictions
	LastEviction time.Time `json:"last_eviction"`  // When last eviction occurred
}

// ContextMerger handles merging contexts during resolution
type ContextMerger interface {
	// MergeContexts merges multiple contexts using inheritance rules
	MergeContexts(contexts []*ContextNode, options *MergeOptions) (*ResolvedContext, error)
	
	// MergeWithGlobal merges context with global contexts
	MergeWithGlobal(base *ResolvedContext, globals []*ContextNode) (*ResolvedContext, error)
	
	// CalculateSpecificity calculates context specificity for merge priority
	CalculateSpecificity(ctx *ContextNode) int
	
	// ValidateMergeResult validates merged context quality
	ValidateMergeResult(resolved *ResolvedContext) (*ValidationResult, error)
}

// ContextValidator validates context data quality and consistency
type ContextValidator interface {
	// ValidateNode validates a single context node
	ValidateNode(ctx context.Context, node *ContextNode) (*ValidationResult, error)
	
	// ValidateResolved validates a resolved context
	ValidateResolved(ctx context.Context, resolved *ResolvedContext) (*ValidationResult, error)
	
	// ValidateHierarchyConsistency validates hierarchy-wide consistency
	ValidateHierarchyConsistency(ctx context.Context) ([]*ValidationIssue, error)
	
	// SuggestImprovements suggests improvements for context quality
	SuggestImprovements(ctx context.Context, node *ContextNode) ([]string, error)
}

// Helper functions and integration examples

// ValidateContextResolutionRequest validates a context resolution request
func ValidateContextResolutionRequest(address ucxl.Address, role string, maxDepth int) error {
	if err := address.Validate(); err != nil {
		return NewContextError(ErrorTypeValidation, ErrorCodeInvalidAddress, 
			"invalid UCXL address in resolution request").WithUnderlying(err).WithAddress(address)
	}

	if role == "" {
		return NewContextError(ErrorTypeValidation, ErrorCodeInvalidRole, 
			"role cannot be empty in resolution request").WithAddress(address)
	}

	if maxDepth < 0 {
		return NewContextError(ErrorTypeValidation, ErrorCodeDepthExceeded, 
			"maxDepth cannot be negative").WithAddress(address).
			WithContext("max_depth", fmt.Sprintf("%d", maxDepth))
	}

	if maxDepth > 50 { // Reasonable upper bound to prevent resource exhaustion
		return NewContextError(ErrorTypeValidation, ErrorCodeDepthExceeded, 
			"maxDepth exceeds reasonable limits").WithAddress(address).
			WithContext("max_depth", fmt.Sprintf("%d", maxDepth))
	}

	return nil
}

// ValidateBatchResolutionRequest validates a batch resolution request
func ValidateBatchResolutionRequest(request *BatchResolutionRequest) error {
	if request == nil {
		return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext, 
			"batch resolution request cannot be nil")
	}

	if len(request.Addresses) == 0 {
		return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext, 
			"batch resolution request must contain at least one address")
	}

	if len(request.Addresses) > 100 { // Prevent excessive batch sizes
		return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext, 
			"batch resolution request exceeds maximum size").
			WithContext("size", fmt.Sprintf("%d", len(request.Addresses)))
	}

	for i, address := range request.Addresses {
		if err := address.Validate(); err != nil {
			return NewContextError(ErrorTypeValidation, ErrorCodeInvalidAddress, 
				fmt.Sprintf("invalid address at index %d", i)).WithUnderlying(err).WithAddress(address)
		}
	}

	if request.Role == "" {
		return NewContextError(ErrorTypeValidation, ErrorCodeInvalidRole, 
			"role cannot be empty in batch resolution request")
	}

	if request.MaxDepth < 0 {
		return NewContextError(ErrorTypeValidation, ErrorCodeDepthExceeded, 
			"maxDepth cannot be negative in batch resolution request").
			WithContext("max_depth", fmt.Sprintf("%d", request.MaxDepth))
	}

	return nil
}

// CalculateResolutionConfidence calculates overall confidence from multiple context nodes
func CalculateResolutionConfidence(contexts []*ContextNode) float64 {
	if len(contexts) == 0 {
		return 0.0
	}

	totalConfidence := 0.0
	totalWeight := 0.0
	
	for _, ctx := range contexts {
		// Weight by specificity - higher specificity contexts have more influence
		weight := float64(ctx.ContextSpecificity + 1)
		totalConfidence += ctx.RAGConfidence * weight
		totalWeight += weight
	}
	
	if totalWeight == 0 {
		return 0.0
	}
	
	confidence := totalConfidence / totalWeight
	
	// Apply diminishing returns for multiple contexts
	if len(contexts) > 1 {
		// Slight boost for having multiple confirming contexts, but not linear
		multiplier := 1.0 + (float64(len(contexts)-1) * 0.1)
		confidence = confidence * multiplier
		if confidence > 1.0 {
			confidence = 1.0
		}
	}
	
	return confidence
}

// FilterContextsByRole filters context nodes based on role access
func FilterContextsByRole(contexts []*ContextNode, role string, authority config.AuthorityLevel) []*ContextNode {
	filtered := make([]*ContextNode, 0, len(contexts))
	
	for _, ctx := range contexts {
		if ctx.CanAccess(role, authority) {
			filtered = append(filtered, ctx)
		}
	}
	
	return filtered
}

// MergeStringSlices merges multiple string slices with deduplication
func MergeStringSlices(slices ...[]string) []string {
	seen := make(map[string]bool)
	var result []string
	
	for _, slice := range slices {
		for _, item := range slice {
			if !seen[item] && item != "" {
				seen[item] = true
				result = append(result, item)
			}
		}
	}
	
	return result
}

// BuildInheritanceChain builds the inheritance chain for a resolved context
func BuildInheritanceChain(contexts []*ContextNode) []string {
	chain := make([]string, 0, len(contexts))
	
	// Sort by specificity (most specific first)
	for _, ctx := range contexts {
		chain = append(chain, ctx.Path)
	}
	
	return chain
}

// GenerateCacheKey generates a cache key for resolution requests
func GenerateCacheKey(address ucxl.Address, role string, maxDepth int) string {
	return fmt.Sprintf("resolve:%s:%s:%d", address.String(), role, maxDepth)
}

// IsContextStale determines if a context node is stale and needs refresh
func IsContextStale(ctx *ContextNode, staleTTL time.Duration) bool {
	return time.Since(ctx.GeneratedAt) > staleTTL
}

/*
Integration Examples:

1. DHT Integration Example:
   
   // Store context in DHT with role-based encryption
   func (resolver *DefaultContextResolver) storeContextInDHT(ctx *ContextNode, roles []string) error {
       for _, role := range roles {
           // Encrypt context for role
           encrypted, err := resolver.crypto.EncryptForRole(ctx, role)
           if err != nil {
               return NewContextError(ErrorTypeEncryption, ErrorCodeEncryptionFailed, 
                   "failed to encrypt context for role").WithAddress(ctx.UCXLAddress).
                   WithContext("role", role).WithUnderlying(err)
           }
           
           // Store in DHT
           key := fmt.Sprintf("context:%s:%s", ctx.UCXLAddress.String(), role)
           if err := resolver.dht.Put(key, encrypted); err != nil {
               return NewContextError(ErrorTypeDHT, ErrorCodeDHTError, 
                   "failed to store context in DHT").WithAddress(ctx.UCXLAddress).
                   WithContext("role", role).WithUnderlying(err)
           }
       }
       return nil
   }

2. Leader Election Integration Example:

   // Context generation only happens on leader node
   func (manager *ContextManager) GenerateContextIfLeader(filePath string, role string) error {
       if !manager.IsLeader() {
           return NewContextError(ErrorTypeAccess, ErrorCodeAccessDenied, 
               "context generation is only allowed on leader nodes").
               WithContext("current_role", "follower")
       }
       
       // Parse UCXL address from file path
       address, err := manager.pathResolver.PathToUCXL(filePath)
       if err != nil {
           return NewContextError(ErrorTypeValidation, ErrorCodeInvalidAddress, 
               "failed to resolve file path to UCXL address").WithUnderlying(err).
               WithContext("file_path", filePath)
       }
       
       // Generate context using intelligence engine
       ctx, err := manager.intelligence.AnalyzeFile(context.Background(), filePath, role)
       if err != nil {
           return NewContextError(ErrorTypeIntelligence, ErrorCodeInternalError, 
               "failed to generate context").WithAddress(*address).WithUnderlying(err)
       }
       
       // Store in hierarchy manager
       if err := manager.hierarchyManager.AddNode(context.Background(), ctx); err != nil {
           return NewContextError(ErrorTypeHierarchy, ErrorCodeStorageError, 
               "failed to add context to hierarchy").WithAddress(ctx.UCXLAddress).
               WithUnderlying(err)
       }
       
       // Distribute via DHT for role-based access
       roles := manager.getRolesForContext(ctx)
       return manager.distributor.DistributeContext(ctx, roles)
   }

3. Crypto Integration Example:

   // Decrypt context based on role authority
   func (resolver *DefaultContextResolver) decryptContextForRole(encrypted []byte, role string) (*ContextNode, error) {
       // Check if current agent can decrypt this role's content
       canDecrypt, err := resolver.config.CanDecryptRole(role)
       if err != nil {
           return nil, NewContextError(ErrorTypeAccess, ErrorCodeInvalidRole, 
               "failed to check decryption permissions").WithContext("role", role).
               WithUnderlying(err)
       }
       
       if !canDecrypt {
           return nil, NewContextError(ErrorTypeAccess, ErrorCodeAccessDenied, 
               "insufficient authority to decrypt context").WithContext("role", role).
               WithContext("current_role", resolver.config.Agent.Role)
       }
       
       // Decrypt using role's private key
       decrypted, err := resolver.crypto.DecryptWithRole(encrypted)
       if err != nil {
           return nil, NewContextError(ErrorTypeEncryption, ErrorCodeDecryptionFailed, 
               "failed to decrypt context").WithContext("role", role).WithUnderlying(err)
       }
       
       // Deserialize context
       var ctx ContextNode
       if err := json.Unmarshal(decrypted, &ctx); err != nil {
           return nil, NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext, 
               "failed to deserialize decrypted context").WithUnderlying(err)
       }
       
       return &ctx, nil
   }

4. Complete Resolution Flow Example:

   // Resolve context with full BZZZ integration
   func (resolver *DefaultContextResolver) ResolveWithIntegration(ctx context.Context, address ucxl.Address, role string, maxDepth int) (*ResolvedContext, error) {
       // 1. Validate request
       if err := ValidateContextResolutionRequest(address, role, maxDepth); err != nil {
           return nil, err
       }
       
       // 2. Check cache first
       cacheKey := GenerateCacheKey(address, role, maxDepth)
       if cached, err := resolver.cache.Get(ctx, cacheKey); err == nil {
           resolver.stats.CacheHits++
           return cached, nil
       }
       resolver.stats.CacheMisses++
       
       // 3. Try local hierarchy first
       localContexts, err := resolver.hierarchyManager.TraverseUp(ctx, address.Path, maxDepth)
       if err != nil {
           return nil, NewContextError(ErrorTypeHierarchy, ErrorCodeStorageError, 
               "failed to traverse local hierarchy").WithAddress(address).WithUnderlying(err)
       }
       
       // 4. If no local contexts, try DHT
       var dhtContexts []*ContextNode
       if len(localContexts) == 0 {
           dhtContext, err := resolver.fetchContextFromDHT(address, role)
           if err == nil {
               dhtContexts = []*ContextNode{dhtContext}
           }
       }
       
       // 5. Combine local and DHT contexts
       allContexts := append(localContexts, dhtContexts...)
       if len(allContexts) == 0 {
           return nil, NewContextError(ErrorTypeResolution, ErrorCodeNotFound, 
               "no context found for address").WithAddress(address)
       }
       
       // 6. Filter by role access
       authority, err := resolver.config.GetRoleAuthority(role)
       if err != nil {
           return nil, NewContextError(ErrorTypeAccess, ErrorCodeInvalidRole, 
               "failed to get role authority").WithContext("role", role).WithUnderlying(err)
       }
       
       accessibleContexts := FilterContextsByRole(allContexts, role, authority)
       if len(accessibleContexts) == 0 {
           return nil, NewContextError(ErrorTypeAccess, ErrorCodeAccessDenied, 
               "no accessible contexts for role").WithAddress(address).WithContext("role", role)
       }
       
       // 7. Merge contexts using inheritance rules
       resolved, err := resolver.merger.MergeContexts(accessibleContexts, resolver.mergeOptions)
       if err != nil {
           return nil, NewContextError(ErrorTypeResolution, ErrorCodeInternalError, 
               "failed to merge contexts").WithAddress(address).WithUnderlying(err)
       }
       
       // 8. Apply global contexts if enabled
       if resolver.globalContextsEnabled {
           globalContexts, err := resolver.globalManager.ListGlobalContexts(ctx)
           if err == nil && len(globalContexts) > 0 {
               resolved, err = resolver.merger.MergeWithGlobal(resolved, globalContexts)
               if err != nil {
                   return nil, NewContextError(ErrorTypeResolution, ErrorCodeInternalError, 
                       "failed to apply global contexts").WithAddress(address).WithUnderlying(err)
               }
               resolved.GlobalContextsApplied = true
           }
       }
       
       // 9. Validate resolved context
       if err := resolved.Validate(); err != nil {
           return nil, NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext, 
               "resolved context failed validation").WithAddress(address).WithUnderlying(err)
       }
       
       // 10. Cache the result
       if err := resolver.cache.Set(ctx, cacheKey, resolved, resolver.cacheTTL); err != nil {
           // Log but don't fail the request
           resolver.logger.Warn("failed to cache resolved context", "error", err)
       }
       
       // 11. Update statistics
       resolver.stats.TotalResolutions++
       
       return resolved, nil
   }
*/