package composer

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/rs/zerolog/log"
)

// Service represents the Team Composer service
type Service struct {
	db            *pgxpool.Pool
	config        *ComposerConfig
	ollamaClient  *OllamaClient
}

// NewService creates a new Team Composer service
func NewService(db *pgxpool.Pool, config *ComposerConfig) *Service {
	if config == nil {
		config = DefaultComposerConfig()
	}

	// Initialize Ollama client for LLM operations
	ollamaClient := NewOllamaClient(config.ClassificationModel)

	return &Service{
		db:           db,
		config:       config,
		ollamaClient: ollamaClient,
	}
}

// AnalyzeAndComposeTeam performs complete task analysis and team composition
func (s *Service) AnalyzeAndComposeTeam(ctx context.Context, input *TaskAnalysisInput) (*CompositionResult, error) {
	startTime := time.Now()
	analysisID := uuid.New()
	
	log.Info().
		Str("analysis_id", analysisID.String()).
		Str("task_title", input.Title).
		Msg("Starting team composition analysis")
	
	// Step 1: Classify the task
	classification, err := s.classifyTask(ctx, input)
	if err != nil {
		return nil, fmt.Errorf("task classification failed: %w", err)
	}
	
	// Step 2: Analyze skill requirements
	skillRequirements, err := s.analyzeSkillRequirements(ctx, input, classification)
	if err != nil {
		return nil, fmt.Errorf("skill analysis failed: %w", err)
	}
	
	// Step 3: Get available agents
	agents, err := s.getAvailableAgents(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get available agents: %w", err)
	}
	
	// Step 4: Match agents to roles
	teamComposition, err := s.composeTeam(ctx, input, classification, skillRequirements, agents)
	if err != nil {
		return nil, fmt.Errorf("team composition failed: %w", err)
	}
	
	processingTime := time.Since(startTime).Milliseconds()
	
	result := &CompositionResult{
		AnalysisID:        analysisID,
		TaskInput:         input,
		Classification:    classification,
		SkillRequirements: skillRequirements,
		TeamComposition:   teamComposition,
		CreatedAt:         time.Now(),
		ProcessingTimeMs:  processingTime,
	}
	
	log.Info().
		Str("analysis_id", analysisID.String()).
		Int64("processing_time_ms", processingTime).
		Int("team_size", teamComposition.EstimatedSize).
		Float64("confidence", teamComposition.ConfidenceScore).
		Msg("Team composition analysis completed")
	
	return result, nil
}

// classifyTask analyzes the task and determines its characteristics
func (s *Service) classifyTask(ctx context.Context, input *TaskAnalysisInput) (*TaskClassification, error) {
	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Debug().
			Str("task_title", input.Title).
			Bool("llm_enabled", s.config.FeatureFlags.EnableLLMClassification).
			Msg("Starting task classification")
	}
	
	// Choose classification method based on feature flag
	if s.config.FeatureFlags.EnableLLMClassification {
		return s.classifyTaskWithLLM(ctx, input)
	}
	
	// Use heuristic-based classification (default/reliable path)
	return s.classifyTaskWithHeuristics(ctx, input)
}

// classifyTaskWithHeuristics uses rule-based classification for reliability
func (s *Service) classifyTaskWithHeuristics(ctx context.Context, input *TaskAnalysisInput) (*TaskClassification, error) {
	taskType := s.determineTaskType(input.Title, input.Description)
	complexity := s.estimateComplexity(input)
	domains := s.identifyDomains(input.TechStack, input.Requirements)
	
	classification := &TaskClassification{
		TaskType:           taskType,
		ComplexityScore:    complexity,
		PrimaryDomains:     domains[:min(len(domains), 3)], // Top 3 domains
		SecondaryDomains:   domains[min(len(domains), 3):], // Rest as secondary
		EstimatedDuration:  s.estimateDuration(complexity, len(input.Requirements)),
		RiskLevel:          s.assessRiskLevel(complexity, taskType),
		RequiredExperience: s.determineRequiredExperience(complexity, taskType),
	}
	
	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Debug().
			Str("task_type", string(taskType)).
			Float64("complexity", complexity).
			Strs("domains", domains).
			Msg("Task classified with heuristics")
	}
	
	return classification, nil
}

