package agent

import (
	"fmt"
	"path/filepath"
	"strings"
	"sync"
)

// AgentRole represents different agent roles in the system
type AgentRole string

const (
	BackendRole   AgentRole = "backend"
	FrontendRole  AgentRole = "frontend"
	DevOpsRole    AgentRole = "devops"
	QARole        AgentRole = "qa"
	TestingRole   AgentRole = "testing"
	GeneralRole   AgentRole = "general"
)

// RoleCapability represents capabilities of an agent role
type RoleCapability struct {
	Name        string
	Description string
	Weight      float64
}

// DirectoryScope represents directory patterns for context filtering
type DirectoryScope struct {
	Patterns    []string
	Description string
}

// RoleConfig holds configuration for an agent role
type RoleConfig struct {
	Role            AgentRole
	DisplayName     string
	Description     string
	Capabilities    []RoleCapability
	DirectoryScopes DirectoryScope
	TaskTypes       []string
	Priority        int
	
	// Context filtering parameters
	ContextWeight   float64
	FeedbackWeight  float64
	LearningRate    float64
}

// RoleManager manages agent roles and their configurations
type RoleManager struct {
	roles    map[AgentRole]*RoleConfig
	agentRoles map[string]AgentRole // Maps agent ID to role
	mu       sync.RWMutex
}

// NewRoleManager creates a new role manager with default configurations
func NewRoleManager() *RoleManager {
	rm := &RoleManager{
		roles:      make(map[AgentRole]*RoleConfig),
		agentRoles: make(map[string]AgentRole),
	}
	
	rm.initializeDefaultRoles()
	return rm
}

