"""
Comprehensive performance benchmarks for CHRONOS modules.

This module provides benchmark suites to measure and track performance
across all CHRONOS subsystems.
"""

import time
import statistics
from typing import Dict, List, Callable, Optional, Any, Tuple
from dataclasses import dataclass, field
from datetime import datetime
import json
import logging

logger = logging.getLogger(__name__)


@dataclass
class BenchmarkResult:
    """Result of a benchmark run."""
    name: str
    module: str
    iterations: int
    total_time_ms: float
    min_time_ms: float = 0.0
    max_time_ms: float = 0.0
    mean_time_ms: float = 0.0
    median_time_ms: float = 0.0
    stddev_ms: float = 0.0
    throughput: float = 0.0  # operations per second
    timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
    
    def __str__(self) -> str:
        """Format result as string."""
        return (
            f"{self.name:<40} "
            f"{self.mean_time_ms:>8.3f}ms "
            f"(±{self.stddev_ms:.3f}ms) "
            f"[{self.iterations} iterations]"
        )


class BenchmarkSuite:
    """Run and track performance benchmarks."""
    
    def __init__(self, module_name: str):
        self.module_name = module_name
        self.results: List[BenchmarkResult] = []
        
    def benchmark(
        self,
        name: str,
        func: Callable,
        iterations: int = 100,
        *args,
        **kwargs
    ) -> BenchmarkResult:
        """Run a function benchmark.
        
        Args:
            name: Benchmark name
            func: Function to benchmark
            iterations: Number of iterations
            args: Function arguments
            kwargs: Function keyword arguments
            
        Returns:
            BenchmarkResult
        """
        times = []
        
        for _ in range(iterations):
            start = time.perf_counter()
            func(*args, **kwargs)
            end = time.perf_counter()
            times.append((end - start) * 1000)  # Convert to ms
            
        total_time = sum(times)
        
        result = BenchmarkResult(
            name=name,
            module=self.module_name,
            iterations=iterations,
            total_time_ms=total_time,
            min_time_ms=min(times),
            max_time_ms=max(times),
            mean_time_ms=statistics.mean(times),
            median_time_ms=statistics.median(times),
            stddev_ms=statistics.stdev(times) if len(times) > 1 else 0,
            throughput=iterations / (total_time / 1000)  # ops/sec
        )
        
        self.results.append(result)
        logger.info(f"Benchmark: {result}")
        return result
        
    def benchmark_async(
        self,
        name: str,
        async_func: Callable,
        iterations: int = 100,
        *args,
        **kwargs
    ) -> BenchmarkResult:
        """Benchmark an async function.
        
        Args:
            name: Benchmark name
            async_func: Async function to benchmark
            iterations: Number of iterations
            args: Function arguments
            kwargs: Function keyword arguments
            
        Returns:
            BenchmarkResult
        """
        import asyncio
        
        async def run_benchmark():
            times = []
            for _ in range(iterations):
                start = time.perf_counter()
                await async_func(*args, **kwargs)
                end = time.perf_counter()
                times.append((end - start) * 1000)
            return times
            
        try:
            loop = asyncio.get_event_loop()
            if loop.is_closed():
                loop = asyncio.new_event_loop()
                asyncio.set_event_loop(loop)
        except RuntimeError:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            
        times = loop.run_until_complete(run_benchmark())
        total_time = sum(times)
        
        result = BenchmarkResult(
            name=name,
            module=self.module_name,
            iterations=iterations,
            total_time_ms=total_time,
            min_time_ms=min(times),
            max_time_ms=max(times),
            mean_time_ms=statistics.mean(times),
            median_time_ms=statistics.median(times),
            stddev_ms=statistics.stdev(times) if len(times) > 1 else 0,
            throughput=iterations / (total_time / 1000)
        )
        
        self.results.append(result)
        logger.info(f"Async Benchmark: {result}")
        return result
        
    def get_report(self) -> str:
        """Generate benchmark report."""
        report = f"\nBenchmark Report: {self.module_name}\n"
        report += "=" * 100 + "\n\n"
        
        if not self.results:
            report += "No benchmarks run.\n"
            return report
            
        # Summary statistics
        all_times = [r.mean_time_ms for r in self.results]
        
        report += f"Results ({len(self.results)} benchmarks):\n"
        report += "-" * 100 + "\n"
        
        for result in self.results:
            report += f"{result}\n"
            
        report += "\n" + "-" * 100 + "\n"
        report += f"Fastest:  {min(all_times):.3f}ms\n"
        report += f"Slowest:  {max(all_times):.3f}ms\n"
        report += f"Average:  {statistics.mean(all_times):.3f}ms\n"
        report += "=" * 100 + "\n"
        
        return report