// classifyTaskWithLLM uses LLM-based classification for advanced analysis
func (s *Service) classifyTaskWithLLM(ctx context.Context, input *TaskAnalysisInput) (*TaskClassification, error) {
	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Info().
			Str("model", s.config.ClassificationModel).
			Msg("Using LLM for task classification")
	}

	// Create classification prompt
	prompt := s.ollamaClient.BuildTaskClassificationPrompt(input)

	// Set timeout for LLM operation
	llmCtx, cancel := context.WithTimeout(ctx, time.Duration(s.config.AnalysisTimeoutSecs)*time.Second)
	defer cancel()

	// Call Ollama API
	response, err := s.ollamaClient.Generate(llmCtx, prompt)
	if err != nil {
		if s.config.FeatureFlags.EnableFailsafeFallback {
			log.Warn().
				Err(err).
				Msg("LLM classification failed, falling back to heuristics")
			return s.classifyTaskWithHeuristics(ctx, input)
		}
		return nil, fmt.Errorf("LLM classification failed: %w", err)
	}

	// Parse LLM response
	classification, err := s.ollamaClient.ParseTaskClassificationResponse(response)
	if err != nil {
		if s.config.FeatureFlags.EnableFailsafeFallback {
			log.Warn().
				Err(err).
				Str("response", response).
				Msg("Failed to parse LLM classification response, falling back to heuristics")
			return s.classifyTaskWithHeuristics(ctx, input)
		}
		return nil, fmt.Errorf("failed to parse LLM classification: %w", err)
	}

	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Info().
			Str("task_type", string(classification.TaskType)).
			Float64("complexity", classification.ComplexityScore).
			Strs("primary_domains", classification.PrimaryDomains).
			Str("risk_level", classification.RiskLevel).
			Msg("Task classified with LLM")
	}

	return classification, nil
}

// determineTaskType uses heuristics to classify the task type
func (s *Service) determineTaskType(title, description string) TaskType {
	titleLower := strings.ToLower(title)
	descLower := strings.ToLower(description)
	combined := titleLower + " " + descLower
	
	// Bug fix patterns
	if strings.Contains(combined, "fix") || strings.Contains(combined, "bug") || 
	   strings.Contains(combined, "error") || strings.Contains(combined, "issue") {
		return TaskTypeBugFix
	}
	
	// Feature development patterns
	if strings.Contains(combined, "implement") || strings.Contains(combined, "add") ||
	   strings.Contains(combined, "create") || strings.Contains(combined, "build") {
		return TaskTypeFeatureDevelopment
	}
	
	// Refactoring patterns
	if strings.Contains(combined, "refactor") || strings.Contains(combined, "restructure") ||
	   strings.Contains(combined, "cleanup") || strings.Contains(combined, "improve") {
		return TaskTypeRefactoring
	}
	
	// Security patterns
	if strings.Contains(combined, "security") || strings.Contains(combined, "auth") ||
	   strings.Contains(combined, "encrypt") || strings.Contains(combined, "secure") {
		return TaskTypeSecurity
	}
	
	// Integration patterns
	if strings.Contains(combined, "integrate") || strings.Contains(combined, "connect") ||
	   strings.Contains(combined, "api") || strings.Contains(combined, "webhook") {
		return TaskTypeIntegration
	}
	
	// Default to feature development
	return TaskTypeFeatureDevelopment
}

// estimateComplexity calculates complexity score based on various factors
func (s *Service) estimateComplexity(input *TaskAnalysisInput) float64 {
	complexity := 0.3 // Base complexity
	
	// Factor in requirements count
	reqCount := len(input.Requirements)
	if reqCount > 10 {
		complexity += 0.3
	} else if reqCount > 5 {
		complexity += 0.2
	} else if reqCount > 2 {
		complexity += 0.1
	}
	
	// Factor in tech stack diversity
	techCount := len(input.TechStack)
	if techCount > 5 {
		complexity += 0.2
	} else if techCount > 3 {
		complexity += 0.1
	}
	
	// Factor in manual complexity if provided
	if input.Complexity > 0 {
		complexity = (complexity + input.Complexity) / 2
	}
	
	// Cap at 1.0
	if complexity > 1.0 {
		complexity = 1.0
	}
	
	return complexity
}

