package intelligence

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

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

// AnalyzeFile analyzes a single file and generates contextual understanding
func (e *DefaultIntelligenceEngine) AnalyzeFile(ctx context.Context, filePath string, role string) (*slurpContext.ContextNode, error) {
	start := time.Now()
	defer func() {
		e.updateStats("file_analysis", time.Since(start), true)
	}()

	// Check cache first
	cacheKey := fmt.Sprintf("file:%s:%s", filePath, role)
	if cached, ok := e.cache.Load(cacheKey); ok {
		if entry, ok := cached.(*CacheEntry); ok && time.Now().Before(entry.ExpiresAt) {
			e.mu.Lock()
			e.stats.CacheHitRate = (e.stats.CacheHitRate*float64(e.stats.TotalAnalyses) + 1) / float64(e.stats.TotalAnalyses+1)
			e.mu.Unlock()
			return entry.ContextNode, nil
		}
	}

	// Read file content
	content, err := e.readFileContent(filePath)
	if err != nil {
		e.updateStats("file_analysis", time.Since(start), false)
		return nil, fmt.Errorf("failed to read file %s: %w", filePath, err)
	}

	// Skip files that are too large
	if int64(len(content)) > e.config.MaxFileSize {
		e.updateStats("file_analysis", time.Since(start), false)
		return nil, fmt.Errorf("file %s too large (%d bytes > %d bytes)", filePath, len(content), e.config.MaxFileSize)
	}

	// Perform file analysis
	analysis, err := e.fileAnalyzer.AnalyzeContent(ctx, filePath, content)
	if err != nil {
		e.updateStats("file_analysis", time.Since(start), false)
		return nil, fmt.Errorf("failed to analyze file content: %w", err)
	}

	// Generate UCXL address for the file
	ucxlAddr, err := e.generateUCXLAddress(filePath)
	if err != nil {
		e.updateStats("file_analysis", time.Since(start), false)
		return nil, fmt.Errorf("failed to generate UCXL address: %w", err)
	}

	// Extract purpose and summary
	purpose, purposeConf, err := e.fileAnalyzer.IdentifyPurpose(ctx, analysis)
	if err != nil {
		purpose = "Unknown purpose"
		purposeConf = 0.0
	}

	summary, err := e.fileAnalyzer.GenerateSummary(ctx, analysis)
	if err != nil {
		summary = "File analysis summary unavailable"
	}

	// Extract technologies
	technologies, err := e.fileAnalyzer.ExtractTechnologies(ctx, analysis)
	if err != nil {
		technologies = []string{}
	}

	// Generate basic tags
	tags := e.generateFileTags(analysis, filePath)

	// Generate role-specific insights
	insights, err := e.GenerateRoleInsights(ctx, nil, role)
	if err != nil {
		insights = []string{}
	}

	// Enhance with RAG if enabled
	ragConfidence := 0.0
	if e.config.RAGEnabled {
		// This would be enhanced in a real implementation
		ragConfidence = 0.7
	}

	// Create context node
	contextNode := &slurpContext.ContextNode{
		Path:               filePath,
		UCXLAddress:        *ucxlAddr,
		Summary:            summary,
		Purpose:            purpose,
		Technologies:       technologies,
		Tags:               tags,
		Insights:           insights,
		OverridesParent:    false,
		ContextSpecificity: e.calculateSpecificity(analysis),
		AppliesToChildren:  false,
		GeneratedAt:        time.Now(),
		RAGConfidence:      ragConfidence,
		EncryptedFor:       e.determineEncryptionRoles(role, purposeConf),
		AccessLevel:        e.determineAccessLevel(analysis, role),
		Metadata:           make(map[string]interface{}),
	}

	// Add analysis metadata
	contextNode.Metadata["analysis"] = analysis
	contextNode.Metadata["purpose_confidence"] = purposeConf
	contextNode.Metadata["role"] = role

	// Cache the result
	cacheEntry := &CacheEntry{
		ContextNode: contextNode,
		CreatedAt:   time.Now(),
		ExpiresAt:   time.Now().Add(e.config.CacheTTL),
	}
	e.cache.Store(cacheKey, cacheEntry)

	return contextNode, nil
}