// initializeDefaultRoles sets up default role configurations
func (rm *RoleManager) initializeDefaultRoles() {
	// Backend role configuration
	rm.roles[BackendRole] = &RoleConfig{
		Role:        BackendRole,
		DisplayName: "Backend Developer",
		Description: "Specializes in server-side development, APIs, databases, and backend services",
		Capabilities: []RoleCapability{
			{Name: "api_development", Description: "REST/GraphQL API development", Weight: 1.0},
			{Name: "database_design", Description: "Database schema and query optimization", Weight: 0.9},
			{Name: "server_architecture", Description: "Server architecture and microservices", Weight: 0.9},
			{Name: "authentication", Description: "Authentication and authorization systems", Weight: 0.8},
			{Name: "caching", Description: "Caching strategies and implementation", Weight: 0.8},
		},
		DirectoryScopes: DirectoryScope{
			Patterns: []string{
				"*/backend/*",
				"*/api/*",
				"*/services/*",
				"*/server/*",
				"*/core/*",
				"*/models/*",
				"*/controllers/*",
				"*/middleware/*",
			},
			Description: "Backend-related directories and server-side code",
		},
		TaskTypes: []string{
			"api_development",
			"database_migration",
			"backend_optimization",
			"server_configuration",
			"authentication_setup",
		},
		Priority: 5,
		ContextWeight: 1.0,
		FeedbackWeight: 0.3,
		LearningRate: 0.1,
	}

	// Frontend role configuration
	rm.roles[FrontendRole] = &RoleConfig{
		Role:        FrontendRole,
		DisplayName: "Frontend Developer",
		Description: "Specializes in user interfaces, client-side logic, and user experience",
		Capabilities: []RoleCapability{
			{Name: "ui_development", Description: "User interface development", Weight: 1.0},
			{Name: "responsive_design", Description: "Responsive and mobile-first design", Weight: 0.9},
			{Name: "state_management", Description: "Client-side state management", Weight: 0.8},
			{Name: "component_architecture", Description: "Component-based architecture", Weight: 0.9},
			{Name: "accessibility", Description: "Web accessibility implementation", Weight: 0.7},
		},
		DirectoryScopes: DirectoryScope{
			Patterns: []string{
				"*/frontend/*",
				"*/ui/*",
				"*/client/*",
				"*/web/*",
				"*/components/*",
				"*/pages/*",
				"*/styles/*",
				"*/assets/*",
			},
			Description: "Frontend-related directories and client-side code",
		},
		TaskTypes: []string{
			"ui_implementation",
			"component_development",
			"responsive_design",
			"frontend_optimization",
			"user_experience",
		},
		Priority: 4,
		ContextWeight: 0.8,
		FeedbackWeight: 0.3,
		LearningRate: 0.1,
	}

	// DevOps role configuration
	rm.roles[DevOpsRole] = &RoleConfig{
		Role:        DevOpsRole,
		DisplayName: "DevOps Engineer",
		Description: "Specializes in deployment, infrastructure, CI/CD, and system operations",
		Capabilities: []RoleCapability{
			{Name: "infrastructure", Description: "Infrastructure as Code", Weight: 1.0},
			{Name: "containerization", Description: "Docker and container orchestration", Weight: 0.9},
			{Name: "ci_cd", Description: "Continuous Integration/Deployment", Weight: 0.9},
			{Name: "monitoring", Description: "System monitoring and alerting", Weight: 0.8},
			{Name: "security", Description: "Security and compliance", Weight: 0.8},
		},
		DirectoryScopes: DirectoryScope{
			Patterns: []string{
				"*/deploy/*",
				"*/config/*",
				"*/docker/*",
				"*/k8s/*",
				"*/kubernetes/*",
				"*/infrastructure/*",
				"*/scripts/*",
				"*/ci/*",
				"*.yml",
				"*.yaml",
				"Dockerfile*",
				"docker-compose*",
			},
			Description: "DevOps-related configuration and deployment files",
		},
		TaskTypes: []string{
			"deployment",
			"infrastructure_setup",
			"ci_cd_pipeline",
			"system_monitoring",
			"security_configuration",
		},
		Priority: 5,
		ContextWeight: 1.0,
		FeedbackWeight: 0.4,
		LearningRate: 0.1,
	}

	// QA role configuration
	rm.roles[QARole] = &RoleConfig{
		Role:        QARole,
		DisplayName: "Quality Assurance",
		Description: "Specializes in quality assurance, code review, and process improvement",
		Capabilities: []RoleCapability{
			{Name: "code_review", Description: "Code review and quality assessment", Weight: 1.0},
			{Name: "process_improvement", Description: "Development process improvement", Weight: 0.9},
			{Name: "quality_metrics", Description: "Quality metrics and reporting", Weight: 0.8},
			{Name: "best_practices", Description: "Best practices enforcement", Weight: 0.9},
			{Name: "documentation", Description: "Documentation quality assurance", Weight: 0.7},
		},
		DirectoryScopes: DirectoryScope{
			Patterns: []string{
				"*/tests/*",
				"*/quality/*",
				"*/review/*",
				"*/docs/*",
				"*/documentation/*",
				"*", // QA role gets broader access for review purposes
			},
			Description: "All directories for quality assurance and code review",
		},
		TaskTypes: []string{
			"code_review",
			"quality_assessment",
			"process_improvement",
			"documentation_review",
			"compliance_check",
		},
		Priority: 4,
		ContextWeight: 0.7,
		FeedbackWeight: 0.5,
		LearningRate: 0.2,
	}

	// Testing role configuration
	rm.roles[TestingRole] = &RoleConfig{
		Role:        TestingRole,
		DisplayName: "Test Engineer",
		Description: "Specializes in automated testing, test frameworks, and test strategy",
		Capabilities: []RoleCapability{
			{Name: "unit_testing", Description: "Unit test development", Weight: 1.0},
			{Name: "integration_testing", Description: "Integration test development", Weight: 0.9},
			{Name: "e2e_testing", Description: "End-to-end test automation", Weight: 0.9},
			{Name: "test_frameworks", Description: "Test framework setup and maintenance", Weight: 0.8},
			{Name: "performance_testing", Description: "Performance and load testing", Weight: 0.7},
		},
		DirectoryScopes: DirectoryScope{
			Patterns: []string{
				"*/tests/*",
				"*/spec/*",
				"*/test/*",
				"*/e2e/*",
				"*/integration/*",
				"*/__tests__/*",
				"*.test.*",
				"*.spec.*",
			},
			Description: "Test-related directories and files",
		},
		TaskTypes: []string{
			"unit_testing",
			"integration_testing",
			"e2e_testing",
			"test_automation",
			"performance_testing",
		},
		Priority: 4,
		ContextWeight: 0.6,
		FeedbackWeight: 0.4,
		LearningRate: 0.15,
	}

	// General role configuration
	rm.roles[GeneralRole] = &RoleConfig{
		Role:        GeneralRole,
		DisplayName: "General Developer",
		Description: "General-purpose development with broad capabilities",
		Capabilities: []RoleCapability{
			{Name: "general_development", Description: "General software development", Weight: 0.7},
			{Name: "problem_solving", Description: "General problem solving", Weight: 0.8},
			{Name: "documentation", Description: "Documentation writing", Weight: 0.6},
			{Name: "code_maintenance", Description: "Code maintenance and refactoring", Weight: 0.7},
			{Name: "research", Description: "Technical research and analysis", Weight: 0.8},
		},
		DirectoryScopes: DirectoryScope{
			Patterns: []string{
				"*", // General role has access to all directories
			},
			Description: "All directories for general development tasks",
		},
		TaskTypes: []string{
			"general_development",
			"documentation",
			"code_maintenance",
			"research",
			"bug_fixes",
		},
		Priority: 2,
		ContextWeight: 0.5,
		FeedbackWeight: 0.2,
		LearningRate: 0.1,
	}
}

// AssignRole assigns a role to an agent
func (rm *RoleManager) AssignRole(agentID string, role AgentRole) error {
	rm.mu.Lock()
	defer rm.mu.Unlock()

	if _, exists := rm.roles[role]; !exists {
		return fmt.Errorf("role %s does not exist", role)
	}

	rm.agentRoles[agentID] = role
	return nil
}