// identifyDomains extracts technical domains from tech stack and requirements
func (s *Service) identifyDomains(techStack, requirements []string) []string {
	domainMap := make(map[string]bool)
	
	// Map common technologies to domains
	techDomains := map[string][]string{
		"go":         {"backend", "systems"},
		"javascript": {"frontend", "web"},
		"react":      {"frontend", "web", "ui"},
		"node":       {"backend", "javascript"},
		"python":     {"backend", "data", "ml"},
		"docker":     {"devops", "containers"},
		"postgres":   {"database", "sql"},
		"redis":      {"cache", "database"},
		"git":        {"version_control"},
		"api":        {"backend", "integration"},
		"auth":       {"security", "backend"},
		"test":       {"testing", "quality"},
	}
	
	// Check tech stack
	for _, tech := range techStack {
		techLower := strings.ToLower(tech)
		if domains, exists := techDomains[techLower]; exists {
			for _, domain := range domains {
				domainMap[domain] = true
			}
		} else {
			// Add the tech itself as a domain if not mapped
			domainMap[techLower] = true
		}
	}
	
	// Check requirements for domain hints
	for _, req := range requirements {
		reqLower := strings.ToLower(req)
		for tech, domains := range techDomains {
			if strings.Contains(reqLower, tech) {
				for _, domain := range domains {
					domainMap[domain] = true
				}
			}
		}
	}
	
	// Convert map to slice
	domains := make([]string, 0, len(domainMap))
	for domain := range domainMap {
		domains = append(domains, domain)
	}
	
	return domains
}

// estimateDuration estimates hours needed based on complexity and requirements
func (s *Service) estimateDuration(complexity float64, requirementCount int) int {
	baseHours := 4 // Minimum estimation
	
	// Factor in complexity
	complexityHours := int(complexity * 16) // 0.0-1.0 maps to 0-16 hours
	
	// Factor in requirements
	reqHours := requirementCount * 2 // 2 hours per requirement on average
	
	total := baseHours + complexityHours + reqHours
	
	// Cap reasonable limits
	if total > 40 {
		total = 40 // Max 1 week for MVP
	}
	
	return total
}

// assessRiskLevel determines project risk based on complexity and type
func (s *Service) assessRiskLevel(complexity float64, taskType TaskType) string {
	// Base risk assessment
	if complexity > 0.8 {
		return "high"
	} else if complexity > 0.6 {
		return "medium"
	} else if complexity > 0.4 {
		return "low"
	} else {
		return "minimal"
	}
}

// determineRequiredExperience maps complexity and type to experience requirements
func (s *Service) determineRequiredExperience(complexity float64, taskType TaskType) string {
	// Security and integration tasks require more experience
	if taskType == TaskTypeSecurity {
		return "senior"
	}
	
	if complexity > 0.8 {
		return "senior"
	} else if complexity > 0.5 {
		return "intermediate"
	} else {
		return "junior"
	}
}

// analyzeSkillRequirements determines what skills are needed for the task
func (s *Service) analyzeSkillRequirements(ctx context.Context, input *TaskAnalysisInput, classification *TaskClassification) (*SkillRequirements, error) {
	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Debug().
			Str("task_title", input.Title).
			Bool("llm_enabled", s.config.FeatureFlags.EnableLLMSkillAnalysis).
			Msg("Starting skill requirements analysis")
	}
	
	// Choose analysis method based on feature flag
	if s.config.FeatureFlags.EnableLLMSkillAnalysis {
		return s.analyzeSkillRequirementsWithLLM(ctx, input, classification)
	}
	
	// Use heuristic-based analysis (default/reliable path)
	return s.analyzeSkillRequirementsWithHeuristics(ctx, input, classification)
}