// AnalyzeDirectory analyzes directory structure for hierarchical patterns
func (e *DefaultIntelligenceEngine) AnalyzeDirectory(ctx context.Context, dirPath string) ([]*slurpContext.ContextNode, error) {
	start := time.Now()
	defer func() {
		e.updateStats("directory_analysis", time.Since(start), true)
	}()

	// Analyze directory structure
	structure, err := e.directoryAnalyzer.AnalyzeStructure(ctx, dirPath)
	if err != nil {
		e.updateStats("directory_analysis", time.Since(start), false)
		return nil, fmt.Errorf("failed to analyze directory structure: %w", err)
	}

	// Generate hierarchy with bounded depth
	hierarchy, err := e.directoryAnalyzer.GenerateHierarchy(ctx, dirPath, 5) // Max 5 levels deep
	if err != nil {
		e.updateStats("directory_analysis", time.Since(start), false)
		return nil, fmt.Errorf("failed to generate hierarchy: %w", err)
	}

	return hierarchy, nil
}

// GenerateRoleInsights generates role-specific insights for existing context
func (e *DefaultIntelligenceEngine) GenerateRoleInsights(ctx context.Context, baseContext *slurpContext.ContextNode, role string) ([]string, error) {
	insights := []string{}

	// Get role profile
	profile, exists := e.roleProfiles[role]
	if !exists {
		// Generate generic insights
		insights = append(insights, "Generic insight: Consider code quality and maintainability")
		return insights, nil
	}

	// Generate role-specific insights based on profile
	for _, insightType := range profile.InsightTypes {
		switch insightType {
		case "security":
			insights = append(insights, "Security: Review for potential vulnerabilities and secure coding practices")
		case "performance":
			insights = append(insights, "Performance: Analyze for optimization opportunities and bottlenecks")
		case "architecture":
			insights = append(insights, "Architecture: Ensure alignment with system design patterns")
		case "testing":
			insights = append(insights, "Testing: Consider test coverage and quality assurance requirements")
		case "ui_ux":
			insights = append(insights, "UI/UX: Focus on user experience and interface design principles")
		case "api_design":
			insights = append(insights, "API Design: Ensure RESTful principles and proper error handling")
		case "database":
			insights = append(insights, "Database: Consider data modeling and query optimization")
		case "deployment":
			insights = append(insights, "Deployment: Plan for scalability and infrastructure requirements")
		}
	}

	// Add context-specific insights if baseContext is provided
	if baseContext != nil {
		contextInsights := e.generateContextSpecificInsights(baseContext, role)
		insights = append(insights, contextInsights...)
	}

	return insights, nil
}

// AssessGoalAlignment assesses how well context aligns with project goals
func (e *DefaultIntelligenceEngine) AssessGoalAlignment(ctx context.Context, node *slurpContext.ContextNode) (float64, error) {
	if len(e.projectGoals) == 0 {
		return 0.5, nil // Default alignment score when no goals defined
	}

	totalAlignment := 0.0
	totalWeight := 0.0

	for _, goal := range e.projectGoals {
		alignment := e.calculateGoalAlignment(node, goal)
		weight := float64(10 - goal.Priority) // Higher priority = higher weight
		totalAlignment += alignment * weight
		totalWeight += weight
	}

	if totalWeight == 0 {
		return 0.5, nil
	}

	return totalAlignment / totalWeight, nil
}

// AnalyzeBatch processes multiple files efficiently in parallel
func (e *DefaultIntelligenceEngine) AnalyzeBatch(ctx context.Context, filePaths []string, role string) (map[string]*slurpContext.ContextNode, error) {
	results := make(map[string]*slurpContext.ContextNode)
	mu := sync.Mutex{}
	wg := sync.WaitGroup{}
	errorCh := make(chan error, len(filePaths))

	// Limit concurrency
	semaphore := make(chan struct{}, e.config.MaxConcurrentAnalysis)

	for _, filePath := range filePaths {
		wg.Add(1)
		go func(path string) {
			defer wg.Done()
			semaphore <- struct{}{} // Acquire semaphore
			defer func() { <-semaphore }() // Release semaphore

			ctxNode, err := e.AnalyzeFile(ctx, path, role)
			if err != nil {
				errorCh <- fmt.Errorf("failed to analyze %s: %w", path, err)
				return
			}

			mu.Lock()
			results[path] = ctxNode
			mu.Unlock()
		}(filePath)
	}

	wg.Wait()
	close(errorCh)

	// Collect any errors
	var errs []error
	for err := range errorCh {
		errs = append(errs, err)
	}

	if len(errs) > 0 {
		return results, fmt.Errorf("batch analysis errors: %v", errs)
	}

	return results, nil
}

