"""
AI Recommendations Engine

Intelligent recommendations engine that provides AI-powered suggestions
for preprocessing, model selection, hyperparameters, and next steps.
"""

import pandas as pd
import numpy as np
from typing import Dict, List, Any, Optional, Tuple, Union
from sklearn.metrics import accuracy_score, mean_squared_error, r2_score
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.model_selection import cross_val_score
import json
from datetime import datetime
import warnings
from dataclasses import dataclass, asdict
from enum import Enum


class RecommendationType(Enum):
    """Types of recommendations"""
    PREPROCESSING = "preprocessing"
    MODEL_SELECTION = "model_selection"
    HYPERPARAMETERS = "hyperparameters"
    FEATURE_ENGINEERING = "feature_engineering"
    NEXT_STEPS = "next_steps"
    IMPROVEMENTS = "improvements"


@dataclass
class Recommendation:
    """Individual recommendation"""
    type: RecommendationType
    title: str
    description: str
    confidence: float  # 0.0 to 1.0
    priority: str  # 'low', 'medium', 'high', 'critical'
    reasoning: str
    implementation: Optional[str] = None
    expected_improvement: Optional[str] = None


@dataclass
class RecommendationReport:
    """Complete recommendation report"""
    dataset_id: str
    timestamp: str
    problem_type: str
    recommendations: List[Recommendation]
    summary: str
    confidence_score: float
    action_items: List[str]