// analyzeSkillRequirementsWithHeuristics uses rule-based skill analysis
func (s *Service) analyzeSkillRequirementsWithHeuristics(ctx context.Context, input *TaskAnalysisInput, classification *TaskClassification) (*SkillRequirements, error) {
	critical := []SkillRequirement{}
	desirable := []SkillRequirement{}
	
	// Map domains to skill requirements
	for _, domain := range classification.PrimaryDomains {
		skill := SkillRequirement{
			Domain:         domain,
			MinProficiency: 0.7, // High proficiency for primary domains
			Weight:         1.0,
			Critical:       true,
		}
		critical = append(critical, skill)
	}
	
	// Secondary domains as desirable skills
	for _, domain := range classification.SecondaryDomains {
		skill := SkillRequirement{
			Domain:         domain,
			MinProficiency: 0.5, // Moderate proficiency for secondary
			Weight:         0.6,
			Critical:       false,
		}
		desirable = append(desirable, skill)
	}
	
	// Add task-type specific skills
	switch classification.TaskType {
	case TaskTypeSecurity:
		critical = append(critical, SkillRequirement{
			Domain:         "security",
			MinProficiency: 0.8,
			Weight:         1.0,
			Critical:       true,
		})
	case TaskTypeBugFix:
		desirable = append(desirable, SkillRequirement{
			Domain:         "debugging",
			MinProficiency: 0.6,
			Weight:         0.8,
			Critical:       false,
		})
	}
	
	result := &SkillRequirements{
		CriticalSkills:  critical,
		DesirableSkills: desirable,
		TotalSkillCount: len(critical) + len(desirable),
	}
	
	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Debug().
			Int("critical_skills", len(critical)).
			Int("desirable_skills", len(desirable)).
			Msg("Skills analyzed with heuristics")
	}
	
	return result, nil
}

// analyzeSkillRequirementsWithLLM uses LLM-based skill analysis
func (s *Service) analyzeSkillRequirementsWithLLM(ctx context.Context, input *TaskAnalysisInput, classification *TaskClassification) (*SkillRequirements, error) {
	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Info().
			Str("model", s.config.SkillAnalysisModel).
			Msg("Using LLM for skill analysis")
	}

	// Create skill analysis prompt
	prompt := s.ollamaClient.BuildSkillAnalysisPrompt(input, classification)

	// Set timeout for LLM operation
	llmCtx, cancel := context.WithTimeout(ctx, time.Duration(s.config.AnalysisTimeoutSecs)*time.Second)
	defer cancel()

	// Call Ollama API (use skill analysis model if different from classification model)
	skillModel := s.config.SkillAnalysisModel
	if skillModel != s.ollamaClient.model {
		// Create a temporary client with the skill analysis model
		skillClient := NewOllamaClient(skillModel)
		response, err := skillClient.Generate(llmCtx, prompt)
		if err != nil {
			if s.config.FeatureFlags.EnableFailsafeFallback {
				log.Warn().
					Err(err).
					Msg("LLM skill analysis failed, falling back to heuristics")
				return s.analyzeSkillRequirementsWithHeuristics(ctx, input, classification)
			}
			return nil, fmt.Errorf("LLM skill analysis failed: %w", err)
		}

		// Parse LLM response
		skillRequirements, err := s.ollamaClient.ParseSkillRequirementsResponse(response)
		if err != nil {
			if s.config.FeatureFlags.EnableFailsafeFallback {
				log.Warn().
					Err(err).
					Str("response", response).
					Msg("Failed to parse LLM skill analysis response, falling back to heuristics")
				return s.analyzeSkillRequirementsWithHeuristics(ctx, input, classification)
			}
			return nil, fmt.Errorf("failed to parse LLM skill analysis: %w", err)
		}

		if s.config.FeatureFlags.EnableAnalysisLogging {
			log.Info().
				Int("critical_skills", len(skillRequirements.CriticalSkills)).
				Int("desirable_skills", len(skillRequirements.DesirableSkills)).
				Msg("Skills analyzed with LLM")
		}

		return skillRequirements, nil
	}

	// Use the same client if models are the same
	response, err := s.ollamaClient.Generate(llmCtx, prompt)
	if err != nil {
		if s.config.FeatureFlags.EnableFailsafeFallback {
			log.Warn().
				Err(err).
				Msg("LLM skill analysis failed, falling back to heuristics")
			return s.analyzeSkillRequirementsWithHeuristics(ctx, input, classification)
		}
		return nil, fmt.Errorf("LLM skill analysis failed: %w", err)
	}

	// Parse LLM response
	skillRequirements, err := s.ollamaClient.ParseSkillRequirementsResponse(response)
	if err != nil {
		if s.config.FeatureFlags.EnableFailsafeFallback {
			log.Warn().
				Err(err).
				Str("response", response).
				Msg("Failed to parse LLM skill analysis response, falling back to heuristics")
			return s.analyzeSkillRequirementsWithHeuristics(ctx, input, classification)
		}
		return nil, fmt.Errorf("failed to parse LLM skill analysis: %w", err)
	}

	if s.config.FeatureFlags.EnableAnalysisLogging {
		log.Info().
			Int("critical_skills", len(skillRequirements.CriticalSkills)).
			Int("desirable_skills", len(skillRequirements.DesirableSkills)).
			Msg("Skills analyzed with LLM")
	}

	return skillRequirements, nil
}

