"""
Performance profiling and monitoring for CHRONOS network detection.

This module provides cProfile-based profiling tools to identify performance
bottlenecks in network monitoring, especially for darkweb and HNDL detection.
"""

import cProfile
import pstats
import io
import functools
import time
import sys
from pathlib import Path
from datetime import datetime
from typing import Callable, Any, Dict, List, Optional
import logging

logger = logging.getLogger(__name__)


class PerformanceProfiler:
    """Profile function execution time and resource usage."""
    
    def __init__(self, name: str = "chronos_profiler"):
        self.name = name
        self.profiler = cProfile.Profile()
        self.stats: Optional[pstats.Stats] = None
        self.results: Dict[str, Any] = {}
        
    def enable(self) -> None:
        """Start profiling."""
        self.profiler.enable()
        
    def disable(self) -> None:
        """Stop profiling."""
        self.profiler.disable()
        
    def get_stats(self) -> pstats.Stats:
        """Get profiling statistics."""
        stream = io.StringIO()
        stats = pstats.Stats(self.profiler, stream=stream)
        stats.sort_stats('cumulative')
        return stats
        
    def print_stats(self, top_n: int = 20) -> str:
        """Print profiling statistics as formatted string.
        
        Args:
            top_n: Number of top functions to display
            
        Returns:
            Formatted statistics string
        """
        stream = io.StringIO()
        stats = pstats.Stats(self.profiler, stream=stream)
        stats.sort_stats('cumulative')
        stats.print_stats(top_n)
        return stream.getvalue()
        
    def save_to_file(self, filepath: Path) -> None:
        """Save profiling results to file.
        
        Args:
            filepath: Path to save profile stats
        """
        self.profiler.dump_stats(str(filepath))
        logger.info(f"Profile saved to {filepath}")


class FunctionTimer:
    """High-resolution timer for function execution."""
    
    def __init__(self, name: str = ""):
        self.name = name
        self.start_time: Optional[float] = None
        self.end_time: Optional[float] = None
        self.duration: float = 0.0
        
    def __enter__(self):
        """Start timing."""
        self.start_time = time.perf_counter()
        return self
        
    def __exit__(self, *args):
        """Stop timing and record duration."""
        self.end_time = time.perf_counter()
        self.duration = self.end_time - self.start_time
        
    def elapsed_ms(self) -> float:
        """Get elapsed time in milliseconds."""
        return self.duration * 1000
        
    def elapsed_us(self) -> float:
        """Get elapsed time in microseconds."""
        return self.duration * 1_000_000


class MemoryProfiler:
    """Track memory usage during profiling."""
    
    def __init__(self):
        self.start_memory: int = 0
        self.peak_memory: int = 0
        self.end_memory: int = 0
        
    def _get_memory_mb(self) -> float:
        """Get current memory usage in MB."""
        try:
            import psutil
            process = psutil.Process()
            return process.memory_info().rss / (1024 * 1024)
        except ImportError:
            return 0.0
            
    def __enter__(self):
        """Start memory profiling."""
        self.start_memory = self._get_memory_mb()
        self.peak_memory = self.start_memory
        return self
        
    def __exit__(self, *args):
        """Stop memory profiling."""
        self.end_memory = self._get_memory_mb()
        self.peak_memory = max(self.peak_memory, self.end_memory)
        
    def memory_delta_mb(self) -> float:
        """Get memory change in MB."""
        return self.end_memory - self.start_memory


def profile_function(func: Callable) -> Callable:
    """Decorator to profile function execution with timing.
    
    Usage:
        @profile_function
        def my_function():
            ...
    """
    @functools.wraps(func)
    def wrapper(*args, **kwargs) -> Any:
        with FunctionTimer(func.__name__) as timer:
            result = func(*args, **kwargs)
        
        logger.info(
            f"Function {func.__name__} executed in "
            f"{timer.elapsed_ms():.2f}ms"
        )
        return result
    return wrapper


def profile_network_monitoring():
    """Context manager for profiling network monitoring operations.
    
    Usage:
        with profile_network_monitoring() as profiler:
            # Network monitoring code
            darkweb_monitor.scan()
        
        profiler.print_stats()
    """
    class NetworkMonitoringProfiler:
        def __init__(self):
            self.profiler = PerformanceProfiler("network_monitoring")
            self.timer = FunctionTimer()
            
        def __enter__(self):
            self.profiler.enable()
            self.timer.__enter__()
            return self
            
        def __exit__(self, *args):
            self.profiler.disable()
            self.timer.__exit__(*args)
            logger.info(
                f"Network monitoring completed in "
                f"{self.timer.elapsed_ms():.2f}ms"
            )
            
        def print_stats(self, top_n: int = 20) -> str:
            return self.profiler.print_stats(top_n)
    
    return NetworkMonitoringProfiler()


