"""
Performance benchmarking service for Trax platform.

Tracks performance metrics, generates reports, and provides
comparison analysis for optimization efforts.
"""

import json
import logging
import statistics
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional, Protocol, Any

from .performance import PerformanceMetrics

logger = logging.getLogger(__name__)


@dataclass
class PerformanceStatistics:
    """Statistical summary of performance metrics."""
    avg_duration: float
    avg_memory_peak: float
    avg_cpu_peak: float
    avg_throughput: float
    min_duration: float
    max_duration: float
    min_memory_peak: float
    max_memory_peak: float
    min_cpu_peak: float
    max_cpu_peak: float
    total_operations: int
    success_rate: float
    error_rate: float


class PerformanceBenchmarkerProtocol(Protocol):
    """Protocol for performance benchmarking services."""
    def record_operation(self, operation_name: str, metrics: PerformanceMetrics) -> None: ...
    def calculate_statistics(self, operation_name: str) -> PerformanceStatistics: ...
    def generate_report(self) -> Dict[str, Any]: ...
    def export_report(self, file_path: Path, format: str = "json") -> None: ...


class PerformanceBenchmarker:
    """Track and analyze performance metrics."""
    
    def __init__(self, report_format: str = "json"):
        self.benchmarks: Dict[str, List[PerformanceMetrics]] = {}
        self.report_format = report_format
        
        logger.info("PerformanceBenchmarker initialized")
    
    def record_operation(self, operation_name: str, metrics: PerformanceMetrics) -> None:
        """Record performance metrics for an operation."""
        if operation_name not in self.benchmarks:
            self.benchmarks[operation_name] = []
        
        self.benchmarks[operation_name].append(metrics)
        
        logger.debug(f"Recorded metrics for {operation_name}: "
                    f"{metrics.duration_seconds:.2f}s, "
                    f"{metrics.throughput_items_per_second:.2f} items/s")
    
    def calculate_statistics(self, operation_name: str) -> PerformanceStatistics:
        """Calculate statistical summary for an operation."""
        if operation_name not in self.benchmarks or not self.benchmarks[operation_name]:
            raise ValueError(f"No metrics found for operation: {operation_name}")
        
        metrics_list = self.benchmarks[operation_name]
        
        # Extract values for calculation
        durations = [m.duration_seconds for m in metrics_list]
        memory_peaks = [m.memory_peak_mb for m in metrics_list]
        cpu_peaks = [m.cpu_peak_percent for m in metrics_list]
        throughputs = [m.throughput_items_per_second for m in metrics_list]
        
        total_operations = len(metrics_list)
        total_items = sum(m.total_count for m in metrics_list)
        successful_items = sum(m.success_count for m in metrics_list)
        error_items = sum(m.error_count for m in metrics_list)
        
        # Calculate statistics
        stats = PerformanceStatistics(
            avg_duration=statistics.mean(durations),
            avg_memory_peak=statistics.mean(memory_peaks),
            avg_cpu_peak=statistics.mean(cpu_peaks),
            avg_throughput=statistics.mean(throughputs),
            min_duration=min(durations),
            max_duration=max(durations),
            min_memory_peak=min(memory_peaks),
            max_memory_peak=max(memory_peaks),
            min_cpu_peak=min(cpu_peaks),
            max_cpu_peak=max(cpu_peaks),
            total_operations=total_operations,
            success_rate=(successful_items / total_items * 100) if total_items > 0 else 0.0,
            error_rate=(error_items / total_items * 100) if total_items > 0 else 0.0
        )
        
        return stats
    
    def generate_report(self) -> Dict[str, any]:
        """Generate comprehensive performance report."""
        report = {
            "generated_at": datetime.now(timezone.utc).isoformat(),
            "total_operations": sum(len(metrics) for metrics in self.benchmarks.values()),
            "operations": {}
        }
        
        for operation_name, metrics_list in self.benchmarks.items():
            try:
                stats = self.calculate_statistics(operation_name)
                report["operations"][operation_name] = {
                    "statistics": asdict(stats),
                    "metrics_count": len(metrics_list),
                    "latest_metrics": asdict(metrics_list[-1]) if metrics_list else None
                }
            except Exception as e:
                logger.error(f"Error calculating statistics for {operation_name}: {e}")
                report["operations"][operation_name] = {
                    "error": str(e),
                    "metrics_count": len(metrics_list)
                }
        
        return report
    
    def export_report(self, file_path: Path, format: str = "json") -> None:
        """Export performance report to file."""
        report = self.generate_report()
        
        if format.lower() == "json":
            with open(file_path, 'w') as f:
                json.dump(report, f, indent=2, default=str)
        else:
            raise ValueError(f"Unsupported export format: {format}")
        
        logger.info(f"Performance report exported to: {file_path}")
    
    def compare_benchmarks(self, baseline_name: str, comparison_name: str) -> Dict[str, float]:
        """Compare two benchmark sets and calculate improvements."""
        try:
            baseline_stats = self.calculate_statistics(baseline_name)
            comparison_stats = self.calculate_statistics(comparison_name)
        except ValueError as e:
            logger.error(f"Error comparing benchmarks: {e}")
            return {}
        
        # Calculate percentage improvements
        duration_improvement = (
            (baseline_stats.avg_duration - comparison_stats.avg_duration) / 
            baseline_stats.avg_duration * 100
        )
        
        memory_improvement = (
            (baseline_stats.avg_memory_peak - comparison_stats.avg_memory_peak) / 
            baseline_stats.avg_memory_peak * 100
        )
        
        cpu_improvement = (
            (baseline_stats.avg_cpu_peak - comparison_stats.avg_cpu_peak) / 
            baseline_stats.avg_cpu_peak * 100
        )
        
        throughput_improvement = (
            (comparison_stats.avg_throughput - baseline_stats.avg_throughput) / 
            baseline_stats.avg_throughput * 100
        )
        
        comparison = {
            "duration_improvement": duration_improvement,
            "memory_improvement": memory_improvement,
            "cpu_improvement": cpu_improvement,
            "throughput_improvement": throughput_improvement,
            "baseline_operation": baseline_name,
            "comparison_operation": comparison_name
        }
        
        logger.info(f"Benchmark comparison: {baseline_name} vs {comparison_name}")
        logger.info(f"Duration improvement: {duration_improvement:.1f}%")
        logger.info(f"Memory improvement: {memory_improvement:.1f}%")
        logger.info(f"CPU improvement: {cpu_improvement:.1f}%")
        logger.info(f"Throughput improvement: {throughput_improvement:.1f}%")
        
        return comparison
    
    def get_operation_summary(self, operation_name: str) -> Dict[str, any]:
        """Get summary for a specific operation."""
        if operation_name not in self.benchmarks:
            return {"error": f"Operation not found: {operation_name}"}
        
        metrics_list = self.benchmarks[operation_name]
        
        if not metrics_list:
            return {"error": f"No metrics for operation: {operation_name}"}
        
        try:
            stats = self.calculate_statistics(operation_name)
            latest = metrics_list[-1]
            
            return {
                "operation_name": operation_name,
                "total_runs": len(metrics_list),
                "latest_run": asdict(latest),
                "statistics": asdict(stats),
                "trend": self._calculate_trend(metrics_list)
            }
        except Exception as e:
            return {"error": f"Error calculating summary: {e}"}
    
    def _calculate_trend(self, metrics_list: List[PerformanceMetrics]) -> Dict[str, str]:
        """Calculate trend direction for metrics."""
        if len(metrics_list) < 2:
            return {"trend": "insufficient_data"}
        
        # Calculate trends for key metrics
        durations = [m.duration_seconds for m in metrics_list]
        throughputs = [m.throughput_items_per_second for m in metrics_list]
        
        # Simple trend calculation (comparing first half vs second half)
        mid_point = len(durations) // 2
        
        first_half_avg_duration = statistics.mean(durations[:mid_point])
        second_half_avg_duration = statistics.mean(durations[mid_point:])
        
        first_half_avg_throughput = statistics.mean(throughputs[:mid_point])
        second_half_avg_throughput = statistics.mean(throughputs[mid_point:])
        
        duration_trend = "improving" if second_half_avg_duration < first_half_avg_duration else "degrading"
        throughput_trend = "improving" if second_half_avg_throughput > first_half_avg_throughput else "degrading"
        
        return {
            "duration_trend": duration_trend,
            "throughput_trend": throughput_trend,
            "overall_trend": "improving" if duration_trend == "improving" and throughput_trend == "improving" else "mixed"
        }
    
    def clear_benchmarks(self, operation_name: Optional[str] = None) -> None:
        """Clear benchmark data."""
        if operation_name:
            if operation_name in self.benchmarks:
                del self.benchmarks[operation_name]
                logger.info(f"Cleared benchmarks for operation: {operation_name}")
        else:
            self.benchmarks.clear()
            logger.info("Cleared all benchmarks")
    
    def get_benchmark_summary(self) -> Dict[str, any]:
        """Get summary of all benchmarks."""
        summary = {
            "total_operations_tracked": len(self.benchmarks),
            "total_metrics_recorded": sum(len(metrics) for metrics in self.benchmarks.values()),
            "operations": list(self.benchmarks.keys()),
            "latest_activity": None
        }
        
        # Find latest activity
        all_metrics = []
        for metrics_list in self.benchmarks.values():
            all_metrics.extend(metrics_list)
        
        if all_metrics:
            latest_metric = max(all_metrics, key=lambda m: m.timestamp)
            summary["latest_activity"] = latest_metric.timestamp.isoformat()
        
        return summary


def create_performance_benchmarker(report_format: str = "json") -> PerformanceBenchmarker:
    """Create a performance benchmarker instance."""
    return PerformanceBenchmarker(report_format=report_format)