// getAvailableAgents retrieves agents that are available for assignment
func (s *Service) getAvailableAgents(ctx context.Context) ([]*Agent, error) {
	query := `
		SELECT id, name, endpoint_url, capabilities, status, last_seen, 
		       performance_metrics, created_at, updated_at
		FROM agents 
		WHERE status IN ('available', 'idle')
		ORDER BY last_seen DESC
	`
	
	rows, err := s.db.Query(ctx, query)
	if err != nil {
		return nil, fmt.Errorf("failed to query agents: %w", err)
	}
	defer rows.Close()
	
	var agents []*Agent
	
	for rows.Next() {
		agent := &Agent{}
		var capabilitiesJSON, metricsJSON []byte
		
		err := rows.Scan(
			&agent.ID, &agent.Name, &agent.EndpointURL, &capabilitiesJSON,
			&agent.Status, &agent.LastSeen, &metricsJSON,
			&agent.CreatedAt, &agent.UpdatedAt,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan agent row: %w", err)
		}
		
		// Parse JSON fields
		if len(capabilitiesJSON) > 0 {
			json.Unmarshal(capabilitiesJSON, &agent.Capabilities)
		}
		if len(metricsJSON) > 0 {
			json.Unmarshal(metricsJSON, &agent.PerformanceMetrics)
		}
		
		agents = append(agents, agent)
	}
	
	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating agent rows: %w", err)
	}
	
	log.Debug().
		Int("agent_count", len(agents)).
		Msg("Retrieved available agents")
	
	return agents, nil
}

// composeTeam creates the optimal team composition
func (s *Service) composeTeam(ctx context.Context, input *TaskAnalysisInput, classification *TaskClassification, 
	skillRequirements *SkillRequirements, agents []*Agent) (*TeamComposition, error) {
	
	// For MVP, use simple team composition strategy
	strategy := s.config.DefaultStrategy
	
	// Get available team roles
	roles, err := s.getTeamRoles(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get team roles: %w", err)
	}
	
	// Select roles based on task requirements
	requiredRoles := s.selectRequiredRoles(classification, skillRequirements, roles)
	
	// Match agents to roles
	agentMatches, confidence := s.matchAgentsToRoles(agents, requiredRoles, skillRequirements)
	
	teamID := uuid.New()
	teamName := fmt.Sprintf("Team-%s", input.Title)
	if len(teamName) > 50 {
		teamName = teamName[:47] + "..."
	}
	
	composition := &TeamComposition{
		TeamID:          teamID,
		Name:            teamName,
		Strategy:        strategy,
		RequiredRoles:   requiredRoles,
		OptionalRoles:   []*TeamRole{}, // MVP: no optional roles
		AgentMatches:    agentMatches,
		EstimatedSize:   len(agentMatches),
		ConfidenceScore: confidence,
	}
	
	return composition, nil
}