// GetAgentRole returns the role assigned to an agent
func (rm *RoleManager) GetAgentRole(agentID string) (AgentRole, bool) {
	rm.mu.RLock()
	defer rm.mu.RUnlock()

	role, exists := rm.agentRoles[agentID]
	return role, exists
}

// GetRoleConfig returns the configuration for a specific role
func (rm *RoleManager) GetRoleConfig(role AgentRole) (*RoleConfig, bool) {
	rm.mu.RLock()
	defer rm.mu.RUnlock()

	config, exists := rm.roles[role]
	return config, exists
}

// GetAllRoles returns all available roles
func (rm *RoleManager) GetAllRoles() map[AgentRole]*RoleConfig {
	rm.mu.RLock()
	defer rm.mu.RUnlock()

	result := make(map[AgentRole]*RoleConfig)
	for role, config := range rm.roles {
		// Create a copy to avoid race conditions
		configCopy := *config
		result[role] = &configCopy
	}
	return result
}

// MatchesDirectoryScope checks if a directory path matches the agent's scope
func (rm *RoleManager) MatchesDirectoryScope(agentID, directory string) bool {
	role, exists := rm.GetAgentRole(agentID)
	if !exists {
		return false
	}

	config, exists := rm.GetRoleConfig(role)
	if !exists {
		return false
	}

	return rm.matchesPatterns(directory, config.DirectoryScopes.Patterns)
}

// GetRelevanceScore calculates context relevance score for an agent and directory
func (rm *RoleManager) GetRelevanceScore(agentID, directory string) float64 {
	role, exists := rm.GetAgentRole(agentID)
	if !exists {
		return 0.1 // Low default score
	}

	config, exists := rm.GetRoleConfig(role)
	if !exists {
		return 0.1
	}

	if rm.matchesPatterns(directory, config.DirectoryScopes.Patterns) {
		return config.ContextWeight
	}

	return 0.1 // Low score for non-matching directories
}

// matchesPatterns checks if a directory matches any of the given patterns
func (rm *RoleManager) matchesPatterns(directory string, patterns []string) bool {
	if directory == "" {
		return false
	}

	directory = strings.ToLower(directory)
	
	for _, pattern := range patterns {
		pattern = strings.ToLower(pattern)
		
		// Handle wildcard patterns
		if pattern == "*" {
			return true
		}
		
		// Handle glob-style patterns
		if matched, _ := filepath.Match(pattern, directory); matched {
			return true
		}
		
		// Handle substring matching for directory paths
		if strings.Contains(directory, strings.Trim(pattern, "*")) {
			return true
		}
	}
	
	return false
}

// UpdateRoleWeight updates the context weight for a role (for RL learning)
func (rm *RoleManager) UpdateRoleWeight(role AgentRole, newWeight float64) error {
	rm.mu.Lock()
	defer rm.mu.Unlock()

	config, exists := rm.roles[role]
	if !exists {
		return fmt.Errorf("role %s does not exist", role)
	}

	// Clamp weight to reasonable bounds
	if newWeight < 0.1 {
		newWeight = 0.1
	}
	if newWeight > 2.0 {
		newWeight = 2.0
	}

	config.ContextWeight = newWeight
	return nil
}

// GetAgentsByRole returns all agents assigned to a specific role
func (rm *RoleManager) GetAgentsByRole(role AgentRole) []string {
	rm.mu.RLock()
	defer rm.mu.RUnlock()

	var agents []string
	for agentID, agentRole := range rm.agentRoles {
		if agentRole == role {
			agents = append(agents, agentID)
		}
	}
	return agents
}

// GetCapabilitiesForRole returns capabilities for a specific role
func (rm *RoleManager) GetCapabilitiesForRole(role AgentRole) ([]RoleCapability, bool) {
	config, exists := rm.GetRoleConfig(role)
	if !exists {
		return nil, false
	}
	return config.Capabilities, true
}

// CanHandleTaskType checks if a role can handle a specific task type
func (rm *RoleManager) CanHandleTaskType(role AgentRole, taskType string) bool {
	config, exists := rm.GetRoleConfig(role)
	if !exists {
		return false
	}

	for _, supportedType := range config.TaskTypes {
		if supportedType == taskType {
			return true
		}
	}
	return false
}

// GetBestRoleForDirectory returns the best role for a given directory
func (rm *RoleManager) GetBestRoleForDirectory(directory string) (AgentRole, float64) {
	bestRole := GeneralRole
	bestScore := 0.0

	for role, config := range rm.roles {
		if rm.matchesPatterns(directory, config.DirectoryScopes.Patterns) {
			score := config.ContextWeight * float64(config.Priority) / 5.0
			if score > bestScore {
				bestScore = score
				bestRole = role
			}
		}
	}

	return bestRole, bestScore
}

// String returns string representation of AgentRole
func (ar AgentRole) String() string {
	return string(ar)
}

// IsValid checks if the agent role is valid
func (ar AgentRole) IsValid() bool {
	switch ar {
	case BackendRole, FrontendRole, DevOpsRole, QARole, TestingRole, GeneralRole:
		return true
	default:
		return false
	}
}