package intelligence

import (
	"context"
	"os"
	"path/filepath"
	"testing"
	"time"

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

func TestIntelligenceEngine_Integration(t *testing.T) {
	// Create test configuration
	config := &EngineConfig{
		EnableRAG:               false, // Disable RAG for testing
		EnableGoalAlignment:     true,
		EnablePatternDetection:  true,
		EnableRoleAware:         true,
		MaxConcurrentAnalysis:   2,
		AnalysisTimeout:         30 * time.Second,
		CacheTTL:               5 * time.Minute,
		MinConfidenceThreshold: 0.5,
	}

	// Create engine
	engine := NewIntelligenceEngine(config)
	ctx := context.Background()

	// Create test context node
	testNode := &slurpContext.ContextNode{
		Path:     "/test/example.go",
		Summary:  "A Go service implementing user authentication",
		Purpose:  "Handles user login and authentication for the web application",
		Technologies: []string{"go", "jwt", "bcrypt"},
		Tags:     []string{"authentication", "security", "web"},
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	// Create test project goal
	testGoal := &ProjectGoal{
		ID:          "auth_service",
		Name:        "Authentication Service",
		Description: "Build secure user authentication system",
		Keywords:    []string{"authentication", "security", "user", "login"},
		Priority:    1,
		Phase:       "development",
		Deadline:    nil,
		CreatedAt:   time.Now(),
	}

	t.Run("AnalyzeFile", func(t *testing.T) {
		content := []byte(`
		package main

		import (
			"context"
			"crypto/jwt"
			"golang.org/x/crypto/bcrypt"
		)

		func authenticateUser(username, password string) error {
			// Hash password and validate
			hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
			if err != nil {
				return err
			}
			return nil
		}
		`)

		analysis, err := engine.AnalyzeFile(ctx, testNode.Path, content)
		if err != nil {
			t.Fatalf("AnalyzeFile failed: %v", err)
		}

		if analysis.Language != "go" {
			t.Errorf("Expected language 'go', got '%s'", analysis.Language)
		}

		if len(analysis.Functions) == 0 {
			t.Error("Expected to find functions")
		}

		if analysis.Complexity <= 0 {
			t.Error("Expected positive complexity score")
		}
	})

	t.Run("AssessGoalAlignment", func(t *testing.T) {
		assessment, err := engine.AssessGoalAlignment(ctx, testNode, testGoal, "developer")
		if err != nil {
			t.Fatalf("AssessGoalAlignment failed: %v", err)
		}

		if assessment.OverallScore < 0 || assessment.OverallScore > 1 {
			t.Errorf("Expected score between 0-1, got %f", assessment.OverallScore)
		}

		if len(assessment.DimensionScores) == 0 {
			t.Error("Expected dimension scores")
		}

		if assessment.Confidence <= 0 {
			t.Error("Expected positive confidence")
		}
	})

	t.Run("ProcessForRole", func(t *testing.T) {
		processedNode, err := engine.ProcessForRole(ctx, testNode, "developer")
		if err != nil {
			t.Fatalf("ProcessForRole failed: %v", err)
		}

		if processedNode.ProcessedForRole != "developer" {
			t.Errorf("Expected processed for role 'developer', got '%s'", processedNode.ProcessedForRole)
		}

		if len(processedNode.RoleSpecificInsights) == 0 {
			t.Error("Expected role-specific insights")
		}
	})

	t.Run("DetectPatterns", func(t *testing.T) {
		content := []byte(`
		package main

		import "sync"

		type Singleton struct {
			instance *Singleton
			once     sync.Once
		}

		func GetInstance() *Singleton {
			s := &Singleton{}
			s.once.Do(func() {
				s.instance = &Singleton{}
			})
			return s.instance
		}
		`)

		patterns, err := engine.DetectCodePatterns(ctx, "/test/singleton.go", content)
		if err != nil {
			t.Fatalf("DetectPatterns failed: %v", err)
		}

		foundSingleton := false
		for _, pattern := range patterns {
			if pattern.Pattern.Name == "Singleton" {
				foundSingleton = true
				break
			}
		}

		if !foundSingleton {
			t.Error("Expected to detect Singleton pattern")
		}
	})

	t.Run("GenerateInsights", func(t *testing.T) {
		insights, err := engine.GenerateInsights(ctx, testNode, "developer")
		if err != nil {
			t.Fatalf("GenerateInsights failed: %v", err)
		}

		if len(insights) == 0 {
			t.Error("Expected to generate insights")
		}

		// Check insight quality
		for _, insight := range insights {
			if insight.Confidence <= 0 || insight.Confidence > 1 {
				t.Errorf("Invalid confidence score: %f", insight.Confidence)
			}
			if insight.Priority <= 0 {
				t.Errorf("Invalid priority: %d", insight.Priority)
			}
		}
	})
}

func TestFileAnalyzer_LanguageDetection(t *testing.T) {
	config := &EngineConfig{}
	analyzer := NewDefaultFileAnalyzer(config)
	ctx := context.Background()

	tests := []struct {
		filename string
		content  []byte
		expected string
	}{
		{"test.go", []byte("package main\nfunc main() {}"), "go"},
		{"test.js", []byte("function test() { return 42; }"), "javascript"},
		{"test.py", []byte("def test():\n    return 42"), "python"},
		{"test.java", []byte("public class Test { public static void main() {} }"), "java"},
		{"test.rs", []byte("fn main() { println!(\"Hello\"); }"), "rust"},
		{"unknown.txt", []byte("some text content"), "text"},
	}

	for _, tt := range tests {
		t.Run(tt.filename, func(t *testing.T) {
			analysis, err := analyzer.AnalyzeFile(ctx, tt.filename, tt.content)
			if err != nil {
				t.Fatalf("AnalyzeFile failed: %v", err)
			}

			if analysis.Language != tt.expected {
				t.Errorf("Expected language '%s', got '%s'", tt.expected, analysis.Language)
			}
		})
	}
}

func TestPatternDetector_DetectDesignPatterns(t *testing.T) {
	config := &EngineConfig{}
	detector := NewDefaultPatternDetector(config)
	ctx := context.Background()

	tests := []struct {
		name        string
		filename    string
		content     []byte
		expectedPattern string
	}{
		{
			name:     "Go Singleton Pattern",
			filename: "singleton.go",
			content: []byte(`
				package main
				import "sync"
				var instance *Singleton
				var once sync.Once
				func GetInstance() *Singleton {
					once.Do(func() {
						instance = &Singleton{}
					})
					return instance
				}
			`),
			expectedPattern: "Singleton",
		},
		{
			name:     "Go Factory Pattern",
			filename: "factory.go", 
			content: []byte(`
				package main
				func NewUser(name string) *User {
					return &User{Name: name}
				}
				func CreateConnection() Connection {
					return &dbConnection{}
				}
			`),
			expectedPattern: "Factory",
		},
		{
			name:     "JavaScript Observer Pattern",
			filename: "observer.js",
			content: []byte(`
				class EventEmitter {
					constructor() {
						this.events = {};
					}
					on(event, listener) {
						this.events[event] = this.events[event] || [];
						this.events[event].push(listener);
					}
					emit(event, data) {
						if (this.events[event]) {
							this.events[event].forEach(listener => listener(data));
						}
					}
				}
			`),
			expectedPattern: "Observer",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			patterns, err := detector.DetectCodePatterns(ctx, tt.filename, tt.content)
			if err != nil {
				t.Fatalf("DetectCodePatterns failed: %v", err)
			}

			found := false
			for _, pattern := range patterns {
				if pattern.Pattern.Name == tt.expectedPattern {
					found = true
					if pattern.Pattern.Confidence <= 0 {
						t.Errorf("Expected positive confidence, got %f", pattern.Pattern.Confidence)
					}
					break
				}
			}

			if !found {
				t.Errorf("Expected to find %s pattern", tt.expectedPattern)
			}
		})
	}
}

func TestGoalAlignment_DimensionCalculators(t *testing.T) {
	config := &EngineConfig{}
	engine := NewGoalAlignmentEngine(config)
	ctx := context.Background()

	testNode := &slurpContext.ContextNode{
		Path:         "/test/auth.go",
		Summary:      "User authentication service with JWT tokens",
		Purpose:      "Handles user login and token generation", 
		Technologies: []string{"go", "jwt", "bcrypt"},
		Tags:         []string{"authentication", "security"},
	}

	testGoal := &ProjectGoal{
		ID:          "auth_system",
		Name:        "Authentication System",
		Description: "Secure user authentication with JWT",
		Keywords:    []string{"authentication", "jwt", "security", "user"},
		Priority:    1,
		Phase:       "development",
	}

	t.Run("KeywordAlignment", func(t *testing.T) {
		calculator := NewKeywordAlignmentCalculator()
		score, err := calculator.Calculate(ctx, testNode, testGoal)
		if err != nil {
			t.Fatalf("Calculate failed: %v", err)
		}

		if score.Score <= 0 {
			t.Error("Expected positive keyword alignment score")
		}

		if len(score.Evidence) == 0 {
			t.Error("Expected evidence for keyword matches")
		}
	})

	t.Run("TechnologyAlignment", func(t *testing.T) {
		calculator := NewTechnologyAlignmentCalculator()
		score, err := calculator.Calculate(ctx, testNode, testGoal)
		if err != nil {
			t.Fatalf("Calculate failed: %v", err)
		}

		if score.Score <= 0 {
			t.Error("Expected positive technology alignment score")
		}
	})

	t.Run("FullAssessment", func(t *testing.T) {
		assessment, err := engine.AssessAlignment(ctx, testNode, testGoal, "developer")
		if err != nil {
			t.Fatalf("AssessAlignment failed: %v", err)
		}

		if assessment.OverallScore <= 0 {
			t.Error("Expected positive overall score")
		}

		if len(assessment.DimensionScores) == 0 {
			t.Error("Expected dimension scores")
		}

		// Verify all dimension scores are valid
		for _, dimScore := range assessment.DimensionScores {
			if dimScore.Score < 0 || dimScore.Score > 1 {
				t.Errorf("Invalid dimension score: %f for %s", dimScore.Score, dimScore.Dimension)
			}
			if dimScore.Confidence <= 0 || dimScore.Confidence > 1 {
				t.Errorf("Invalid confidence: %f for %s", dimScore.Confidence, dimScore.Dimension)
			}
		}
	})
}

func TestRoleAwareProcessor_Integration(t *testing.T) {
	config := &EngineConfig{}
	processor := NewRoleAwareProcessor(config)
	ctx := context.Background()

	testNode := &slurpContext.ContextNode{
		Path:         "/src/auth/service.go",
		Summary:      "Authentication service with password hashing and JWT generation",
		Purpose:      "Provides secure user authentication for the application",
		Technologies: []string{"go", "bcrypt", "jwt", "postgresql"},
		Tags:         []string{"authentication", "security", "database"},
		Insights:     []string{"Uses bcrypt for password hashing", "Implements JWT token generation"},
	}

	roles := []string{"architect", "developer", "security_analyst", "devops_engineer", "qa_engineer"}

	for _, roleID := range roles {
		t.Run("Role_"+roleID, func(t *testing.T) {
			// Test role-specific processing
			processedNode, err := processor.ProcessContextForRole(ctx, testNode, roleID)
			if err != nil {
				t.Fatalf("ProcessContextForRole failed for %s: %v", roleID, err)
			}

			if processedNode.ProcessedForRole != roleID {
				t.Errorf("Expected processed for role '%s', got '%s'", roleID, processedNode.ProcessedForRole)
			}

			// Test role-specific insight generation
			insights, err := processor.GenerateRoleSpecificInsights(ctx, testNode, roleID)
			if err != nil {
				t.Fatalf("GenerateRoleSpecificInsights failed for %s: %v", roleID, err)
			}

			if len(insights) == 0 {
				t.Errorf("Expected insights for role %s", roleID)
			}

			// Validate insight properties
			for _, insight := range insights {
				if insight.RoleID != roleID {
					t.Errorf("Expected insight for role %s, got %s", roleID, insight.RoleID)
				}
				if insight.Confidence <= 0 || insight.Confidence > 1 {
					t.Errorf("Invalid confidence: %f", insight.Confidence)
				}
			}

			// Test role-specific filtering
			filteredNode, err := processor.FilterContextForRole(testNode, roleID)
			if err != nil {
				t.Fatalf("FilterContextForRole failed for %s: %v", roleID, err)
			}

			// Verify filtering applied
			if filteredNode.Metadata == nil {
				t.Error("Expected metadata after filtering")
			} else {
				if filteredNode.Metadata["filtered_for_role"] != roleID {
					t.Errorf("Expected filtered_for_role to be %s", roleID)
				}
			}
		})
	}
}

func TestRoleAwareProcessor_AccessControl(t *testing.T) {
	config := &EngineConfig{}
	processor := NewRoleAwareProcessor(config)

	testCases := []struct {
		roleID   string
		action   string
		resource string
		expected bool
	}{
		{"architect", "context:read", "/src/architecture/design.go", true},
		{"developer", "context:write", "/src/auth/service.go", true},
		{"developer", "context:write", "/architecture/system.go", false},
		{"security_analyst", "context:read", "/src/security/auth.go", true},
		{"qa_engineer", "context:read", "/test/integration.go", true},
		{"qa_engineer", "context:write", "/src/production.go", false},
	}

	for _, tc := range testCases {
		t.Run(tc.roleID+"_"+tc.action+"_"+filepath.Base(tc.resource), func(t *testing.T) {
			err := processor.ValidateRoleAccess(tc.roleID, tc.action, tc.resource)
			hasAccess := err == nil

			if hasAccess != tc.expected {
				t.Errorf("Expected access %v for role %s, action %s, resource %s, got %v", 
					tc.expected, tc.roleID, tc.action, tc.resource, hasAccess)
			}
		})
	}
}

func TestDirectoryAnalyzer_StructureAnalysis(t *testing.T) {
	config := &EngineConfig{}
	analyzer := NewDefaultDirectoryAnalyzer(config)

	// Create temporary directory structure for testing
	tempDir, err := os.MkdirTemp("", "test_structure")
	if err != nil {
		t.Fatalf("Failed to create temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir)

	// Create test structure
	testDirs := []string{
		"src/main",
		"src/lib", 
		"test/unit",
		"test/integration",
		"docs/api",
		"config/dev",
		"deploy/k8s",
	}

	for _, dir := range testDirs {
		fullPath := filepath.Join(tempDir, dir)
		if err := os.MkdirAll(fullPath, 0755); err != nil {
			t.Fatalf("Failed to create directory %s: %v", fullPath, err)
		}
		
		// Create a dummy file in each directory
		testFile := filepath.Join(fullPath, "test.txt")
		if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil {
			t.Fatalf("Failed to create test file %s: %v", testFile, err)
		}
	}

	ctx := context.Background()
	analysis, err := analyzer.AnalyzeDirectory(ctx, tempDir)
	if err != nil {
		t.Fatalf("AnalyzeDirectory failed: %v", err)
	}

	if analysis.TotalFiles <= 0 {
		t.Error("Expected to find files")
	}

	if analysis.Depth <= 0 {
		t.Error("Expected positive directory depth")
	}

	if len(analysis.Structure) == 0 {
		t.Error("Expected directory structure information")
	}

	if len(analysis.Technologies) == 0 {
		t.Log("No technologies detected (expected for simple test structure)")
	}
}

// Benchmark tests for performance validation
func BenchmarkIntelligenceEngine_AnalyzeFile(b *testing.B) {
	config := &EngineConfig{EnableRAG: false}
	engine := NewIntelligenceEngine(config)
	ctx := context.Background()

	content := []byte(`
		package main
		import (
			"context"
			"fmt"
			"log"
		)
		
		func main() {
			fmt.Println("Hello, World!")
		}
		
		func processData(data []string) error {
			for _, item := range data {
				if err := validateItem(item); err != nil {
					return fmt.Errorf("validation failed: %w", err)
				}
			}
			return nil
		}
		
		func validateItem(item string) error {
			if len(item) == 0 {
				return fmt.Errorf("empty item")
			}
			return nil
		}
	`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := engine.AnalyzeFile(ctx, "test.go", content)
		if err != nil {
			b.Fatalf("AnalyzeFile failed: %v", err)
		}
	}
}

func BenchmarkPatternDetector_DetectPatterns(b *testing.B) {
	config := &EngineConfig{}
	detector := NewDefaultPatternDetector(config)
	ctx := context.Background()

	content := []byte(`
		package main
		import "sync"
		
		type Singleton struct {
			value string
		}
		
		var instance *Singleton
		var once sync.Once
		
		func GetInstance() *Singleton {
			once.Do(func() {
				instance = &Singleton{value: "initialized"}
			})
			return instance
		}
		
		func NewUser(name string) *User {
			return &User{Name: name}
		}
		
		func CreateDatabase() Database {
			return &postgresDatabase{}
		}
	`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := detector.DetectCodePatterns(ctx, "test.go", content)
		if err != nil {
			b.Fatalf("DetectCodePatterns failed: %v", err)
		}
	}
}

func BenchmarkRoleAwareProcessor_ProcessForRole(b *testing.B) {
	config := &EngineConfig{}
	processor := NewRoleAwareProcessor(config)
	ctx := context.Background()

	testNode := &slurpContext.ContextNode{
		Path:         "/src/service.go",
		Summary:      "A service implementation",
		Purpose:      "Handles business logic",
		Technologies: []string{"go", "postgresql"},
		Tags:         []string{"service", "database"},
		Insights:     []string{"Well structured code", "Good error handling"},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := processor.ProcessContextForRole(ctx, testNode, "developer")
		if err != nil {
			b.Fatalf("ProcessContextForRole failed: %v", err)
		}
	}
}

// Helper functions for testing

func createTestContextNode(path, summary, purpose string, technologies, tags []string) *slurpContext.ContextNode {
	return &slurpContext.ContextNode{
		Path:         path,
		Summary:      summary,
		Purpose:      purpose,
		Technologies: technologies,
		Tags:         tags,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}
}

func createTestProjectGoal(id, name, description string, keywords []string, priority int, phase string) *ProjectGoal {
	return &ProjectGoal{
		ID:          id,
		Name:        name,
		Description: description,
		Keywords:    keywords,
		Priority:    priority,
		Phase:       phase,
		CreatedAt:   time.Now(),
	}
}

func assertValidInsight(t *testing.T, insight *RoleSpecificInsight) {
	if insight.ID == "" {
		t.Error("Insight ID should not be empty")
	}
	if insight.RoleID == "" {
		t.Error("Insight RoleID should not be empty")
	}
	if insight.Confidence <= 0 || insight.Confidence > 1 {
		t.Errorf("Invalid confidence: %f", insight.Confidence)
	}
	if insight.Priority <= 0 {
		t.Errorf("Invalid priority: %d", insight.Priority)
	}
	if insight.Content == "" {
		t.Error("Insight content should not be empty")
	}
}

func assertValidDimensionScore(t *testing.T, score *DimensionScore) {
	if score.Dimension == "" {
		t.Error("Dimension name should not be empty")
	}
	if score.Score < 0 || score.Score > 1 {
		t.Errorf("Invalid dimension score: %f", score.Score)
	}
	if score.Confidence <= 0 || score.Confidence > 1 {
		t.Errorf("Invalid confidence: %f", score.Confidence)
	}
}