// getTeamRoles retrieves available team roles from database
func (s *Service) getTeamRoles(ctx context.Context) ([]*TeamRole, error) {
	query := `SELECT id, name, description, capabilities, created_at FROM team_roles ORDER BY name`
	
	rows, err := s.db.Query(ctx, query)
	if err != nil {
		return nil, fmt.Errorf("failed to query team roles: %w", err)
	}
	defer rows.Close()
	
	var roles []*TeamRole
	
	for rows.Next() {
		role := &TeamRole{}
		var capabilitiesJSON []byte
		
		err := rows.Scan(&role.ID, &role.Name, &role.Description, &capabilitiesJSON, &role.CreatedAt)
		if err != nil {
			return nil, fmt.Errorf("failed to scan role row: %w", err)
		}
		
		if len(capabilitiesJSON) > 0 {
			json.Unmarshal(capabilitiesJSON, &role.Capabilities)
		}
		
		roles = append(roles, role)
	}
	
	return roles, rows.Err()
}

// selectRequiredRoles determines which roles are needed for this task
func (s *Service) selectRequiredRoles(classification *TaskClassification, skillRequirements *SkillRequirements, availableRoles []*TeamRole) []*TeamRole {
	required := []*TeamRole{}
	
	// For MVP, simple role selection
	// Always need an executor
	for _, role := range availableRoles {
		if role.Name == "executor" {
			required = append(required, role)
			break
		}
	}
	
	// Add coordinator for complex tasks
	if classification.ComplexityScore > 0.7 {
		for _, role := range availableRoles {
			if role.Name == "coordinator" {
				required = append(required, role)
				break
			}
		}
	}
	
	// Add reviewer for high-risk tasks
	if classification.RiskLevel == "high" {
		for _, role := range availableRoles {
			if role.Name == "reviewer" {
				required = append(required, role)
				break
			}
		}
	}
	
	return required
}

// matchAgentsToRoles performs agent-to-role matching
func (s *Service) matchAgentsToRoles(agents []*Agent, roles []*TeamRole, skillRequirements *SkillRequirements) ([]*AgentMatch, float64) {
	matches := []*AgentMatch{}
	totalConfidence := 0.0
	
	// For MVP, simple first-available matching
	// In production, this would use sophisticated scoring algorithms
	
	usedAgents := make(map[uuid.UUID]bool)
	
	for _, role := range roles {
		bestMatch := s.findBestAgentForRole(agents, role, skillRequirements, usedAgents)
		if bestMatch != nil {
			matches = append(matches, bestMatch)
			usedAgents[bestMatch.Agent.ID] = true
			totalConfidence += bestMatch.OverallScore
		}
	}
	
	averageConfidence := totalConfidence / float64(len(matches))
	
	return matches, averageConfidence
}

// findBestAgentForRole finds the best available agent for a specific role
func (s *Service) findBestAgentForRole(agents []*Agent, role *TeamRole, skillRequirements *SkillRequirements, usedAgents map[uuid.UUID]bool) *AgentMatch {
	var bestMatch *AgentMatch
	bestScore := 0.0
	
	for _, agent := range agents {
		// Skip already used agents
		if usedAgents[agent.ID] {
			continue
		}
		
		// Calculate match score
		skillScore := s.calculateSkillMatch(agent, role, skillRequirements)
		availabilityScore := s.calculateAvailabilityScore(agent)
		experienceScore := s.calculateExperienceScore(agent)
		
		overallScore := (skillScore*0.5 + availabilityScore*0.3 + experienceScore*0.2)
		
		if overallScore > bestScore && overallScore >= s.config.SkillMatchThreshold {
			bestScore = overallScore
			bestMatch = &AgentMatch{
				Agent:             agent,
				Role:              role,
				OverallScore:      overallScore,
				SkillScore:        skillScore,
				AvailabilityScore: availabilityScore,
				ExperienceScore:   experienceScore,
				Reasoning:         fmt.Sprintf("Matched based on skill compatibility (%.2f) and availability (%.2f)", skillScore, availabilityScore),
				Confidence:        overallScore,
			}
		}
	}
	
	return bestMatch
}