// DetectPatterns identifies recurring patterns across multiple contexts
func (e *DefaultIntelligenceEngine) DetectPatterns(ctx context.Context, contexts []*slurpContext.ContextNode) ([]*Pattern, error) {
	patterns := []*Pattern{}

	// Use pattern detector to find code patterns
	for _, context := range contexts {
		if context.Metadata["analysis"] != nil {
			if analysis, ok := context.Metadata["analysis"].(*FileAnalysis); ok {
				codePatterns, err := e.patternDetector.DetectCodePatterns(ctx, context.Path, []byte(analysis.FilePath))
				if err == nil {
					for _, cp := range codePatterns {
						patterns = append(patterns, &cp.Pattern)
					}
				}
			}
		}
	}

	// Detect naming patterns
	namingPatterns, err := e.patternDetector.DetectNamingPatterns(ctx, contexts)
	if err == nil {
		for _, np := range namingPatterns {
			patterns = append(patterns, &np.Pattern)
		}
	}

	return patterns, nil
}

// EnhanceWithRAG enhances context using RAG system knowledge
func (e *DefaultIntelligenceEngine) EnhanceWithRAG(ctx context.Context, node *slurpContext.ContextNode) (*slurpContext.ContextNode, error) {
	if !e.config.RAGEnabled {
		return node, nil // Return unchanged if RAG is disabled
	}

	// Create query for RAG system
	query := fmt.Sprintf("Provide insights for %s: %s", node.Purpose, node.Summary)
	queryContext := map[string]interface{}{
		"file_path":    node.Path,
		"technologies": node.Technologies,
		"tags":         node.Tags,
	}

	// Query RAG system
	ragResponse, err := e.ragIntegration.Query(ctx, query, queryContext)
	if err != nil {
		return node, fmt.Errorf("RAG query failed: %w", err)
	}

	// Enhance context with RAG insights
	enhanced := node.Clone()
	if ragResponse.Confidence >= e.config.MinConfidenceThreshold {
		enhanced.Insights = append(enhanced.Insights, fmt.Sprintf("RAG: %s", ragResponse.Answer))
		enhanced.RAGConfidence = ragResponse.Confidence
		
		// Add source information to metadata
		if len(ragResponse.Sources) > 0 {
			sources := make([]string, len(ragResponse.Sources))
			for i, source := range ragResponse.Sources {
				sources[i] = source.Title
			}
			enhanced.Metadata["rag_sources"] = sources
		}
	}

	return enhanced, nil
}

// ValidateContext validates generated context quality and consistency
func (e *DefaultIntelligenceEngine) ValidateContext(ctx context.Context, node *slurpContext.ContextNode) (*ValidationResult, error) {
	result := &ValidationResult{
		Valid:           true,
		ConfidenceScore: 1.0,
		QualityScore:    1.0,
		Issues:          []*ValidationIssue{},
		Suggestions:     []*Suggestion{},
		ValidatedAt:     time.Now(),
	}

	// Validate basic structure
	if err := node.Validate(); err != nil {
		result.Valid = false
		result.Issues = append(result.Issues, &ValidationIssue{
			Type:       "structure",
			Severity:   "error",
			Message:    err.Error(),
			Field:      "context_node",
			Suggestion: "Fix validation errors in context structure",
			Impact:     0.8,
		})
	}

	// Check quality thresholds
	if node.RAGConfidence < e.config.MinConfidenceThreshold {
		result.QualityScore *= 0.8
		result.Suggestions = append(result.Suggestions, &Suggestion{
			Type:        "quality",
			Title:       "Low RAG confidence",
			Description: "Consider enhancing context with additional analysis",
			Confidence:  0.7,
			Priority:    2,
			Action:      "re_analyze",
			Impact:      "medium",
		})
	}

	// Validate content quality
	if len(node.Summary) < 10 {
		result.QualityScore *= 0.9
		result.Issues = append(result.Issues, &ValidationIssue{
			Type:       "content",
			Severity:   "warning",
			Message:    "Summary too short",
			Field:      "summary",
			Suggestion: "Provide more detailed summary",
			Impact:     0.1,
		})
	}

	return result, nil
}

