prediction.rs•42.9 kB
//! Approach prediction based on learned patterns and context
#[cfg(feature = "napi-bindings")]
use napi_derive::napi;
use crate::patterns::types::{ApproachPrediction, ProblemComplexity, GeneratedApproach, Pattern};
use crate::types::{ParseError, SemanticConcept};
use std::collections::{HashMap, HashSet};
use serde_json::{Value, from_str};
/// Predictor for suggesting coding approaches based on patterns and context
#[cfg_attr(feature = "napi-bindings", napi)]
pub struct ApproachPredictor {
    learned_patterns: HashMap<String, Pattern>,
    approach_templates: HashMap<String, ApproachTemplate>,
    context_weights: HashMap<String, f64>,
    historical_approaches: Vec<HistoricalApproach>,
}
#[derive(Debug, Clone)]
struct ApproachTemplate {
    name: String,
    description: String,
    complexity_suitability: Vec<ProblemComplexity>,
    required_patterns: Vec<String>,
    preferred_patterns: Vec<String>,
    technologies: Vec<String>,
    confidence_base: f64,
    // Fields accessed by update_templates_from_history
    confidence: f64,
    patterns: Vec<String>,
}
#[derive(Debug, Clone)]
struct HistoricalApproach {
    problem_description: String,
    approach_taken: String,
    patterns_used: Vec<String>,
    success_rating: f64,
    complexity: ProblemComplexity,
    context: HashMap<String, String>,
}
#[derive(Debug, Clone)]
struct ProblemContext {
    domain: String,
    scale: String,
    performance_requirements: String,
    maintainability_requirements: String,
    team_size: String,
    timeline: String,
    existing_patterns: Vec<String>,
    technologies: Vec<String>,
}
#[cfg_attr(feature = "napi-bindings", napi)]
impl ApproachPredictor {
    #[cfg_attr(feature = "napi-bindings", napi(constructor))]
    pub fn new() -> Self {
        let mut predictor = ApproachPredictor {
            learned_patterns: HashMap::new(),
            approach_templates: HashMap::new(),
            context_weights: HashMap::new(),
            historical_approaches: Vec::new(),
        };
        predictor.initialize_approach_templates();
        predictor.initialize_context_weights();
        predictor
    }
    /// Predict the best approach for a given problem description
    #[cfg_attr(feature = "napi-bindings", napi)]
    pub fn predict_approach(&self, problem_description: String, context_data: Option<String>) -> Result<ApproachPrediction, ParseError> {
        let complexity = self.analyze_problem_complexity(&problem_description);
        let context = self.parse_context_data(context_data.as_deref())?;
        let available_patterns = self.extract_available_patterns(&context);
        
        let candidates = self.generate_approach_candidates(&problem_description, &complexity, &context, &available_patterns);
        let best_approach = self.select_best_approach(candidates, &context);
        
        Ok(ApproachPrediction {
            approach: best_approach.description.clone(),
            confidence: best_approach.confidence,
            reasoning: self.generate_reasoning(&best_approach, &complexity, &context),
            patterns: self.extract_recommended_patterns(&best_approach),
            complexity: complexity.to_string(),
        })
    }
    /// Learn from historical approach data
    pub fn learn_from_approaches(&mut self, approach_data: &str) -> Result<bool, ParseError> {
        let historical_data: Value = from_str(approach_data)
            .map_err(|e| ParseError::from_reason(format!("Failed to parse approach data: {}", e)))?;
        
        if let Some(approaches) = historical_data.as_array() {
            for approach_value in approaches {
                if let Ok(historical_approach) = self.parse_historical_approach(approach_value) {
                    self.historical_approaches.push(historical_approach);
                    
                    // Update approach templates based on successful patterns
                    self.update_templates_from_history();
                }
            }
            Ok(true)
        } else {
            Ok(false)
        }
    }
    /// Update predictor with new pattern information
    pub fn update_patterns(&mut self, patterns: Vec<Pattern>) {
        for pattern in patterns {
            self.learned_patterns.insert(pattern.id.clone(), pattern);
        }
        
        // Recalculate approach template confidence based on new patterns
        self.recalculate_template_confidence();
    }
    /// Predict approach based on existing codebase analysis
    pub fn predict_from_codebase(&self, concepts: &[SemanticConcept], problem_description: &str) -> Result<ApproachPrediction, ParseError> {
        let context = self.analyze_codebase_context(concepts);
        let existing_patterns = self.identify_existing_patterns(concepts);
        let complexity = self.analyze_problem_complexity(problem_description);
        
        // Generate candidates that align with existing codebase patterns
        let candidates = self.generate_contextual_candidates(problem_description, &complexity, &context, &existing_patterns);
        let best_approach = self.select_best_approach(candidates, &context);
        
        Ok(ApproachPrediction {
            approach: best_approach.description.clone(),
            confidence: best_approach.confidence,
            reasoning: self.generate_contextual_reasoning(&best_approach, &existing_patterns, &context),
            patterns: existing_patterns,
            complexity: complexity.to_string(),
        })
    }
    /// Generate multiple approach alternatives
    pub fn generate_alternatives(&self, problem_description: &str, context_data: Option<&str>, count: usize) -> Result<Vec<ApproachPrediction>, ParseError> {
        let complexity = self.analyze_problem_complexity(problem_description);
        let context = self.parse_context_data(context_data)?;
        let available_patterns = self.extract_available_patterns(&context);
        
        let mut candidates = self.generate_approach_candidates(problem_description, &complexity, &context, &available_patterns);
        
        // Sort by confidence and take top N
        candidates.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap_or(std::cmp::Ordering::Equal));
        candidates.truncate(count);
        