// calculateSkillMatch determines how well an agent's skills match a role
func (s *Service) calculateSkillMatch(agent *Agent, role *TeamRole, skillRequirements *SkillRequirements) float64 {
	// Simple capability matching for MVP
	if agent.Capabilities == nil || role.Capabilities == nil {
		return 0.5 // Default moderate match
	}
	
	matchCount := 0
	totalCapabilities := 0
	
	// Check role capabilities against agent capabilities
	for capability := range role.Capabilities {
		totalCapabilities++
		if _, hasCapability := agent.Capabilities[capability]; hasCapability {
			matchCount++
		}
	}
	
	if totalCapabilities == 0 {
		return 0.5
	}
	
	return float64(matchCount) / float64(totalCapabilities)
}

// calculateAvailabilityScore assesses how available an agent is
func (s *Service) calculateAvailabilityScore(agent *Agent) float64 {
	switch agent.Status {
	case AgentStatusAvailable:
		return 1.0
	case AgentStatusIdle:
		return 0.9
	case AgentStatusBusy:
		return 0.3
	case AgentStatusOffline:
		return 0.0
	default:
		return 0.5
	}
}

// calculateExperienceScore evaluates agent experience from metrics
func (s *Service) calculateExperienceScore(agent *Agent) float64 {
	if agent.PerformanceMetrics == nil {
		return 0.5 // Default score for unknown experience
	}
	
	// Look for experience indicators in metrics
	if tasksCompleted, exists := agent.PerformanceMetrics["tasks_completed"]; exists {
		if count, ok := tasksCompleted.(float64); ok {
			// Scale task completion count to 0-1 score
			if count >= 10 {
				return 1.0
			} else if count >= 5 {
				return 0.8
			} else if count >= 1 {
				return 0.6
			}
		}
	}
	
	return 0.5
}

// CreateTeam persists a composed team to the database
func (s *Service) CreateTeam(ctx context.Context, composition *TeamComposition, taskInput *TaskAnalysisInput) (*Team, error) {
	tx, err := s.db.Begin(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer tx.Rollback(ctx)
	
	// Insert team record
	team := &Team{
		ID:          composition.TeamID,
		Name:        composition.Name,
		Description: fmt.Sprintf("Team for: %s", taskInput.Title),
		Status:      TeamStatusForming,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}
	
	insertTeamQuery := `
		INSERT INTO teams (id, name, description, status, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6)
	`
	
	_, err = tx.Exec(ctx, insertTeamQuery, team.ID, team.Name, team.Description, team.Status, team.CreatedAt, team.UpdatedAt)
	if err != nil {
		return nil, fmt.Errorf("failed to insert team: %w", err)
	}
	
	// Insert team assignments
	for _, match := range composition.AgentMatches {
		assignment := &TeamAssignment{
			ID:         uuid.New(),
			TeamID:     team.ID,
			AgentID:    match.Agent.ID,
			RoleID:     match.Role.ID,
			Status:     "active",
			AssignedAt: time.Now(),
		}
		
		insertAssignmentQuery := `
			INSERT INTO team_assignments (id, team_id, agent_id, role_id, status, assigned_at)
			VALUES ($1, $2, $3, $4, $5, $6)
		`
		
		_, err = tx.Exec(ctx, insertAssignmentQuery, 
			assignment.ID, assignment.TeamID, assignment.AgentID, 
			assignment.RoleID, assignment.Status, assignment.AssignedAt)
		if err != nil {
			return nil, fmt.Errorf("failed to insert team assignment: %w", err)
		}
	}
	
	if err = tx.Commit(ctx); err != nil {
		return nil, fmt.Errorf("failed to commit team creation: %w", err)
	}
	
	log.Info().
		Str("team_id", team.ID.String()).
		Str("team_name", team.Name).
		Int("members", len(composition.AgentMatches)).
		Msg("Team created successfully")
	
	return team, nil
}

// GetTeam retrieves a team with its assignments
func (s *Service) GetTeam(ctx context.Context, teamID uuid.UUID) (*Team, []*TeamAssignment, error) {
	// Get team info
	teamQuery := `
		SELECT id, name, description, status, task_id, gitea_issue_url, 
		       created_at, updated_at, completed_at
		FROM teams WHERE id = $1
	`
	
	row := s.db.QueryRow(ctx, teamQuery, teamID)
	team := &Team{}
	
	err := row.Scan(&team.ID, &team.Name, &team.Description, &team.Status, 
		&team.TaskID, &team.GiteaIssueURL, &team.CreatedAt, &team.UpdatedAt, &team.CompletedAt)
	
	if err != nil {
		if err == pgx.ErrNoRows {
			return nil, nil, fmt.Errorf("team not found")
		}
		return nil, nil, fmt.Errorf("failed to get team: %w", err)
	}
	
	// Get team assignments
	assignmentQuery := `
		SELECT id, team_id, agent_id, role_id, status, assigned_at, completed_at
		FROM team_assignments WHERE team_id = $1 ORDER BY assigned_at
	`
	
	rows, err := s.db.Query(ctx, assignmentQuery, teamID)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to query team assignments: %w", err)
	}
	defer rows.Close()
	
	var assignments []*TeamAssignment
	
	for rows.Next() {
		assignment := &TeamAssignment{}
		err := rows.Scan(&assignment.ID, &assignment.TeamID, &assignment.AgentID, 
			&assignment.RoleID, &assignment.Status, &assignment.AssignedAt, &assignment.CompletedAt)
		if err != nil {
			return nil, nil, fmt.Errorf("failed to scan assignment row: %w", err)
		}
		assignments = append(assignments, assignment)
	}
	
	return team, assignments, rows.Err()
}