// GetEngineStats returns engine performance and operational statistics
func (e *DefaultIntelligenceEngine) GetEngineStats() (*EngineStatistics, error) {
	e.mu.RLock()
	defer e.mu.RUnlock()

	// Calculate cache hit rate
	cacheSize := 0
	e.cache.Range(func(key, value interface{}) bool {
		cacheSize++
		return true
	})

	stats := *e.stats // Copy current stats
	stats.CacheHitRate = e.calculateCacheHitRate()

	return &stats, nil
}

// SetConfiguration updates engine configuration
func (e *DefaultIntelligenceEngine) SetConfiguration(config *EngineConfig) error {
	e.mu.Lock()
	defer e.mu.Unlock()

	if config == nil {
		return fmt.Errorf("configuration cannot be nil")
	}

	e.config = config
	e.projectGoals = config.ProjectGoals
	e.roleProfiles = config.RoleProfiles

	return nil
}

// Helper methods

// readFileContent reads and returns file content
func (e *DefaultIntelligenceEngine) readFileContent(filePath string) ([]byte, error) {
	return ioutil.ReadFile(filePath)
}

// generateUCXLAddress generates a UCXL address for a file path
func (e *DefaultIntelligenceEngine) generateUCXLAddress(filePath string) (*ucxl.Address, error) {
	// Simple implementation - in reality this would be more sophisticated
	cleanPath := filepath.Clean(filePath)
	addr, err := ucxl.ParseAddress(fmt.Sprintf("file://%s", cleanPath))
	if err != nil {
		return nil, fmt.Errorf("failed to generate UCXL address: %w", err)
	}
	return addr, nil
}

// generateFileTags generates tags based on file analysis and path
func (e *DefaultIntelligenceEngine) generateFileTags(analysis *FileAnalysis, filePath string) []string {
	tags := []string{}

	// Add language tag
	if analysis.Language != "" {
		tags = append(tags, analysis.Language)
	}

	// Add file type tag
	if analysis.FileType != "" {
		tags = append(tags, analysis.FileType)
	}

	// Add directory-based tags
	dir := filepath.Dir(filePath)
	dirName := filepath.Base(dir)
	if dirName != "." && dirName != "/" {
		tags = append(tags, "dir:"+dirName)
	}

	// Add complexity tag
	if analysis.Complexity > 10 {
		tags = append(tags, "high-complexity")
	} else if analysis.Complexity > 5 {
		tags = append(tags, "medium-complexity")
	} else {
		tags = append(tags, "low-complexity")
	}

	return tags
}

// calculateSpecificity calculates context specificity based on analysis
func (e *DefaultIntelligenceEngine) calculateSpecificity(analysis *FileAnalysis) int {
	specificity := 1

	// More specific if it has many functions/classes
	if len(analysis.Functions) > 5 || len(analysis.Classes) > 3 {
		specificity += 2
	}

	// More specific if it has dependencies
	if len(analysis.Dependencies) > 0 {
		specificity += 1
	}

	// More specific if it's complex
	if analysis.Complexity > 10 {
		specificity += 1
	}

	return specificity
}

// determineEncryptionRoles determines which roles can access this context
func (e *DefaultIntelligenceEngine) determineEncryptionRoles(role string, confidence float64) []string {
	roles := []string{role}

	// Add senior roles that can access everything
	seniorRoles := []string{"senior_architect", "project_manager"}
	for _, senior := range seniorRoles {
		if senior != role {
			roles = append(roles, senior)
		}
	}

	// If high confidence, allow broader access
	if confidence > 0.8 {
		roles = append(roles, "*")
	}

	return roles
}

// determineAccessLevel determines the required access level for context
func (e *DefaultIntelligenceEngine) determineAccessLevel(analysis *FileAnalysis, role string) slurpContext.RoleAccessLevel {
	// Default to low access
	level := slurpContext.AccessLow

	// Increase level based on content sensitivity
	sensitive := false
	for _, comment := range analysis.Comments {
		if strings.Contains(strings.ToLower(comment), "password") ||
			strings.Contains(strings.ToLower(comment), "secret") ||
			strings.Contains(strings.ToLower(comment), "private") {
			sensitive = true
			break
		}
	}

	if sensitive {
		level = slurpContext.AccessHigh
	} else if len(analysis.Dependencies) > 5 {
		level = slurpContext.AccessMedium
	}

	return level
}

