"""
Advanced Analytics for RAGLint Dashboard.

Provides drift detection, embedding visualization, and cohort analysis.
"""

import logging
from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import numpy as np

logger = logging.getLogger(__name__)


class DriftDetector:
    """Detect metric drift over time."""
    
    def detect_drift(self, historical_data: List[Dict[str, Any]], 
                    metric_name: str,
                    threshold: float = 0.15) -> Dict[str, Any]:
        """
        Detect if a metric has drifted significantly from baseline.
        
        Args:
            historical_data: List of run data with timestamps
            metric_name: Name of the metric to check
            threshold: Percentage change threshold (0.15 = 15%)
            
        Returns:
            Dict with drift analysis
        """
        if len(historical_data) < 2:
            return {"drift_detected": False, "reason": "Insufficient data"}
        
        # Sort by timestamp
        sorted_data = sorted(historical_data, key=lambda x: x.get('timestamp', datetime.now()))
        
        # Extract metric values
        values = []
        timestamps = []
        for run in sorted_data:
            if metric_name in run.get('metrics', {}):
                values.append(run['metrics'][metric_name])
                timestamps.append(run.get('timestamp', datetime.now()))
        
        if len(values) < 2:
            return {"drift_detected": False, "reason": "Insufficient metric data"}
        
        # Calculate baseline (first 30% of data)
        baseline_size = max(1, len(values) // 3)
        baseline_mean = np.mean(values[:baseline_size])
        
        # Calculate recent mean (last 30% of data)
        recent_size = max(1, len(values) // 3)
        recent_mean = np.mean(values[-recent_size:])
        
        # Calculate drift
        if baseline_mean == 0:
            drift_percentage = 0
        else:
            drift_percentage = abs(recent_mean - baseline_mean) / baseline_mean
        
        drift_detected = drift_percentage > threshold
        
        return {
            "drift_detected": drift_detected,
            "baseline_mean": float(baseline_mean),
            "recent_mean": float(recent_mean),
            "drift_percentage": float(drift_percentage),
            "threshold": threshold,
            "direction": "increase" if recent_mean > baseline_mean else "decrease",
            "timestamps": [ts.isoformat() if isinstance(ts, datetime) else str(ts) for ts in timestamps],
            "values": [float(v) for v in values]
        }


class EmbeddingVisualizer:
    """Visualize embeddings using UMAP."""
    
    def __init__(self):
        try:
            import umap
            self.umap_reducer = umap.UMAP(n_components=2, random_state=42)
            self.available = True
        except ImportError:
            logger.warning("UMAP not available. Install umap-learn for embedding visualization.")
            self.available = False
    
    def reduce_dimensions(self, embeddings: List[List[float]], 
                         labels: Optional[List[str]] = None) -> Dict[str, Any]:
        """
        Reduce embedding dimensions to 2D for visualization.
        
        Args:
            embeddings: List of embedding vectors
            labels: Optional labels for each embedding
            
        Returns:
            Dict with 2D coordinates and labels
        """
        if not self.available:
            return {"error": "UMAP not available"}
        
        if len(embeddings) < 2:
            return {"error": "Need at least 2 embeddings"}
        
        # Convert to numpy array
        X = np.array(embeddings)
        
        # Reduce dimensions
        X_2d = self.umap_reducer.fit_transform(X)
        
        return {
            "x": X_2d[:, 0].tolist(),
            "y": X_2d[:, 1].tolist(),
            "labels": labels if labels else [f"Point {i}" for i in range(len(embeddings))]
        }


class CohortAnalyzer:
    """Analyze and compare cohorts of runs."""
    
    def analyze_cohorts(self, runs: List[Dict[str, Any]], 
                       group_by: str = "config_hash") -> Dict[str, Any]:
        """
        Group runs into cohorts and compare metrics.
        
        Args:
            runs: List of run data
            group_by: Field to group by (config_hash, tags, etc.)
            
        Returns:
            Dict with cohort comparison
        """
        cohorts = {}
        
        for run in runs:
            # Get grouping key
            if group_by == "tags":
                key = ",".join(sorted(run.get('tags', [])))
            else:
                key = run.get(group_by, "default")
            
            if key not in cohorts:
                cohorts[key] = []
            cohorts[key].append(run)
        
        # Calculate cohort statistics
        cohort_stats = {}
        for cohort_name, cohort_runs in cohorts.items():
            metrics_summary = {}
            
            # Aggregate metrics across runs
            for run in cohort_runs:
                for metric_name, value in run.get('metrics', {}).items():
                    if metric_name not in metrics_summary:
                        metrics_summary[metric_name] = []
                    metrics_summary[metric_name].append(value)
            
            # Calculate statistics
            stats = {
                "count": len(cohort_runs),
                "metrics": {}
            }
            
            for metric_name, values in metrics_summary.items():
                stats["metrics"][metric_name] = {
                    "mean": float(np.mean(values)),
                    "std": float(np.std(values)),
                    "min": float(np.min(values)),
                    "max": float(np.max(values))
                }
            
            cohort_stats[cohort_name] = stats
        
        return {
            "cohorts": cohort_stats,
            "total_runs": len(runs),
            "num_cohorts": len(cohorts)
        }