// ListTeams retrieves all teams with pagination
func (s *Service) ListTeams(ctx context.Context, limit, offset int) ([]*Team, int, error) {
	// Get total count
	var total int
	countRow := s.db.QueryRow(ctx, `SELECT COUNT(*) FROM teams`)
	err := countRow.Scan(&total)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to count teams: %w", err)
	}
	
	// Get teams with pagination
	teamsQuery := `
		SELECT id, name, description, status, task_id, gitea_issue_url, 
		       created_at, updated_at, completed_at
		FROM teams 
		ORDER BY created_at DESC 
		LIMIT $1 OFFSET $2
	`
	
	rows, err := s.db.Query(ctx, teamsQuery, limit, offset)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to query teams: %w", err)
	}
	defer rows.Close()
	
	var teams []*Team
	
	for rows.Next() {
		team := &Team{}
		err := rows.Scan(&team.ID, &team.Name, &team.Description, &team.Status,
			&team.TaskID, &team.GiteaIssueURL, &team.CreatedAt, &team.UpdatedAt, &team.CompletedAt)
		if err != nil {
			return nil, 0, fmt.Errorf("failed to scan team row: %w", err)
		}
		teams = append(teams, team)
	}
	
	return teams, total, rows.Err()
}

// Public methods for testing (expose internal logic)

// DetermineTaskType exposes the internal task type determination logic
func (s *Service) DetermineTaskType(title, description string) TaskType {
	return s.determineTaskType(title, description)
}

// EstimateComplexity exposes the internal complexity estimation logic
func (s *Service) EstimateComplexity(input *TaskAnalysisInput) float64 {
	return s.estimateComplexity(input)
}

// IdentifyDomains exposes the internal domain identification logic
func (s *Service) IdentifyDomains(techStack, requirements []string) []string {
	return s.identifyDomains(techStack, requirements)
}

// EstimateDuration exposes the internal duration estimation logic
func (s *Service) EstimateDuration(complexity float64, requirementCount int) int {
	return s.estimateDuration(complexity, requirementCount)
}

// AssessRiskLevel exposes the internal risk assessment logic
func (s *Service) AssessRiskLevel(complexity float64, taskType TaskType) string {
	return s.assessRiskLevel(complexity, taskType)
}

// DetermineRequiredExperience exposes the internal experience requirement logic
func (s *Service) DetermineRequiredExperience(complexity float64, taskType TaskType) string {
	return s.determineRequiredExperience(complexity, taskType)
}

// AnalyzeSkillRequirementsLocal exposes skill analysis without database dependency
func (s *Service) AnalyzeSkillRequirementsLocal(input *TaskAnalysisInput, classification *TaskClassification) (*SkillRequirements, error) {
	return s.analyzeSkillRequirements(context.Background(), input, classification)
}

// Helper functions
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}