        let alternatives: Result<Vec<_>, _> = candidates.into_iter()
            .map(|approach| Ok(ApproachPrediction {
                approach: approach.description.clone(),
                confidence: approach.confidence,
                reasoning: self.generate_reasoning(&approach, &complexity, &context),
                patterns: self.extract_recommended_patterns(&approach),
                complexity: complexity.to_string(),
            }))
            .collect();
            
        alternatives
    }
    /// Initialize approach templates
    fn initialize_approach_templates(&mut self) {
        // Microservices Architecture
        self.approach_templates.insert("microservices".to_string(), ApproachTemplate {
            name: "Microservices Architecture".to_string(),
            description: "Decompose into loosely coupled, independently deployable services".to_string(),
            complexity_suitability: vec![ProblemComplexity::Medium, ProblemComplexity::High],
            required_patterns: vec!["service_boundaries".to_string(), "api_gateway".to_string()],
            preferred_patterns: vec!["event_driven".to_string(), "database_per_service".to_string()],
            technologies: vec!["docker".to_string(), "kubernetes".to_string(), "rest_api".to_string()],
            confidence_base: 0.8,
            confidence: 0.8,
            patterns: vec!["service_boundaries".to_string(), "api_gateway".to_string()],
        });
        // Monolithic Architecture  
        self.approach_templates.insert("monolith".to_string(), ApproachTemplate {
            name: "Modular Monolith".to_string(),
            description: "Single deployable unit with clear internal module boundaries".to_string(),
            complexity_suitability: vec![ProblemComplexity::Low, ProblemComplexity::Medium],
            required_patterns: vec!["layered_architecture".to_string()],
            preferred_patterns: vec!["dependency_injection".to_string(), "domain_driven_design".to_string()],
            technologies: vec!["mvc".to_string(), "orm".to_string()],
            confidence_base: 0.7,
            confidence: 0.7,
            patterns: vec!["layered_architecture".to_string()],
        });
        // Event-Driven Architecture
        self.approach_templates.insert("event_driven".to_string(), ApproachTemplate {
            name: "Event-Driven Architecture".to_string(),
            description: "Asynchronous communication through events and message queues".to_string(),
            complexity_suitability: vec![ProblemComplexity::Medium, ProblemComplexity::High],
            required_patterns: vec!["event_sourcing".to_string(), "publisher_subscriber".to_string()],
            preferred_patterns: vec!["saga_pattern".to_string(), "cqrs".to_string()],
            technologies: vec!["message_queue".to_string(), "event_store".to_string()],
            confidence_base: 0.75,
            confidence: 0.75,
            patterns: vec!["event_sourcing".to_string(), "publisher_subscriber".to_string()],
        });
        // Serverless Architecture
        self.approach_templates.insert("serverless".to_string(), ApproachTemplate {
            name: "Serverless Architecture".to_string(),
            description: "Function-based architecture with managed infrastructure".to_string(),
            complexity_suitability: vec![ProblemComplexity::Low, ProblemComplexity::Medium],
            required_patterns: vec!["function_as_service".to_string()],
            preferred_patterns: vec!["api_gateway".to_string(), "event_triggers".to_string()],
            technologies: vec!["aws_lambda".to_string(), "azure_functions".to_string(), "api_gateway".to_string()],
            confidence_base: 0.6,
            confidence: 0.6,
            patterns: vec!["function_as_service".to_string()],
        });
        // Clean Architecture
        self.approach_templates.insert("clean_architecture".to_string(), ApproachTemplate {
            name: "Clean Architecture".to_string(),
            description: "Dependency inversion with clear separation of concerns".to_string(),
            complexity_suitability: vec![ProblemComplexity::Medium, ProblemComplexity::High],
            required_patterns: vec!["dependency_inversion".to_string(), "use_cases".to_string()],
            preferred_patterns: vec!["repository_pattern".to_string(), "domain_entities".to_string()],
            technologies: vec!["dependency_injection".to_string(), "testing_framework".to_string()],
            confidence_base: 0.85,
            confidence: 0.85,
            patterns: vec!["dependency_inversion".to_string(), "use_cases".to_string()],
        });
        // CRUD Application
        self.approach_templates.insert("crud".to_string(), ApproachTemplate {
            name: "CRUD Application".to_string(),
            description: "Simple Create, Read, Update, Delete operations with standard patterns".to_string(),
            complexity_suitability: vec![ProblemComplexity::Low],
            required_patterns: vec!["mvc".to_string(), "repository".to_string()],
            preferred_patterns: vec!["validation".to_string(), "orm".to_string()],
            technologies: vec!["database".to_string(), "web_framework".to_string()],
            confidence_base: 0.9,
            confidence: 0.9,
            patterns: vec!["mvc".to_string(), "repository".to_string()],
        });
    }
    /// Initialize context weights for decision making
    fn initialize_context_weights(&mut self) {
        self.context_weights.insert("performance".to_string(), 0.25);
        self.context_weights.insert("scalability".to_string(), 0.2);
        self.context_weights.insert("maintainability".to_string(), 0.2);
        self.context_weights.insert("team_experience".to_string(), 0.15);
        self.context_weights.insert("timeline".to_string(), 0.1);
        self.context_weights.insert("budget".to_string(), 0.1);
    }
    /// Analyze problem complexity from description
    fn analyze_problem_complexity(&self, problem_description: &str) -> ProblemComplexity {
        let description_lower = problem_description.to_lowercase();
        
        let high_complexity_indicators = [
            "distributed", "microservices", "real-time", "high-throughput", "scalable",
            "multiple systems", "complex business rules", "enterprise", "multi-tenant",
            "event-driven", "asynchronous", "concurrent", "parallel processing",
        ];
        
        let medium_complexity_indicators = [
            "api", "database", "user management", "authentication", "integration",
            "business logic", "workflows", "reporting", "analytics", "modular",
        ];
        
        let high_score = high_complexity_indicators.iter()
            .filter(|&indicator| description_lower.contains(indicator))
            .count();
            
        let medium_score = medium_complexity_indicators.iter()
            .filter(|&indicator| description_lower.contains(indicator))
            .count();
        
        if high_score >= 2 || description_lower.len() > 500 {
            ProblemComplexity::High
        } else if medium_score >= 2 || high_score >= 1 || description_lower.len() > 200 {
            ProblemComplexity::Medium
        } else {
            ProblemComplexity::Low
        }
    }
    /// Parse context data from JSON string
    fn parse_context_data(&self, context_data: Option<&str>) -> Result<ProblemContext, ParseError> {
        let default_context = ProblemContext {
            domain: "general".to_string(),
            scale: "medium".to_string(),
            performance_requirements: "standard".to_string(),
            maintainability_requirements: "high".to_string(),
            team_size: "small".to_string(),
            timeline: "months".to_string(),
            existing_patterns: Vec::new(),
            technologies: Vec::new(),
        };
        
        if let Some(data) = context_data {
            let parsed: Value = from_str(data)
                .map_err(|e| ParseError::from_reason(format!("Failed to parse context: {}", e)))?;
            
            Ok(ProblemContext {
                domain: parsed.get("domain").and_then(|v| v.as_str()).unwrap_or("general").to_string(),
                scale: parsed.get("scale").and_then(|v| v.as_str()).unwrap_or("medium").to_string(),
                performance_requirements: parsed.get("performance").and_then(|v| v.as_str()).unwrap_or("standard").to_string(),
                maintainability_requirements: parsed.get("maintainability").and_then(|v| v.as_str()).unwrap_or("high").to_string(),
                team_size: parsed.get("team_size").and_then(|v| v.as_str()).unwrap_or("small").to_string(),
                timeline: parsed.get("timeline").and_then(|v| v.as_str()).unwrap_or("months").to_string(),
                existing_patterns: parsed.get("existing_patterns")
                    .and_then(|v| v.as_array())
                    .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect())
                    .unwrap_or_default(),
                technologies: parsed.get("technologies")
                    .and_then(|v| v.as_array())
                    .map(|arr| arr.iter().filter_map(|v| v.as_str().map(String::from)).collect())
                    .unwrap_or_default(),
            })
        } else {
            Ok(default_context)
        }
    }
    /// Extract available patterns from context
    fn extract_available_patterns(&self, context: &ProblemContext) -> Vec<String> {
        let mut patterns = context.existing_patterns.clone();
        
        // Infer patterns from technologies
        for tech in &context.technologies {
            match tech.to_lowercase().as_str() {
                "react" | "vue" | "angular" => patterns.push("component_based".to_string()),
                "express" | "fastapi" | "spring" => patterns.push("mvc".to_string()),
                "docker" | "kubernetes" => patterns.push("containerization".to_string()),
                "redis" | "rabbitmq" | "kafka" => patterns.push("event_driven".to_string()),
                "graphql" => patterns.push("api_gateway".to_string()),
                _ => {}
            }
        }
        
        patterns.extend(self.learned_patterns.keys().cloned());
        patterns.sort();
        patterns.dedup();
        patterns
    }
    /// Generate approach candidates
    fn generate_approach_candidates(
        &self,
        problem_description: &str,
        complexity: &ProblemComplexity,
        context: &ProblemContext,
        available_patterns: &[String],
    ) -> Vec<GeneratedApproach> {
        let mut candidates = Vec::new();
        
        for template in self.approach_templates.values() {
            if template.complexity_suitability.contains(complexity) {
                let confidence = self.calculate_template_confidence(template, context, available_patterns);
                
                if confidence > 0.3 {
                    candidates.push(GeneratedApproach {
                        description: format!("{}: {}", template.name, template.description),
                        confidence,
                        reasoning: self.generate_template_reasoning(template, context, available_patterns),
                    });
                }
            }
        }
        
        // Add custom approaches based on historical data
        candidates.extend(self.generate_historical_candidates(problem_description, complexity, context));
        
        candidates
    }
    /// Calculate confidence for a template
    fn calculate_template_confidence(
        &self,
        template: &ApproachTemplate,
        context: &ProblemContext,
        available_patterns: &[String],
    ) -> f64 {
        let mut confidence = template.confidence_base;
        
        // Adjust for required patterns availability
        let required_available = template.required_patterns.iter()
            .filter(|&pattern| available_patterns.contains(pattern))
            .count() as f64;
        let required_ratio = if template.required_patterns.is_empty() { 
            1.0 
        } else { 
            required_available / template.required_patterns.len() as f64 
        };
        confidence *= required_ratio;
        
        // Boost for preferred patterns
        let preferred_available = template.preferred_patterns.iter()
            .filter(|&pattern| available_patterns.contains(pattern))
            .count() as f64;
        let preferred_boost = preferred_available * 0.1;
        confidence += preferred_boost;
        
        // Adjust for context factors
        confidence *= self.calculate_context_multiplier(template, context);
        
        confidence.min(1.0)
    }
    /// Calculate context multiplier
    fn calculate_context_multiplier(&self, template: &ApproachTemplate, context: &ProblemContext) -> f64 {
        let mut multiplier = 1.0;
        
        // Scale considerations
        match (template.name.as_str(), context.scale.as_str()) {
            ("Microservices Architecture", "large") => multiplier *= 1.2,
            ("Microservices Architecture", "small") => multiplier *= 0.7,
            ("Modular Monolith", "small") | ("Modular Monolith", "medium") => multiplier *= 1.1,
            ("CRUD Application", "small") => multiplier *= 1.3,
            ("CRUD Application", "large") => multiplier *= 0.5,
            _ => {}
        }
        
        // Performance considerations
        if context.performance_requirements == "high" {
            match template.name.as_str() {
                "Event-Driven Architecture" => multiplier *= 1.1,
                "Serverless Architecture" => multiplier *= 0.8,
                _ => {}
            }
        }
        
        // Team size considerations
        if context.team_size == "large" && template.name == "Clean Architecture" {
            multiplier *= 1.2;
        }
        // Domain-specific adjustments
        match (template.name.as_str(), context.domain.as_str()) {
            ("Microservices Architecture", "enterprise") => multiplier *= 1.15,
            ("CRUD Application", "prototype") => multiplier *= 1.2,
            ("Event-Driven Architecture", "real_time") => multiplier *= 1.3,
            ("Serverless Architecture", "prototype") => multiplier *= 1.1,
            ("Clean Architecture", "long_term_project") => multiplier *= 1.2,
            _ => {}
        }
        // Maintainability requirements
        match context.maintainability_requirements.as_str() {
            "high" => {
                match template.name.as_str() {
                    "Clean Architecture" => multiplier *= 1.25,
                    "Modular Monolith" => multiplier *= 1.1,
                    _ => {}
                }
            },
            "low" => {
                match template.name.as_str() {
                    "CRUD Application" => multiplier *= 1.1,
                    "Serverless Architecture" => multiplier *= 1.05,
                    _ => {}
                }
            },
            _ => {}
        }
        // Timeline considerations
        match context.timeline.as_str() {
            "urgent" | "short" => {
                match template.name.as_str() {
                    "CRUD Application" => multiplier *= 1.3,
                    "Serverless Architecture" => multiplier *= 1.15,
                    "Microservices Architecture" => multiplier *= 0.7, // Complex to implement quickly
                    _ => {}
                }
            },
            "long_term" | "ongoing" => {
                match template.name.as_str() {
                    "Clean Architecture" => multiplier *= 1.2,
                    "Microservices Architecture" => multiplier *= 1.1,
                    "CRUD Application" => multiplier *= 0.8, // Less suitable for long-term
                    _ => {}
                }
            },
            _ => {}
        }
        
        multiplier
    }
    /// Select best approach from candidates
    fn select_best_approach(&self, mut candidates: Vec<GeneratedApproach>, _context: &ProblemContext) -> GeneratedApproach {
        candidates.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap_or(std::cmp::Ordering::Equal));
        
        candidates.into_iter().next().unwrap_or_else(|| GeneratedApproach {
            description: "Standard layered architecture with clear separation of concerns".to_string(),
            confidence: 0.5,
            reasoning: "Default approach when no specific patterns are identified".to_string(),
        })
    }
    /// Generate reasoning for an approach
    fn generate_reasoning(&self, approach: &GeneratedApproach, complexity: &ProblemComplexity, context: &ProblemContext) -> String {
        let mut reasoning = vec![
            format!("Problem complexity: {}", complexity),
            format!("Approach confidence: {:.1}%", approach.confidence * 100.0),
        ];
        
        reasoning.push(approach.reasoning.clone());
        
        if context.performance_requirements == "high" {
            reasoning.push("High performance requirements favor this approach".to_string());
        }
        
        if context.scale == "large" {
            reasoning.push("Large scale requirements support this architectural choice".to_string());
        }
        
        reasoning.join(". ")
    }
    /// Generate contextual reasoning based on existing codebase
    fn generate_contextual_reasoning(&self, approach: &GeneratedApproach, existing_patterns: &[String], context: &ProblemContext) -> String {
        let mut reasoning = vec![
            approach.reasoning.clone(),
            format!("Existing codebase patterns: {}", existing_patterns.join(", ")),
        ];
        
        if !existing_patterns.is_empty() {
            reasoning.push("Recommendation aligns with existing architectural patterns".to_string());
        }
        
        reasoning.push(format!("Context scale: {}, team size: {}", context.scale, context.team_size));
        
        reasoning.join(". ")
    }
    /// Extract recommended patterns from approach
    fn extract_recommended_patterns(&self, approach: &GeneratedApproach) -> Vec<String> {
        let mut patterns = Vec::new();
        
        // Extract patterns mentioned in the approach description
        for template in self.approach_templates.values() {
            if approach.description.contains(&template.name) {
                patterns.extend(template.required_patterns.clone());
                patterns.extend(template.preferred_patterns.clone());
                break;
            }
        }
        
        patterns.sort();
        patterns.dedup();
        patterns
    }
    /// Additional helper methods for historical data and codebase analysis
    fn parse_historical_approach(&self, value: &Value) -> Result<HistoricalApproach, ParseError> {
        let problem = value.get("problem").and_then(|v| v.as_str()).unwrap_or("").to_string();
        let approach = value.get("approach").and_then(|v| v.as_str()).unwrap_or("").to_string();
        let success = value.get("success").and_then(|v| v.as_f64()).unwrap_or(0.5);
        let complexity_str = value.get("complexity").and_then(|v| v.as_str()).unwrap_or("medium");
        
        let complexity = match complexity_str {
            "low" => ProblemComplexity::Low,
            "high" => ProblemComplexity::High,
            _ => ProblemComplexity::Medium,
        };
        
        Ok(HistoricalApproach {
            problem_description: problem,
            approach_taken: approach,
            patterns_used: Vec::new(),
            success_rating: success,
            complexity,
            context: HashMap::new(),
        })
    }
    fn update_templates_from_history(&mut self) {
        // Collect template descriptions first to avoid borrowing conflicts
        let template_descriptions: Vec<(String, String)> = self.approach_templates
            .iter()
            .map(|(name, template)| (name.clone(), template.description.clone()))
            .collect();
        // Analyze historical approaches to update template confidence based on success ratings
        for historical in &self.historical_approaches {
            // Use problem_description for internal consistency check
            let problem_approach_alignment = self.calculate_approach_similarity(&historical.problem_description, &historical.approach_taken);
            
            // Find templates that match this historical approach's patterns
            for (template_name, template_desc) in &template_descriptions {
                let approach_similarity = self.calculate_approach_similarity(&historical.approach_taken, template_desc);
                let problem_match_bonus = self.calculate_approach_similarity(&historical.problem_description, template_desc) * 0.3;
                // Factor in how well the problem aligned with the chosen approach
                let alignment_bonus = problem_approach_alignment * 0.1;
                let total_similarity = approach_similarity + problem_match_bonus + alignment_bonus;
                
                if total_similarity > 0.6 {  // Similar approaches
                    if let Some(template) = self.approach_templates.get_mut(template_name) {
                        // Check if template complexity matches historical complexity
                        let complexity_match = template.complexity_suitability.contains(&historical.complexity);
                        let complexity_bonus = if complexity_match { 0.1 } else { -0.05 };
                        
                        // Adjust confidence based on historical success and complexity matching
                        let base_adjustment = (historical.success_rating - 0.5) * 0.2;
                        let final_adjustment = base_adjustment + complexity_bonus;
                        template.confidence = (template.confidence + final_adjustment).clamp(0.1, 1.0);
                        
                        // Add patterns from successful approaches
                        if historical.success_rating > 0.7 {
                            for pattern in &historical.patterns_used {
                                if !template.patterns.contains(pattern) {
                                    template.patterns.push(pattern.clone());
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    fn calculate_approach_similarity(&self, approach1: &str, approach2: &str) -> f64 {
        // Simple similarity calculation based on common words
        let approach1_lower = approach1.to_lowercase();
        let approach2_lower = approach2.to_lowercase();
        let words1: HashSet<&str> = approach1_lower.split_whitespace().collect();
        let words2: HashSet<&str> = approach2_lower.split_whitespace().collect();
        
        let intersection = words1.intersection(&words2).count();
        let union = words1.union(&words2).count();
        
        if union == 0 { 0.0 } else { intersection as f64 / union as f64 }
    }
    fn recalculate_template_confidence(&mut self) {
        // Recalculate base confidence based on available patterns
        // Implementation depends on specific requirements
    }
    fn analyze_codebase_context(&self, concepts: &[SemanticConcept]) -> ProblemContext {
        let mut technologies = HashSet::new();
        let mut patterns = HashSet::new();
        
        // Extract technologies and patterns from file paths and names
        for concept in concepts {
            if concept.file_path.contains("test") {
                technologies.insert("testing".to_string());
            }
            if concept.file_path.contains("api") {
                patterns.insert("api".to_string());
            }
            if concept.name.contains("Service") {
                patterns.insert("service_layer".to_string());
            }
            if concept.name.contains("Repository") {
                patterns.insert("repository".to_string());
            }
        }
        
        let scale = if concepts.len() > 100 {
            "large"
        } else if concepts.len() > 50 {
            "medium"
        } else {
            "small"
        };
        
        // Use historical approaches to inform context decisions
        let mut maintainability = "high".to_string();
        let mut domain = "existing_codebase".to_string();
        let mut timeline = "ongoing".to_string();
        
        // Learn from successful historical approaches with similar complexity
        for historical in &self.historical_approaches {
            if historical.success_rating > 0.7 {  // Only learn from successful approaches
                if let Some(hist_domain) = historical.context.get("domain") {
                    domain = hist_domain.clone();
                }
                if let Some(hist_maint) = historical.context.get("maintainability") {
                    maintainability = hist_maint.clone();
                }
                if let Some(hist_timeline) = historical.context.get("timeline") {
                    timeline = hist_timeline.clone();
                }
            }
        }
        ProblemContext {
            domain,
            scale: scale.to_string(),
            performance_requirements: "standard".to_string(),
            maintainability_requirements: maintainability,
            team_size: "medium".to_string(),
            timeline,
            existing_patterns: patterns.into_iter().collect(),
            technologies: technologies.into_iter().collect(),
        }
    }
    fn identify_existing_patterns(&self, concepts: &[SemanticConcept]) -> Vec<String> {
        let mut patterns = Vec::new();
        
        // Identify patterns based on concept analysis
        let has_controllers = concepts.iter().any(|c| c.name.contains("Controller"));
        let has_services = concepts.iter().any(|c| c.name.contains("Service"));
        let has_repositories = concepts.iter().any(|c| c.name.contains("Repository"));
        
        if has_controllers && has_services {
            patterns.push("mvc".to_string());
        }
        if has_repositories {
            patterns.push("repository_pattern".to_string());
        }
        if has_services {
            patterns.push("service_layer".to_string());
        }
        
        patterns
    }
    fn generate_contextual_candidates(
        &self,
        problem_description: &str,
        complexity: &ProblemComplexity,
        context: &ProblemContext,
        existing_patterns: &[String],
    ) -> Vec<GeneratedApproach> {
        self.generate_approach_candidates(problem_description, complexity, context, existing_patterns)
    }
    fn generate_historical_candidates(
        &self,
        _problem_description: &str,
        _complexity: &ProblemComplexity,
        _context: &ProblemContext,
    ) -> Vec<GeneratedApproach> {
        // Generate candidates based on historical success patterns
        Vec::new() // Simplified for now
    }
    fn generate_template_reasoning(&self, template: &ApproachTemplate, context: &ProblemContext, available_patterns: &[String]) -> String {
        let mut reasoning = vec![template.description.clone()];
        
        let pattern_match_count = template.required_patterns.iter()
            .filter(|&p| available_patterns.contains(p))
            .count();
        
        if pattern_match_count > 0 {
            reasoning.push(format!("Matches {} existing patterns", pattern_match_count));
        }
        
        if template.technologies.iter().any(|t| context.technologies.contains(t)) {
            reasoning.push("Aligns with existing technology stack".to_string());
        }
        
        reasoning.join(", ")
    }
}
impl Default for ApproachPredictor {
    fn default() -> Self {
        Self::new()
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;
    #[test]
    fn test_approach_predictor_creation() {
        let predictor = ApproachPredictor::new();
        assert!(!predictor.approach_templates.is_empty());
        assert!(!predictor.context_weights.is_empty());
    }
    #[test]
    fn test_problem_complexity_analysis() {
        let predictor = ApproachPredictor::new();
        
        let simple_problem = "Create a basic user registration form";
        assert!(matches!(predictor.analyze_problem_complexity(simple_problem), ProblemComplexity::Low));
        
        let medium_problem = "Build an API for user management with authentication and database integration";
        assert!(matches!(predictor.analyze_problem_complexity(medium_problem), ProblemComplexity::Medium));
        
        let complex_problem = "Design a distributed microservices architecture for real-time high-throughput event processing with multiple systems integration";
        assert!(matches!(predictor.analyze_problem_complexity(complex_problem), ProblemComplexity::High));
    }
    #[test]
    fn test_approach_prediction() {
        let predictor = ApproachPredictor::new();
        
        let simple_problem = "Create a basic CRUD application for managing tasks";
        let prediction = predictor.predict_approach(simple_problem.to_string(), None).unwrap();
        
        assert!(!prediction.approach.is_empty());
        assert!(prediction.confidence > 0.0);
        assert!(prediction.confidence <= 1.0);
        assert_eq!(prediction.complexity, "low");
    }
    #[test]
    fn test_context_data_parsing() {
        let predictor = ApproachPredictor::new();
        
        let context_json = r#"{
            "domain": "e-commerce",
            "scale": "large",
            "performance": "high",
            "team_size": "large",
            "technologies": ["react", "node", "mongodb"]
        }"#;
        
        let context = predictor.parse_context_data(Some(context_json)).unwrap();
        assert_eq!(context.domain, "e-commerce");
        assert_eq!(context.scale, "large");
        assert_eq!(context.performance_requirements, "high");
        assert!(context.technologies.contains(&"react".to_string()));
    }
    #[test]
    fn test_approach_alternatives_generation() {
        let predictor = ApproachPredictor::new();
        
        let problem = "Build a scalable web application for handling user data";
        let alternatives = predictor.generate_alternatives(problem, None, 3).unwrap();
        
        assert!(!alternatives.is_empty());
        assert!(alternatives.len() <= 3);
        
        // Alternatives should be sorted by confidence
        for window in alternatives.windows(2) {
            assert!(window[0].confidence >= window[1].confidence);
        }
    }
    #[test]
    fn test_pattern_extraction() {
        let predictor = ApproachPredictor::new();
        let context = ProblemContext {
            domain: "web".to_string(),
            scale: "medium".to_string(),
            performance_requirements: "standard".to_string(),
            maintainability_requirements: "high".to_string(),
            team_size: "small".to_string(),
            timeline: "months".to_string(),
            existing_patterns: vec!["mvc".to_string()],
            technologies: vec!["react".to_string(), "express".to_string()],
        };
        
        let patterns = predictor.extract_available_patterns(&context);
        assert!(patterns.contains(&"mvc".to_string()));
        assert!(patterns.contains(&"component_based".to_string()));
    }
    #[test]
    fn test_template_confidence_calculation() {
        let predictor = ApproachPredictor::new();
        
        let template = &predictor.approach_templates["microservices"];
        let context = ProblemContext {
            domain: "web".to_string(),
            scale: "large".to_string(),
            performance_requirements: "high".to_string(),
            maintainability_requirements: "high".to_string(),
            team_size: "large".to_string(),
            timeline: "months".to_string(),
            existing_patterns: vec!["service_boundaries".to_string()],
            technologies: vec!["docker".to_string()],
        };
        let available_patterns = vec!["service_boundaries".to_string(), "api_gateway".to_string()];
        
        let confidence = predictor.calculate_template_confidence(template, &context, &available_patterns);
        assert!(confidence > template.confidence_base);
    }
    #[test]
    fn test_codebase_context_analysis() {
        let predictor = ApproachPredictor::new();
        
        let concepts = vec![
            SemanticConcept {
                id: "1".to_string(),
                name: "UserController".to_string(),
                concept_type: "class".to_string(),
                confidence: 0.8,
                file_path: "controllers/UserController.js".to_string(),
                line_range: crate::types::LineRange { start: 1, end: 50 },
                relationships: HashMap::new(),
                metadata: HashMap::new(),
            },
            SemanticConcept {
                id: "2".to_string(),
                name: "UserService".to_string(),
                concept_type: "class".to_string(),
                confidence: 0.8,
                file_path: "services/UserService.js".to_string(),
                line_range: crate::types::LineRange { start: 1, end: 30 },
                relationships: HashMap::new(),
                metadata: HashMap::new(),
            },
        ];
        
        let context = predictor.analyze_codebase_context(&concepts);
        assert_eq!(context.scale, "small");
        assert!(context.existing_patterns.contains(&"service_layer".to_string()));
    }
    #[test]
    fn test_existing_pattern_identification() {
        let predictor = ApproachPredictor::new();
        
        let concepts = vec![
            SemanticConcept {
                id: "1".to_string(),
                name: "UserController".to_string(),
                concept_type: "class".to_string(),
                confidence: 0.8,
                file_path: "UserController.js".to_string(),
                line_range: crate::types::LineRange { start: 1, end: 50 },
                relationships: HashMap::new(),
                metadata: HashMap::new(),
            },
            SemanticConcept {
                id: "2".to_string(),
                name: "UserRepository".to_string(),
                concept_type: "class".to_string(),
                confidence: 0.8,
                file_path: "UserRepository.js".to_string(),
                line_range: crate::types::LineRange { start: 1, end: 30 },
                relationships: HashMap::new(),
                metadata: HashMap::new(),
            },
        ];
        
        let patterns = predictor.identify_existing_patterns(&concepts);
        assert!(patterns.contains(&"mvc".to_string()));
        assert!(patterns.contains(&"repository_pattern".to_string()));
    }
    #[test]
    fn test_complexity_suitability_matching() {
        let predictor = ApproachPredictor::new();
        
        // Test that CRUD template is suitable for low complexity
        let crud_template = &predictor.approach_templates["crud"];
        assert!(crud_template.complexity_suitability.contains(&ProblemComplexity::Low));
        
        // Test that microservices template is suitable for high complexity
        let microservices_template = &predictor.approach_templates["microservices"];
        assert!(microservices_template.complexity_suitability.contains(&ProblemComplexity::High));
    }
    #[test]
    fn test_prediction_from_codebase() {
        let predictor = ApproachPredictor::new();
        
        let concepts = vec![
            SemanticConcept {
                id: "1".to_string(),
                name: "UserService".to_string(),
                concept_type: "class".to_string(),
                confidence: 0.8,
                file_path: "services/UserService.js".to_string(),
                line_range: crate::types::LineRange { start: 1, end: 30 },
                relationships: HashMap::new(),
                metadata: HashMap::new(),
            },
        ];
        
        let problem = "Add new feature to manage user profiles";
        let prediction = predictor.predict_from_codebase(&concepts, problem).unwrap();
        
        assert!(!prediction.approach.is_empty());
        assert!(prediction.confidence > 0.0);
        assert!(!prediction.patterns.is_empty());
    }
}