class CodeAnalysisBenchmarks:
    """Benchmarks for code vulnerability scanning."""
    
    @staticmethod
    def create_suite() -> BenchmarkSuite:
        """Create code analysis benchmark suite."""
        suite = BenchmarkSuite("Code Analysis")
        
        # Mock functions for demonstration
        def scan_file_simple():
            """Simulate simple file scan."""
            time.sleep(0.001)  # 1ms
            
        def scan_file_thorough():
            """Simulate thorough file scan."""
            time.sleep(0.005)  # 5ms
            
        def pattern_matching():
            """Simulate pattern matching."""
            patterns = ["SQL_INJECTION", "HARDCODED_SECRET", "XSS"]
            for _ in range(100):
                for pattern in patterns:
                    pattern in ["SQL_INJECTION", "HARDCODED_SECRET", "XSS"]
                    
        def cwe_lookup():
            """Simulate CWE database lookup."""
            cwe_db = {f"CWE-{i}": f"Description {i}" for i in range(1000)}
            for i in range(100):
                cwe_db.get(f"CWE-{i}")
                
        # Run benchmarks
        suite.benchmark("Simple File Scan", scan_file_simple, iterations=100)
        suite.benchmark("Thorough File Scan", scan_file_thorough, iterations=100)
        suite.benchmark("Pattern Matching", pattern_matching, iterations=50)
        suite.benchmark("CWE Lookup", cwe_lookup, iterations=100)
        
        return suite


class CryptographyBenchmarks:
    """Benchmarks for cryptography analysis."""
    
    @staticmethod
    def create_suite() -> BenchmarkSuite:
        """Create cryptography benchmark suite."""
        suite = BenchmarkSuite("Cryptography")
        
        def algorithm_strength_calc():
            """Calculate algorithm strength score."""
            algorithms = {
                'AES-256': 256,
                'RSA-4096': 4096,
                'ECDSA-384': 384,
                'SHA-256': 256
            }
            scores = []
            for algo, strength in algorithms.items():
                score = strength / 2.0
                scores.append(score)
                
        def key_size_validation():
            """Validate key sizes."""
            key_sizes = [256, 512, 1024, 2048, 4096, 8192]
            valid = []
            for size in key_sizes:
                is_valid = size >= 2048
                valid.append(is_valid)
                
        def tls_version_check():
            """Check TLS versions."""
            versions = ['1.0', '1.1', '1.2', '1.3']
            minimum_version = '1.2'
            for version in versions:
                version >= minimum_version
                
        def weakness_detection():
            """Detect weak cryptography."""
            weak_algos = ['DES', 'MD5', 'SHA-1', 'RC4']
            found = []
            for algo in ['AES', 'MD5', 'SHA-256', 'RSA']:
                if algo in weak_algos:
                    found.append(algo)
                    
        suite.benchmark("Algorithm Strength Calc", algorithm_strength_calc, 100)
        suite.benchmark("Key Size Validation", key_size_validation, 100)
        suite.benchmark("TLS Version Check", tls_version_check, 100)
        suite.benchmark("Weakness Detection", weakness_detection, 100)
        
        return suite


class NetworkMonitoringBenchmarks:
    """Benchmarks for network monitoring operations."""
    
    @staticmethod
    def create_suite() -> BenchmarkSuite:
        """Create network monitoring benchmark suite."""
        suite = BenchmarkSuite("Network Monitoring")
        
        def port_scan_check():
            """Simulate port scan."""
            for port in range(1, 1001):
                is_open = port in [22, 80, 443, 3306]
                
        def service_identification():
            """Identify services."""
            port_services = {
                22: 'SSH',
                80: 'HTTP',
                443: 'HTTPS',
                3306: 'MySQL',
                5432: 'PostgreSQL'
            }
            for port in [22, 80, 443, 3306]:
                service = port_services.get(port, 'Unknown')
                
        def ip_validation():
            """Validate IP addresses."""
            ips = ['192.168.1.1', '10.0.0.1', '172.16.0.1']
            for ip in ips:
                parts = ip.split('.')
                valid = len(parts) == 4
                
        def tor_exit_check():
            """Check Tor exit nodes."""
            tor_ips = ['192.0.2.1', '198.51.100.1']
            suspicious = []
            for ip in ['192.0.2.1', '10.0.0.1']:
                if ip in tor_ips:
                    suspicious.append(ip)
                    
        suite.benchmark("Port Scan Check", port_scan_check, 100)
        suite.benchmark("Service Identification", service_identification, 100)
        suite.benchmark("IP Validation", ip_validation, 100)
        suite.benchmark("Tor Exit Check", tor_exit_check, 100)
        
        return suite