// generateContextSpecificInsights generates insights specific to the provided context
func (e *DefaultIntelligenceEngine) generateContextSpecificInsights(context *slurpContext.ContextNode, role string) []string {
	insights := []string{}

	// Technology-specific insights
	for _, tech := range context.Technologies {
		switch strings.ToLower(tech) {
		case "react", "vue", "angular":
			insights = append(insights, fmt.Sprintf("Frontend: %s component requires testing for accessibility and responsiveness", tech))
		case "go", "python", "java":
			insights = append(insights, fmt.Sprintf("Backend: %s code should follow language-specific best practices", tech))
		case "docker", "kubernetes":
			insights = append(insights, fmt.Sprintf("Infrastructure: %s configuration needs security review", tech))
		}
	}

	// Purpose-specific insights
	if strings.Contains(strings.ToLower(context.Purpose), "api") {
		insights = append(insights, "API: Consider rate limiting, authentication, and proper error responses")
	}
	if strings.Contains(strings.ToLower(context.Purpose), "database") {
		insights = append(insights, "Database: Review for proper indexing and query optimization")
	}

	return insights
}

// calculateGoalAlignment calculates alignment score between context and goal
func (e *DefaultIntelligenceEngine) calculateGoalAlignment(node *slurpContext.ContextNode, goal *ProjectGoal) float64 {
	score := 0.0
	checks := 0.0

	// Check keyword overlap
	nodeText := strings.ToLower(node.Summary + " " + node.Purpose + " " + strings.Join(node.Technologies, " "))
	for _, keyword := range goal.Keywords {
		checks += 1.0
		if strings.Contains(nodeText, strings.ToLower(keyword)) {
			score += 1.0
		}
	}

	// Check tag overlap
	for _, tag := range node.Tags {
		checks += 1.0
		for _, keyword := range goal.Keywords {
			if strings.Contains(strings.ToLower(tag), strings.ToLower(keyword)) {
				score += 0.5
				break
			}
		}
	}

	if checks == 0 {
		return 0.5 // Default score when no keywords to check
	}

	return score / checks
}

// updateStats updates engine statistics
func (e *DefaultIntelligenceEngine) updateStats(operation string, duration time.Duration, success bool) {
	e.mu.Lock()
	defer e.mu.Unlock()

	e.stats.TotalAnalyses++
	if success {
		e.stats.SuccessfulAnalyses++
	} else {
		e.stats.FailedAnalyses++
	}

	// Update average analysis time
	if e.stats.TotalAnalyses == 1 {
		e.stats.AverageAnalysisTime = duration
	} else {
		e.stats.AverageAnalysisTime = time.Duration(
			(int64(e.stats.AverageAnalysisTime)*(e.stats.TotalAnalyses-1) + int64(duration)) / e.stats.TotalAnalyses,
		)
	}

	// Update operation-specific stats
	switch operation {
	case "file_analysis":
		e.stats.FilesAnalyzed++
	case "directory_analysis":
		e.stats.DirectoriesAnalyzed++
	}
}

// calculateCacheHitRate calculates the current cache hit rate
func (e *DefaultIntelligenceEngine) calculateCacheHitRate() float64 {
	return e.stats.CacheHitRate // This would be calculated from cache access stats in a real implementation
}

// DefaultEngineConfig returns default configuration for the intelligence engine
func DefaultEngineConfig() *EngineConfig {
	return &EngineConfig{
		MaxConcurrentAnalysis:  4,
		AnalysisTimeout:        30 * time.Second,
		MaxFileSize:            10 * 1024 * 1024, // 10MB
		RAGEndpoint:            "",
		RAGTimeout:             10 * time.Second,
		RAGEnabled:             false,
		MinConfidenceThreshold: 0.6,
		RequireValidation:      true,
		CacheEnabled:           true,
		CacheTTL:               1 * time.Hour,
		RoleProfiles:           make(map[string]*RoleProfile),
		ProjectGoals:           []*ProjectGoal{},
	}
}