class PerformanceReport:
    """Generate performance analysis reports."""
    
    def __init__(self):
        self.measurements: List[Dict[str, Any]] = []
        
    def add_measurement(
        self,
        operation: str,
        duration_ms: float,
        memory_delta_mb: float = 0.0,
        items_processed: int = 0
    ) -> None:
        """Record a performance measurement.
        
        Args:
            operation: Name of the operation
            duration_ms: Duration in milliseconds
            memory_delta_mb: Memory change in MB
            items_processed: Number of items processed
        """
        self.measurements.append({
            'timestamp': datetime.now().isoformat(),
            'operation': operation,
            'duration_ms': duration_ms,
            'memory_delta_mb': memory_delta_mb,
            'items_processed': items_processed,
            'throughput': items_processed / (duration_ms / 1000) if duration_ms > 0 else 0
        })
        
    def get_report(self) -> str:
        """Generate formatted performance report."""
        if not self.measurements:
            return "No measurements recorded"
            
        report = "CHRONOS Performance Report\n"
        report += "=" * 60 + "\n\n"
        
        # Summary statistics
        total_duration = sum(m['duration_ms'] for m in self.measurements)
        avg_duration = total_duration / len(self.measurements)
        total_memory = sum(m['memory_delta_mb'] for m in self.measurements)
        
        report += f"Total Operations: {len(self.measurements)}\n"
        report += f"Total Duration: {total_duration:.2f}ms\n"
        report += f"Average Duration: {avg_duration:.2f}ms\n"
        report += f"Total Memory Delta: {total_memory:.2f}MB\n\n"
        
        # Per-operation details
        report += "Operation Details:\n"
        report += "-" * 60 + "\n"
        report += f"{'Operation':<30} {'Time(ms)':<12} {'Memory(MB)':<12}\n"
        report += "-" * 60 + "\n"
        
        for m in self.measurements:
            report += (
                f"{m['operation']:<30} "
                f"{m['duration_ms']:<12.2f} "
                f"{m['memory_delta_mb']:<12.2f}\n"
            )
            
        return report
        
    def save_report(self, filepath: Path) -> None:
        """Save performance report to file.
        
        Args:
            filepath: Path to save report
        """
        with open(filepath, 'w') as f:
            f.write(self.get_report())
        logger.info(f"Performance report saved to {filepath}")


class NetworkDetectionProfiler:
    """Specialized profiler for network detection operations."""
    
    def __init__(self):
        self.darkweb_stats: Dict[str, float] = {}
        self.hndl_stats: Dict[str, float] = {}
        self.network_stats: Dict[str, float] = {}
        
    @profile_function
    def profile_darkweb_scan(self, scan_func: Callable) -> Any:
        """Profile darkweb monitoring scan.
        
        Args:
            scan_func: Function to profile
            
        Returns:
            Function result
        """
        with FunctionTimer("darkweb_scan") as timer:
            result = scan_func()
            
        self.darkweb_stats['scan_time_ms'] = timer.elapsed_ms()
        logger.debug(f"Darkweb scan profiled: {timer.elapsed_ms():.2f}ms")
        return result
        
    @profile_function
    def profile_hndl_detection(self, detect_func: Callable) -> Any:
        """Profile HNDL vulnerability detection.
        
        Args:
            detect_func: Function to profile
            
        Returns:
            Function result
        """
        with FunctionTimer("hndl_detection") as timer:
            result = detect_func()
            
        self.hndl_stats['detection_time_ms'] = timer.elapsed_ms()
        logger.debug(f"HNDL detection profiled: {timer.elapsed_ms():.2f}ms")
        return result
        
    @profile_function
    def profile_network_monitor(self, monitor_func: Callable) -> Any:
        """Profile network monitoring.
        
        Args:
            monitor_func: Function to profile
            
        Returns:
            Function result
        """
        with FunctionTimer("network_monitor") as timer:
            result = monitor_func()
            
        self.network_stats['monitor_time_ms'] = timer.elapsed_ms()
        logger.debug(f"Network monitoring profiled: {timer.elapsed_ms():.2f}ms")
        return result


def compare_performance(
    func1: Callable,
    func2: Callable,
    iterations: int = 5,
    *args,
    **kwargs
) -> Dict[str, float]:
    """Compare performance of two functions.
    
    Args:
        func1: First function to compare
        func2: Second function to compare
        iterations: Number of times to run each
        args: Arguments to pass to functions
        kwargs: Keyword arguments to pass to functions
        
    Returns:
        Dictionary with performance metrics
    """
    times1 = []
    times2 = []
    
    # Profile func1
    for _ in range(iterations):
        with FunctionTimer() as timer:
            func1(*args, **kwargs)
        times1.append(timer.elapsed_ms())
        
    # Profile func2
    for _ in range(iterations):
        with FunctionTimer() as timer:
            func2(*args, **kwargs)
        times2.append(timer.elapsed_ms())
        
    avg1 = sum(times1) / len(times1)
    avg2 = sum(times2) / len(times2)
    
    return {
        'func1_avg_ms': avg1,
        'func2_avg_ms': avg2,
        'faster': 'func1' if avg1 < avg2 else 'func2',
        'speed_ratio': max(avg1, avg2) / min(avg1, avg2)
    }