class DatabaseBenchmarks:
    """Benchmarks for database operations."""
    
    @staticmethod
    def create_suite() -> BenchmarkSuite:
        """Create database benchmark suite."""
        suite = BenchmarkSuite("Database")
        
        def single_insert():
            """Simulate single record insert."""
            record = {
                'id': 1,
                'name': 'Test',
                'severity': 'HIGH',
                'timestamp': datetime.now().isoformat()
            }
            
        def batch_insert():
            """Simulate batch insert."""
            records = [
                {
                    'id': i,
                    'name': f'Test_{i}',
                    'severity': 'HIGH',
                    'timestamp': datetime.now().isoformat()
                }
                for i in range(100)
            ]
            
        def query_by_index():
            """Simulate indexed query."""
            index = {i: f"vuln_{i}" for i in range(1000)}
            for i in range(100):
                result = index.get(i)
                
        def join_operation():
            """Simulate table join."""
            vulns = [{'id': i, 'scan_id': i % 10} for i in range(100)]
            scans = [{'id': i, 'risk': 7.5} for i in range(10)]
            
            joined = []
            for v in vulns:
                for s in scans:
                    if v['scan_id'] == s['id']:
                        joined.append({**v, **s})
                        
        suite.benchmark("Single Insert", single_insert, 100)
        suite.benchmark("Batch Insert (100 records)", batch_insert, 50)
        suite.benchmark("Query by Index", query_by_index, 100)
        suite.benchmark("Join Operation", join_operation, 50)
        
        return suite


class CachingBenchmarks:
    """Benchmarks for caching performance."""
    
    @staticmethod
    def create_suite() -> BenchmarkSuite:
        """Create caching benchmark suite."""
        suite = BenchmarkSuite("Caching")
        
        from chronos.performance.caching import InMemoryCache
        
        cache = InMemoryCache()
        
        def cache_hit():
            """Simulate cache hit."""
            cache.set("key_1", "value_1")
            value = cache.get("key_1")
            
        def cache_miss():
            """Simulate cache miss."""
            value = cache.get("nonexistent_key")
            
        def cache_eviction():
            """Simulate cache eviction."""
            for i in range(100):
                cache.set(f"key_{i}", f"value_{i}")
                
        suite.benchmark("Cache Hit", cache_hit, 1000)
        suite.benchmark("Cache Miss", cache_miss, 1000)
        suite.benchmark("Cache Eviction", cache_eviction, 100)
        
        return suite


def run_all_benchmarks() -> Dict[str, BenchmarkSuite]:
    """Run all benchmark suites.
    
    Returns:
        Dictionary of module name -> BenchmarkSuite
    """
    suites = {
        'code_analysis': CodeAnalysisBenchmarks.create_suite(),
        'cryptography': CryptographyBenchmarks.create_suite(),
        'network_monitoring': NetworkMonitoringBenchmarks.create_suite(),
        'database': DatabaseBenchmarks.create_suite(),
        'caching': CachingBenchmarks.create_suite(),
    }
    
    return suites


def generate_benchmark_report(suites: Dict[str, BenchmarkSuite]) -> str:
    """Generate comprehensive benchmark report.
    
    Args:
        suites: Dictionary of benchmark suites
        
    Returns:
        Formatted report string
    """
    report = "\n" + "=" * 100 + "\n"
    report += "CHRONOS PERFORMANCE BENCHMARK REPORT\n"
    report += "=" * 100 + "\n"
    
    for module_name, suite in suites.items():
        report += suite.get_report()
        
    return report


def save_benchmark_results(
    suites: Dict[str, BenchmarkSuite],
    filepath: str
) -> None:
    """Save benchmark results to JSON file.
    
    Args:
        suites: Dictionary of benchmark suites
        filepath: File path to save results
    """
    all_results = []
    
    for suite in suites.values():
        for result in suite.results:
            all_results.append({
                'name': result.name,
                'module': result.module,
                'iterations': result.iterations,
                'mean_time_ms': result.mean_time_ms,
                'min_time_ms': result.min_time_ms,
                'max_time_ms': result.max_time_ms,
                'stddev_ms': result.stddev_ms,
                'throughput_ops_per_sec': result.throughput,
                'timestamp': result.timestamp
            })
            
    with open(filepath, 'w') as f:
        json.dump(all_results, f, indent=2)
        
    logger.info(f"Benchmark results saved to {filepath}")


def compare_benchmarks(
    previous_results: str,
    current_results: str
) -> Dict[str, float]:
    """Compare benchmark results across runs.
    
    Args:
        previous_results: Path to previous benchmark JSON
        current_results: Path to current benchmark JSON
        
    Returns:
        Dictionary of performance changes
    """
    import json
    
    with open(previous_results) as f:
        prev = json.load(f)
        
    with open(current_results) as f:
        curr = json.load(f)
        
    changes = {}
    
    for curr_result in curr:
        for prev_result in prev:
            if curr_result['name'] == prev_result['name']:
                change = (
                    (curr_result['mean_time_ms'] - prev_result['mean_time_ms'])
                    / prev_result['mean_time_ms']
                    * 100
                )
                changes[curr_result['name']] = change
                
    return changes