class AIRecommendationsEngine:
    """
    AI-powered recommendations engine that analyzes ML workflows
    and provides intelligent suggestions for improvement.
    """
    
    def __init__(self):
        self.recommendation_history = []
        self.pattern_database = self._load_pattern_database()
        self.success_cases = self._load_success_cases()
        
    def _load_pattern_database(self) -> Dict[str, Any]:
        """Load pattern database from historical data"""
        # This would typically load from a database or file
        # For now, return a simplified pattern database
        return {
            'preprocessing_patterns': {
                'high_missing_values': {
                    'threshold': 0.3,
                    'recommendations': ['advanced_imputation', 'feature_creation', 'data_collection']
                },
                'high_cardinality': {
                    'threshold': 50,
                    'recommendations': ['target_encoding', 'feature_hashing', 'dimensionality_reduction']
                },
                'high_correlation': {
                    'threshold': 0.9,
                    'recommendations': ['feature_selection', 'pca', 'regularization']
                }
            },
            'model_patterns': {
                'small_dataset': {
                    'threshold': 1000,
                    'recommendations': ['simple_models', 'regularization', 'cross_validation']
                },
                'large_dataset': {
                    'threshold': 100000,
                    'recommendations': ['ensemble_methods', 'deep_learning', 'parallel_processing']
                },
                'high_dimensional': {
                    'threshold': 1000,
                    'recommendations': ['feature_selection', 'dimensionality_reduction', 'regularization']
                }
            }
        }
    
    def _load_success_cases(self) -> List[Dict[str, Any]]:
        """Load successful ML cases for reference"""
        # This would typically load from a database
        return [
            {
                'problem_type': 'classification',
                'dataset_size': 'medium',
                'features': 'mixed',
                'best_approach': 'random_forest_with_feature_engineering',
                'improvement': '15% accuracy increase'
            },
            {
                'problem_type': 'regression',
                'dataset_size': 'large',
                'features': 'numerical',
                'best_approach': 'gradient_boosting_with_regularization',
                'improvement': '20% RMSE reduction'
            }
        ]
    
    def analyze_dataset(self, df: pd.DataFrame, target_column: Optional[str] = None) -> Dict[str, Any]:
        """
        Analyze dataset characteristics for recommendations
        
        Args:
            df: Input DataFrame
            target_column: Target column name
            
        Returns:
            Dataset analysis results
        """
        analysis = {
            'basic_info': {
                'shape': df.shape,
                'columns': list(df.columns),
                'dtypes': df.dtypes.to_dict()
            },
            'data_quality': {
                'missing_values': df.isnull().sum().to_dict(),
                'missing_percentage': (df.isnull().sum() / len(df) * 100).to_dict(),
                'duplicate_rows': df.duplicated().sum(),
                'constant_columns': [col for col in df.columns if df[col].nunique() == 1]
            },
            'feature_analysis': {
                'numerical_features': df.select_dtypes(include=[np.number]).columns.tolist(),
                'categorical_features': df.select_dtypes(include=['object', 'category']).columns.tolist(),
                'cardinality': {col: df[col].nunique() for col in df.select_dtypes(include=['object', 'category']).columns}
            },
            'correlations': {}
        }
        
        # Correlation analysis for numerical features
        numerical_cols = analysis['feature_analysis']['numerical_features']
        if len(numerical_cols) > 1:
            corr_matrix = df[numerical_cols].corr()
            high_corr_pairs = []
            for i in range(len(corr_matrix.columns)):
                for j in range(i+1, len(corr_matrix.columns)):
                    if abs(corr_matrix.iloc[i, j]) > 0.8:
                        high_corr_pairs.append((corr_matrix.columns[i], corr_matrix.columns[j]))
            
            analysis['correlations'] = {
                'matrix': corr_matrix.to_dict(),
                'high_correlations': high_corr_pairs
            }
        
        # Target analysis
        if target_column and target_column in df.columns:
            target_series = df[target_column]
            if pd.api.types.is_numeric_dtype(target_series):
                if target_series.nunique() <= 20:
                    analysis['target'] = {
                        'type': 'classification',
                        'classes': target_series.nunique(),
                        'distribution': target_series.value_counts().to_dict()
                    }
                else:
                    analysis['target'] = {
                        'type': 'regression',
                        'distribution': {
                            'mean': target_series.mean(),
                            'std': target_series.std(),
                            'skewness': target_series.skew()
                        }
                    }
            else:
                analysis['target'] = {
                    'type': 'classification',
                    'classes': target_series.nunique(),
                    'distribution': target_series.value_counts().to_dict()
                }
        
        return analysis
    
    def generate_preprocessing_recommendations(self, analysis: Dict[str, Any]) -> List[Recommendation]:
        """Generate preprocessing recommendations based on analysis"""
        recommendations = []
        
        # Missing values recommendations
        missing_pct = analysis['data_quality']['missing_percentage']
        high_missing = [col for col, pct in missing_pct.items() if pct > 20]
        
        if high_missing:
            recommendations.append(Recommendation(
                type=RecommendationType.PREPROCESSING,
                title="Handle High Missing Values",
                description=f"Features {high_missing} have >20% missing values",
                confidence=0.9,
                priority='high',
                reasoning="High missing values can significantly impact model performance",
                implementation="Consider advanced imputation methods like KNN imputation or MICE, or create missing indicator features",
                expected_improvement="5-15% improvement in model accuracy"
            ))
        
        # Cardinality recommendations
        cardinality = analysis['feature_analysis']['cardinality']
        high_cardinality = [col for col, card in cardinality.items() if card > 50]
        
        if high_cardinality:
            recommendations.append(Recommendation(
                type=RecommendationType.PREPROCESSING,
                title="Handle High Cardinality Features",
                description=f"Features {high_cardinality} have high cardinality",
                confidence=0.8,
                priority='medium',
                reasoning="High cardinality can lead to overfitting and computational issues",
                implementation="Consider target encoding, feature hashing, or dimensionality reduction techniques",
                expected_improvement="Reduced overfitting and faster training"
            ))
        
        # Correlation recommendations
        high_corr = analysis['correlations'].get('high_correlations', [])
        if high_corr:
            recommendations.append(Recommendation(
                type=RecommendationType.PREPROCESSING,
                title="Address High Correlation",
                description=f"Found {len(high_corr)} highly correlated feature pairs",
                confidence=0.85,
                priority='medium',
                reasoning="High correlation can cause multicollinearity and instability",
                implementation="Remove one feature from each highly correlated pair or use PCA",
                expected_improvement="More stable model coefficients"
            ))
        
        # Data quality recommendations
        if analysis['data_quality']['duplicate_rows'] > 0:
            recommendations.append(Recommendation(
                type=RecommendationType.PREPROCESSING,
                title="Remove Duplicate Rows",
                description=f"Found {analysis['data_quality']['duplicate_rows']} duplicate rows",
                confidence=0.95,
                priority='high',
                reasoning="Duplicates can bias model training and evaluation",
                implementation="Remove duplicate rows using pandas drop_duplicates()",
                expected_improvement="More accurate model evaluation"
            ))
        
        return recommendations
    
    def generate_model_selection_recommendations(self, analysis: Dict[str, Any], 
                                               current_performance: Optional[Dict[str, float]] = None) -> List[Recommendation]:
        """Generate model selection recommendations"""
        recommendations = []
        
        n_samples = analysis['basic_info']['shape'][0]
        n_features = analysis['basic_info']['shape'][1] - 1  # Exclude target
        problem_type = analysis.get('target', {}).get('type', 'unknown')
        
        # Dataset size recommendations
        if n_samples < 1000:
            recommendations.append(Recommendation(
                type=RecommendationType.MODEL_SELECTION,
                title="Use Simple Models for Small Dataset",
                description=f"Dataset has only {n_samples} samples",
                confidence=0.8,
                priority='high',
                reasoning="Small datasets are prone to overfitting with complex models",
                implementation="Consider Logistic Regression, Linear Regression, or simple decision trees with strong regularization",
                expected_improvement="Better generalization and more interpretable results"
            ))
        
        elif n_samples > 100000:
            recommendations.append(Recommendation(
                type=RecommendationType.MODEL_SELECTION,
                title="Consider Scalable Models for Large Dataset",
                description=f"Dataset has {n_samples} samples",
                confidence=0.85,
                priority='medium',
                reasoning="Large datasets can benefit from more complex models and ensemble methods",
                implementation="Consider Random Forest, Gradient Boosting, or Neural Networks with proper regularization",
                expected_improvement="Higher accuracy and better capture of complex patterns"
            ))
        
        # Feature dimensionality recommendations
        if n_features > 1000:
            recommendations.append(Recommendation(
                type=RecommendationType.MODEL_SELECTION,
                title="Address High Dimensionality",
                description=f"Dataset has {n_features} features",
                confidence=0.9,
                priority='high',
                reasoning="High dimensionality can lead to overfitting and computational issues",
                implementation="Use feature selection methods like SelectKBest or dimensionality reduction like PCA",
                expected_improvement="Reduced overfitting and faster training"
            ))
        
        # Problem type specific recommendations
        if problem_type == 'classification':
            target_classes = analysis.get('target', {}).get('classes', 0)
            if target_classes > 10:
                recommendations.append(Recommendation(
                    type=RecommendationType.MODEL_SELECTION,
                    title="Multi-class Classification Strategy",
                    description=f"Problem has {target_classes} classes",
                    confidence=0.8,
                    priority='medium',
                    reasoning="Multi-class problems require specific model choices and evaluation metrics",
                    implementation="Consider models that handle multi-class natively or use one-vs-rest strategy",
                    expected_improvement="Better handling of class relationships"
                ))
        
        elif problem_type == 'regression':
            target_dist = analysis.get('target', {}).get('distribution', {})
            if abs(target_dist.get('skewness', 0)) > 2:
                recommendations.append(Recommendation(
                    type=RecommendationType.MODEL_SELECTION,
                    title="Address Skewed Target Distribution",
                    description=f"Target distribution is highly skewed (skewness: {target_dist.get('skewness', 0):.2f})",
                    confidence=0.85,
                    priority='medium',
                    reasoning="Skewed targets can affect model performance and prediction quality",
                    implementation="Consider log transformation or use models robust to skewed distributions",
                    expected_improvement="More accurate predictions and better model convergence"
                ))
        
        # Performance-based recommendations
        if current_performance:
            if problem_type == 'classification':
                accuracy = current_performance.get('accuracy', 0)
                if accuracy < 0.7:
                    recommendations.append(Recommendation(
                        type=RecommendationType.IMPROVEMENTS,
                        title="Low Classification Accuracy",
                        description=f"Current accuracy is {accuracy:.3f}",
                        confidence=0.9,
                        priority='high',
                        reasoning="Accuracy below 70% suggests room for significant improvement",
                        implementation="Try ensemble methods, feature engineering, or collect more data",
                        expected_improvement="10-20% improvement in accuracy"
                    ))
            
            elif problem_type == 'regression':
                r2 = current_performance.get('r2', 0)
                if r2 < 0.7:
                    recommendations.append(Recommendation(
                        type=RecommendationType.IMPROVEMENTS,
                        title="Low Regression R² Score",
                        description=f"Current R² is {r2:.3f}",
                        confidence=0.9,
                        priority='high',
                        reasoning="R² below 0.7 suggests poor model fit",
                        implementation="Try non-linear models, feature transformation, or ensemble methods",
                        expected_improvement="15-25% improvement in R²"
                    ))
        
        return recommendations
    
    def generate_feature_engineering_recommendations(self, analysis: Dict[str, Any]) -> List[Recommendation]:
        """Generate feature engineering recommendations"""
        recommendations = []
        
        numerical_features = analysis['feature_analysis']['numerical_features']
        categorical_features = analysis['feature_analysis']['categorical_features']
        
        # Interaction features for numerical data
        if len(numerical_features) >= 3:
            recommendations.append(Recommendation(
                type=RecommendationType.FEATURE_ENGINEERING,
                title="Create Interaction Features",
                description=f"Dataset has {len(numerical_features)} numerical features",
                confidence=0.7,
                priority='medium',
                reasoning="Interaction features can capture non-linear relationships",
                implementation="Create polynomial features or interaction terms between important features",
                expected_improvement="5-10% improvement in model performance"
            ))
        
        # Polynomial features for non-linear relationships
        if len(numerical_features) >= 2:
            recommendations.append(Recommendation(
                type=RecommendationType.FEATURE_ENGINEERING,
                title="Consider Polynomial Features",
                description="Non-linear relationships may exist in the data",
                confidence=0.6,
                priority='low',
                reasoning="Polynomial features can capture non-linear patterns",
                implementation="Use PolynomialFeatures from sklearn with degree 2 or 3",
                expected_improvement="3-8% improvement in model performance"
            ))
        
        # Text features from categorical data
        text_features = [col for col in categorical_features if analysis['feature_analysis']['cardinality'].get(col, 0) > 100]
        if text_features:
            recommendations.append(Recommendation(
                type=RecommendationType.FEATURE_ENGINEERING,
                title="Extract Text Features",
                description=f"Features {text_features} may contain text information",
                confidence=0.8,
                priority='medium',
                reasoning="Text features can provide additional information through NLP techniques",
                implementation="Use TF-IDF, word embeddings, or text classification techniques",
                expected_improvement="5-15% improvement in model performance"
            ))
        
        # Date/time features
        datetime_features = [col for col in analysis['basic_info']['columns'] 
                           if any(keyword in col.lower() for keyword in ['date', 'time', 'created', 'updated'])]
        if datetime_features:
            recommendations.append(Recommendation(
                type=RecommendationType.FEATURE_ENGINEERING,
                title="Extract DateTime Features",
                description=f"Found potential datetime features: {datetime_features}",
                confidence=0.9,
                priority='medium',
                reasoning="DateTime features can provide temporal patterns",
                implementation="Extract year, month, day, hour, day of week, and other temporal features",
                expected_improvement="5-12% improvement in model performance"
            ))
        
        return recommendations
    
    def generate_hyperparameter_recommendations(self, model_type: str, 
                                             current_params: Dict[str, Any],
                                             performance: Dict[str, float]) -> List[Recommendation]:
        """Generate hyperparameter optimization recommendations"""
        recommendations = []
        
        # Model-specific recommendations
        if 'random_forest' in model_type.lower():
            n_estimators = current_params.get('n_estimators', 100)
            max_depth = current_params.get('max_depth', None)
            
            if n_estimators < 200:
                recommendations.append(Recommendation(
                    type=RecommendationType.HYPERPARAMETERS,
                    title="Increase Number of Estimators",
                    description=f"Current n_estimators={n_estimators}",
                    confidence=0.7,
                    priority='medium',
                    reasoning="More trees can improve model stability and performance",
                    implementation="Increase n_estimators to 200-500",
                    expected_improvement="2-5% improvement in accuracy"
                ))
            
            if max_depth is None or max_depth > 20:
                recommendations.append(Recommendation(
                    type=RecommendationType.HYPERPARAMETERS,
                    title="Optimize Tree Depth",
                    description=f"Current max_depth={max_depth}",
                    confidence=0.8,
                    priority='medium',
                    reasoning="Unlimited depth can cause overfitting",
                    implementation="Set max_depth between 10-20 and use cross-validation",
                    expected_improvement="Better generalization"
                ))
        
        elif 'gradient_boosting' in model_type.lower() or 'xgboost' in model_type.lower():
            learning_rate = current_params.get('learning_rate', 0.1)
            
            if learning_rate > 0.1:
                recommendations.append(Recommendation(
                    type=RecommendationType.HYPERPARAMETERS,
                    title="Reduce Learning Rate",
                    description=f"Current learning_rate={learning_rate}",
                    confidence=0.8,
                    priority='medium',
                    reasoning="Lower learning rate with more iterations often performs better",
                    implementation="Reduce learning_rate to 0.01-0.05 and increase n_estimators",
                    expected_improvement="3-8% improvement in performance"
                ))
        
        elif 'svm' in model_type.lower():
            C = current_params.get('C', 1.0)
            
            if C == 1.0:  # Default value
                recommendations.append(Recommendation(
                    type=RecommendationType.HYPERPARAMETERS,
                    title="Optimize Regularization Parameter",
                    description="Using default C=1.0",
                    confidence=0.9,
                    priority='high',
                    reasoning="Default C may not be optimal for your data",
                    implementation="Use GridSearchCV or RandomizedSearchCV to find optimal C",
                    expected_improvement="5-15% improvement in performance"
                ))
        
        # General optimization recommendations
        if 'cross_validation' not in str(current_params).lower():
            recommendations.append(Recommendation(
                type=RecommendationType.HYPERPARAMETERS,
                title="Use Cross-Validation",
                description="No cross-validation detected in current setup",
                confidence=0.95,
                priority='high',
                reasoning="Cross-validation provides more reliable performance estimates",
                implementation="Use GridSearchCV or RandomizedSearchCV with cv=5 or cv=10",
                expected_improvement="More reliable model selection and hyperparameter tuning"
            ))
        
        return recommendations
    
    def generate_next_steps_recommendations(self, analysis: Dict[str, Any], 
                                         current_performance: Optional[Dict[str, float]] = None) -> List[Recommendation]:
        """Generate next steps recommendations"""
        recommendations = []
        
        # Data collection recommendations
        n_samples = analysis['basic_info']['shape'][0]
        if n_samples < 5000:
            recommendations.append(Recommendation(
                type=RecommendationType.NEXT_STEPS,
                title="Collect More Data",
                description=f"Current dataset has {n_samples} samples",
                confidence=0.8,
                priority='medium',
                reasoning="More data generally improves model performance",
                implementation="Collect additional samples or use data augmentation techniques",
                expected_improvement="5-20% improvement in model performance"
            ))
        
        # Advanced modeling techniques
        if current_performance:
            problem_type = analysis.get('target', {}).get('type', 'unknown')
            
            if problem_type == 'classification':
                accuracy = current_performance.get('accuracy', 0)
                if accuracy > 0.85:
                    recommendations.append(Recommendation(
                        type=RecommendationType.NEXT_STEPS,
                        title="Try Ensemble Methods",
                        description="Model already performing well",
                        confidence=0.7,
                        priority='low',
                        reasoning="Ensemble methods can push performance even higher",
                        implementation="Try stacking, blending, or voting classifiers",
                        expected_improvement="1-3% improvement in accuracy"
                    ))
            
            elif problem_type == 'regression':
                r2 = current_performance.get('r2', 0)
                if r2 > 0.85:
                    recommendations.append(Recommendation(
                        type=RecommendationType.NEXT_STEPS,
                        title="Consider Advanced Techniques",
                        description="Model already performing well",
                        confidence=0.7,
                        priority='low',
                        reasoning="Advanced techniques can provide marginal improvements",
                        implementation="Try neural networks, ensemble methods, or domain-specific approaches",
                        expected_improvement="1-5% improvement in R²"
                    ))
        
        # Model deployment recommendations
        recommendations.append(Recommendation(
            type=RecommendationType.NEXT_STEPS,
            title="Prepare for Model Deployment",
            description="Consider deployment and monitoring",
            confidence=0.8,
            priority='medium',
            reasoning="Model deployment requires planning for scalability and monitoring",
            implementation="Set up REST API, monitoring, and retraining pipeline",
            expected_improvement="Production-ready model with continuous improvement"
        ))
        
        return recommendations
    
    def generate_comprehensive_report(self, df: pd.DataFrame, 
                                    target_column: Optional[str] = None,
                                    current_model: Optional[str] = None,
                                    current_performance: Optional[Dict[str, float]] = None,
                                    current_params: Optional[Dict[str, Any]] = None) -> RecommendationReport:
        """
        Generate comprehensive recommendation report
        
        Args:
            df: Input DataFrame
            target_column: Target column name
            current_model: Current model type
            current_performance: Current model performance
            current_params: Current model parameters
            
        Returns:
            Comprehensive recommendation report
        """
        # Analyze dataset
        analysis = self.analyze_dataset(df, target_column)
        
        # Generate all types of recommendations
        all_recommendations = []
        
        # Preprocessing recommendations
        preprocessing_recs = self.generate_preprocessing_recommendations(analysis)
        all_recommendations.extend(preprocessing_recs)
        
        # Model selection recommendations
        model_recs = self.generate_model_selection_recommendations(analysis, current_performance)
        all_recommendations.extend(model_recs)
        
        # Feature engineering recommendations
        feature_recs = self.generate_feature_engineering_recommendations(analysis)
        all_recommendations.extend(feature_recs)
        
        # Hyperparameter recommendations (if model provided)
        if current_model and current_params:
            hyperparam_recs = self.generate_hyperparameter_recommendations(
                current_model, current_params, current_performance or {}
            )
            all_recommendations.extend(hyperparam_recs)
        
        # Next steps recommendations
        next_steps_recs = self.generate_next_steps_recommendations(analysis, current_performance)
        all_recommendations.extend(next_steps_recs)
        
        # Sort by priority and confidence
        priority_order = {'critical': 4, 'high': 3, 'medium': 2, 'low': 1}
        all_recommendations.sort(key=lambda x: (priority_order.get(x.priority, 0), x.confidence), reverse=True)
        
        # Generate summary
        problem_type = analysis.get('target', {}).get('type', 'unknown')
        summary = self._generate_summary(analysis, all_recommendations, current_performance)
        
        # Generate action items
        action_items = [rec.title for rec in all_recommendations if rec.priority in ['critical', 'high']]
        
        # Calculate overall confidence score
        confidence_score = np.mean([rec.confidence for rec in all_recommendations])
        
        # Create report
        report = RecommendationReport(
            dataset_id=hashlib.md5(f"{df.shape}_{datetime.now()}".encode()).hexdigest()[:16],
            timestamp=datetime.now().isoformat(),
            problem_type=problem_type,
            recommendations=all_recommendations,
            summary=summary,
            confidence_score=confidence_score,
            action_items=action_items
        )
        
        # Store in history
        self.recommendation_history.append(report)
        
        return report
    
    def _generate_summary(self, analysis: Dict[str, Any], 
                         recommendations: List[Recommendation],
                         current_performance: Optional[Dict[str, float]]) -> str:
        """Generate summary of recommendations"""
        n_samples = analysis['basic_info']['shape'][0]
        n_features = analysis['basic_info']['shape'][1]
        problem_type = analysis.get('target', {}).get('type', 'unknown')
        
        summary = f"Dataset Analysis Summary:\n"
        summary += f"- Dataset size: {n_samples} samples, {n_features} features\n"
        summary += f"- Problem type: {problem_type}\n"
        
        # Data quality assessment
        missing_pct = np.mean(list(analysis['data_quality']['missing_percentage'].values()))
        if missing_pct > 10:
            summary += f"- Data quality concerns: {missing_pct:.1f}% missing values\n"
        else:
            summary += "- Data quality appears good\n"
        
        # Performance assessment
        if current_performance:
            if problem_type == 'classification':
                accuracy = current_performance.get('accuracy', 0)
                summary += f"- Current model accuracy: {accuracy:.3f}\n"
                if accuracy < 0.7:
                    summary += "- Significant room for improvement\n"
                elif accuracy < 0.85:
                    summary += "- Moderate room for improvement\n"
                else:
                    summary += "- Good performance achieved\n"
            elif problem_type == 'regression':
                r2 = current_performance.get('r2', 0)
                summary += f"- Current model R²: {r2:.3f}\n"
                if r2 < 0.7:
                    summary += "- Significant room for improvement\n"
                elif r2 < 0.85:
                    summary += "- Moderate room for improvement\n"
                else:
                    summary += "- Good performance achieved\n"
        
        # Top recommendations
        high_priority_recs = [rec for rec in recommendations if rec.priority in ['critical', 'high']]
        if high_priority_recs:
            summary += f"\nTop Priority Recommendations:\n"
            for rec in high_priority_recs[:3]:
                summary += f"- {rec.title}: {rec.description}\n"
        
        return summary
    
    def export_report(self, report: RecommendationReport, filepath: str):
        """Export recommendation report to file"""
        report_dict = asdict(report)
        
        # Convert enum to string
        report_dict['recommendations'] = [
            {
                **asdict(rec),
                'type': rec.type.value
            }
            for rec in report.recommendations
        ]
        
        with open(filepath, 'w') as f:
            json.dump(report_dict, f, indent=2)
    
    def import_report(self, filepath: str) -> RecommendationReport:
        """Import recommendation report from file"""
        with open(filepath, 'r') as f:
            report_dict = json.load(f)
        
        # Convert back to Recommendation objects
        recommendations = []
        for rec_dict in report_dict['recommendations']:
            rec_dict['type'] = RecommendationType(rec_dict['type'])
            recommendations.append(Recommendation(**rec_dict))
        
        report_dict['recommendations'] = recommendations
        
        return RecommendationReport(**report_dict)
    
    def get_recommendation_statistics(self) -> Dict[str, Any]:
        """Get statistics about recommendations made"""
        if not self.recommendation_history:
            return {"message": "No recommendations made yet"}
        
        total_reports = len(self.recommendation_history)
        total_recommendations = sum(len(report.recommendations) for report in self.recommendation_history)
        
        # Count by type
        type_counts = {}
        for report in self.recommendation_history:
            for rec in report.recommendations:
                type_name = rec.type.value
                type_counts[type_name] = type_counts.get(type_name, 0) + 1
        
        # Count by priority
        priority_counts = {}
        for report in self.recommendation_history:
            for rec in report.recommendations:
                priority_counts[rec.priority] = priority_counts.get(rec.priority, 0) + 1
        
        # Average confidence
        all_confidences = []
        for report in self.recommendation_history:
            all_confidences.extend([rec.confidence for rec in report.recommendations])
        
        avg_confidence = np.mean(all_confidences) if all_confidences else 0
        
        return {
            'total_reports': total_reports,
            'total_recommendations': total_recommendations,
            'recommendations_by_type': type_counts,
            'recommendations_by_priority': priority_counts,
            'average_confidence': avg_confidence,
            'most_common_type': max(type_counts.items(), key=lambda x: x[1])[0] if type_counts else None,
            'most_common_priority': max(priority_counts.items(), key=lambda x: x[1])[0] if priority_counts else None
        }
