//! Advanced profiling capabilities for Beta 2

use crate::profiling::entries::{MemoryEntry, TimingEntry};
use crate::profiling::profiler::Profiler;
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::fs::File;
use std::io::{BufWriter, Write};
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};

/// Flame graph data structure for visualizing call hierarchies
#[derive(Debug, Clone)]
pub struct FlameGraphNode {
    /// Function name
    pub name: String,
    /// Total execution time
    pub total_time: Duration,
    /// Self execution time (excluding children)
    pub self_time: Duration,
    /// Number of samples
    pub samples: u64,
    /// Child nodes
    pub children: BTreeMap<String, FlameGraphNode>,
    /// Call depth
    pub depth: usize,
}

impl FlameGraphNode {
    /// Create a new flame graph node
    pub fn new(name: String, depth: usize) -> Self {
        Self {
            name,
            total_time: Duration::from_secs(0),
            self_time: Duration::from_secs(0),
            samples: 0,
            children: BTreeMap::new(),
            depth,
        }
    }

    /// Add a sample to this node
    pub fn add_sample(&mut self, duration: Duration) {
        self.total_time += duration;
        self.samples += 1;
    }

    /// Calculate self time by subtracting children's time
    pub fn calculate_self_time(&mut self) {
        let children_time: Duration = self.children.values().map(|child| child.total_time).sum();
        self.self_time = self.total_time.saturating_sub(children_time);

        // Recursively calculate for children
        for child in self.children.values_mut() {
            child.calculate_self_time();
        }
    }

    /// Generate flame graph format output
    pub fn to_flame_graph_format(&self, prefix: &str) -> Vec<String> {
        let mut lines = Vec::new();
        let current_stack = if prefix.is_empty() {
            self.name.clone()
        } else {
            format!("{prefix};{}", self.name)
        };

        if self.self_time.as_nanos() > 0 {
            {
                let nanos = self.self_time.as_nanos();
                lines.push(format!("{current_stack} {nanos}"));
            }
        }

        for child in self.children.values() {
            lines.extend(child.to_flame_graph_format(&current_stack));
        }

        lines
    }
}

/// Flame graph generator
#[derive(Debug)]
pub struct FlameGraphGenerator {
    /// Root node of the flame graph
    root: FlameGraphNode,
    /// Current call stack
    call_stack: Vec<String>,
    /// Stack of start times
    time_stack: Vec<Instant>,
}

impl FlameGraphGenerator {
    /// Create a new flame graph generator
    pub fn new() -> Self {
        Self {
            root: FlameGraphNode::new("root".to_string(), 0),
            call_stack: Vec::new(),
            time_stack: Vec::new(),
        }
    }

    /// Start a new function call
    pub fn start_call(&mut self, functionname: &str) {
        self.call_stack.push(functionname.to_string());
        self.time_stack.push(Instant::now());
    }

    /// End the current function call
    pub fn end_call(&mut self) {
        if let (Some(_function_name), Some(start_time)) =
            (self.call_stack.pop(), self.time_stack.pop())
        {
            let duration = start_time.elapsed();

            // Navigate to the correct node in the tree
            let mut current_node = &mut self.root;
            for (_depth, name) in self.call_stack.iter().enumerate() {
                current_node = current_node
                    .children
                    .entry(name.clone())
                    .or_insert_with(|| FlameGraphNode::new(name.clone(), _depth + 1));
            }

            // Add the sample
            current_node.add_sample(duration);
        }
    }

    /// Generate the flame graph
    pub fn generate(&mut self) -> FlameGraphNode {
        self.root.calculate_self_time();
        self.root.clone()
    }

    /// Export flame graph to file
    pub fn export_to_file(&mut self, path: &str) -> Result<(), std::io::Error> {
        let flame_graph = self.generate();
        let lines = flame_graph.to_flame_graph_format("");

        let file = File::create(path)?;
        let mut writer = BufWriter::new(file);

        for line in lines {
            writeln!(writer, "{line}")?;
        }

        writer.flush()?;
        Ok(())
    }
}

impl Default for FlameGraphGenerator {
    fn default() -> Self {
        Self::new()
    }
}

/// Performance bottleneck detection configuration
#[derive(Debug, Clone)]
pub struct BottleneckConfig {
    /// Minimum execution time threshold (operations slower than this are considered bottlenecks)
    pub min_execution_threshold: Duration,
    /// Memory usage threshold (operations using more memory than this are flagged)
    pub memory_threshold: usize,
    /// CPU usage threshold (0.0 to 1.0)
    pub cpu_threshold: f64,
    /// Minimum number of calls to consider for bottleneck analysis
    pub min_calls: usize,
    /// Enable automatic suggestions
    pub enable_suggestions: bool,
}

impl Default for BottleneckConfig {
    fn default() -> Self {
        Self {
            min_execution_threshold: Duration::from_millis(100),
            memory_threshold: 1024 * 1024, // 1 MB
            cpu_threshold: 0.8,            // 80%
            min_calls: 5,
            enable_suggestions: true,
        }
    }
}

/// Bottleneck detection result
#[derive(Debug, Clone)]
pub struct BottleneckReport {
    /// Operation name
    pub operation: String,
    /// Bottleneck type
    pub bottleneck_type: BottleneckType,
    /// Severity score (0.0 to 1.0, higher is more severe)
    pub severity: f64,
    /// Description of the issue
    pub description: String,
    /// Optimization suggestions
    pub suggestions: Vec<String>,
    /// Performance statistics
    pub stats: PerformanceStats,
}

/// Type of bottleneck detected
#[derive(Debug, Clone, PartialEq)]
pub enum BottleneckType {
    /// Slow execution time
    SlowExecution,
    /// High memory usage
    HighMemoryUsage,
    /// High CPU usage
    HighCpuUsage,
    /// Frequent calls (hot path)
    HotPath,
    /// Memory leaks
    MemoryLeak,
    /// Inefficient algorithm
    IneffientAlgorithm,
}

/// Performance statistics for bottleneck analysis
#[derive(Debug, Clone)]
pub struct PerformanceStats {
    /// Total calls
    pub calls: usize,
    /// Total execution time
    pub total_time: Duration,
    /// Average execution time
    pub avg_time: Duration,
    /// Maximum execution time
    pub max_time: Duration,
    /// Total memory usage
    pub total_memory: usize,
    /// Average memory usage
    pub avg_memory: f64,
    /// Maximum memory usage
    pub max_memory: usize,
    /// CPU utilization
    pub cpu_utilization: f64,
}

/// Automated bottleneck detector
#[derive(Debug)]
pub struct BottleneckDetector {
    /// Configuration for detection
    config: BottleneckConfig,
    /// Performance history
    #[allow(dead_code)]
    performance_history: HashMap<String, Vec<PerformanceStats>>,
}

impl BottleneckDetector {
    /// Create a new bottleneck detector
    pub fn new(config: BottleneckConfig) -> Self {
        Self {
            config,
            performance_history: HashMap::new(),
        }
    }

    /// Analyze profiling data for bottlenecks
    pub fn analyze(&mut self, profiler: &Profiler) -> Vec<BottleneckReport> {
        let mut reports = Vec::new();

        // Analyze timing data
        for (operation, timing_entry) in profiler.timings() {
            if timing_entry.calls() >= self.config.min_calls {
                let stats = PerformanceStats {
                    calls: timing_entry.calls(),
                    total_time: timing_entry.total_duration(),
                    avg_time: timing_entry.average_duration(),
                    max_time: timing_entry.max_duration(),
                    total_memory: 0, // Would be populated from memory tracking
                    avg_memory: 0.0,
                    max_memory: 0,
                    cpu_utilization: 0.0, // Would be populated from CPU monitoring
                };

                // Check for slow execution
                if stats.avg_time > self.config.min_execution_threshold {
                    let severity = (stats.avg_time.as_secs_f64()
                        / self.config.min_execution_threshold.as_secs_f64())
                    .min(1.0);
                    let mut suggestions = Vec::new();

                    if self.config.enable_suggestions {
                        suggestions.extend([
                            "Consider algorithm optimization".to_string(),
                            "Profile inner functions for specific bottlenecks".to_string(),
                            "Check for unnecessary allocations".to_string(),
                            "Consider parallel processing if applicable".to_string(),
                        ]);
                    }

                    reports.push(BottleneckReport {
                        operation: operation.clone(),
                        bottleneck_type: BottleneckType::SlowExecution,
                        severity,
                        description: format!(
                            "Operation '{}' takes {:.2}ms on average, which exceeds the threshold of {:.2}ms",
                            operation,
                            stats.avg_time.as_secs_f64() * 1000.0,
                            self.config.min_execution_threshold.as_secs_f64() * 1000.0
                        ),
                        suggestions,
                        stats: stats.clone(),
                    });
                }

                // Check for hot paths (frequent calls)
                if stats.calls > 1000 {
                    let severity = (stats.calls as f64 / 10000.0).min(1.0);
                    let mut suggestions = Vec::new();

                    if self.config.enable_suggestions {
                        suggestions.extend([
                            "Consider caching results if applicable".to_string(),
                            "Look for opportunities to batch operations".to_string(),
                            "Profile for micro-optimizations".to_string(),
                            "Consider memoization for pure functions".to_string(),
                        ]);
                    }

                    reports.push(BottleneckReport {
                        operation: operation.clone(),
                        bottleneck_type: BottleneckType::HotPath,
                        severity,
                        description: format!(
                            "Operation '{}' is called {} times, indicating a hot path",
                            operation, stats.calls
                        ),
                        suggestions,
                        stats,
                    });
                }
            }
        }

        // Analyze memory data
        for (operation, memory_entry) in profiler.memory() {
            if memory_entry.allocations() >= self.config.min_calls {
                let avg_memory =
                    memory_entry.total_delta() as f64 / memory_entry.allocations() as f64;

                if memory_entry.max_delta() > self.config.memory_threshold {
                    let severity = (memory_entry.max_delta() as f64
                        / (self.config.memory_threshold as f64 * 2.0))
                        .min(1.0);
                    let mut suggestions = Vec::new();

                    if self.config.enable_suggestions {
                        suggestions.extend([
                            "Consider pre-allocating memory where possible".to_string(),
                            "Look for opportunities to reuse memory".to_string(),
                            "Check for memory leaks".to_string(),
                            "Consider using memory pools".to_string(),
                        ]);
                    }

                    reports.push(BottleneckReport {
                        operation: operation.clone(),
                        bottleneck_type: BottleneckType::HighMemoryUsage,
                        severity,
                        description: format!(
                            "Operation '{}' uses up to {:.2}MB of memory, exceeding threshold of {:.2}MB",
                            operation,
                            memory_entry.max_delta() as f64 / 1024.0 / 1024.0,
                            self.config.memory_threshold as f64 / 1024.0 / 1024.0
                        ),
                        suggestions,
                        stats: PerformanceStats {
                            calls: memory_entry.allocations(),
                            total_time: Duration::from_secs(0),
                            avg_time: Duration::from_secs(0),
                            max_time: Duration::from_secs(0),
                            total_memory: memory_entry.total_delta() as usize,
                            avg_memory,
                            max_memory: memory_entry.max_delta(),
                            cpu_utilization: 0.0,
                        },
                    });
                }
            }
        }

        reports
    }

    /// Print bottleneck report
    pub fn print_report(&self, reports: &[BottleneckReport]) {
        if reports.is_empty() {
            println!("No performance bottlenecks detected.");
            return;
        }

        println!("\n=== Bottleneck Analysis Report ===");

        for report in reports {
            println!("\n🔍 Operation: {}", report.operation);
            println!("   Type: {:?}", report.bottleneck_type);
            println!("   Severity: {:.1}%", report.severity * 100.0);
            println!("   Description: {}", report.description);

            if !report.suggestions.is_empty() {
                println!("   Suggestions:");
                for suggestion in &report.suggestions {
                    println!("     • {suggestion}");
                }
            }

            println!("   Stats:");
            println!("     • Calls: {}", report.stats.calls);
            if report.stats.total_time.as_nanos() > 0 {
                println!(
                    "     • Avg Time: {:.2}ms",
                    report.stats.avg_time.as_secs_f64() * 1000.0
                );
                println!(
                    "     • Max Time: {:.2}ms",
                    report.stats.max_time.as_secs_f64() * 1000.0
                );
            }
            if report.stats.total_memory > 0 {
                println!(
                    "     • Avg Memory: {:.2}KB",
                    report.stats.avg_memory / 1024.0
                );
                println!(
                    "     • Max Memory: {:.2}KB",
                    report.stats.max_memory as f64 / 1024.0
                );
            }
        }
    }
}

impl Default for BottleneckDetector {
    fn default() -> Self {
        Self::new(BottleneckConfig::default())
    }
}

/// System resource monitor for tracking CPU, memory, and network usage
#[derive(Debug)]
pub struct SystemResourceMonitor {
    /// Monitoring interval
    interval: Duration,
    /// Whether monitoring is active
    active: Arc<std::sync::atomic::AtomicBool>,
    /// CPU usage history
    cpu_history: Arc<std::sync::Mutex<VecDeque<f64>>>,
    /// Memory usage history
    memory_history: Arc<std::sync::Mutex<VecDeque<usize>>>,
    /// Network I/O history (bytes)
    network_history: Arc<std::sync::Mutex<VecDeque<(u64, u64)>>>, // (bytes_in, bytes_out)
}

impl SystemResourceMonitor {
    /// Create a new system resource monitor
    pub fn new(interval: Duration) -> Self {
        Self {
            interval,
            active: Arc::new(std::sync::atomic::AtomicBool::new(false)),
            cpu_history: Arc::new(std::sync::Mutex::new(VecDeque::new())),
            memory_history: Arc::new(std::sync::Mutex::new(VecDeque::new())),
            network_history: Arc::new(std::sync::Mutex::new(VecDeque::new())),
        }
    }

    /// Start monitoring system resources
    pub fn start(&self) {
        self.active.store(true, Ordering::Relaxed);

        let active = self.active.clone();
        let cpu_history = self.cpu_history.clone();
        let memory_history = self.memory_history.clone();
        let network_history = self.network_history.clone();
        let interval = self.interval;

        thread::spawn(move || {
            while active.load(Ordering::Relaxed) {
                // Sample CPU usage
                let cpu_usage = Self::get_cpu_usage();
                if let Ok(mut cpu_hist) = cpu_history.lock() {
                    cpu_hist.push_back(cpu_usage);
                    if cpu_hist.len() > 1000 {
                        cpu_hist.pop_front();
                    }
                }

                // Sample memory usage
                let memory_usage = Self::get_memory_usage();
                if let Ok(mut mem_hist) = memory_history.lock() {
                    mem_hist.push_back(memory_usage);
                    if mem_hist.len() > 1000 {
                        mem_hist.pop_front();
                    }
                }

                // Sample network usage
                let network_usage = Self::get_network_usage();
                if let Ok(mut net_hist) = network_history.lock() {
                    net_hist.push_back(network_usage);
                    if net_hist.len() > 1000 {
                        net_hist.pop_front();
                    }
                }

                thread::sleep(interval);
            }
        });
    }

    /// Stop monitoring
    pub fn stop(&self) {
        self.active.store(false, Ordering::Relaxed);
    }

    /// Get current CPU usage (0.0 to 1.0)
    fn get_cpu_usage() -> f64 {
        // This is a simplified implementation
        // In a real implementation, you would use platform-specific APIs
        #[cfg(target_os = "linux")]
        {
            // On Linux, parse /proc/stat
            0.5 // Placeholder
        }

        #[cfg(target_os = "macos")]
        {
            // On macOS, use host_processor_info
            0.5 // Placeholder
        }

        #[cfg(target_os = "windows")]
        {
            // On Windows, use GetSystemTimes
            0.5 // Placeholder
        }

        #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
        {
            0.5 // Fallback placeholder
        }
    }

    /// Get current memory usage in bytes
    fn get_memory_usage() -> usize {
        // Simplified implementation - would use platform-specific APIs
        1024 * 1024 * 512 // 512 MB placeholder
    }

    /// Get current network usage (bytes_in, bytes_out)
    fn get_network_usage() -> (u64, u64) {
        // Simplified implementation - would parse /proc/net/dev on Linux
        (1024, 1024) // Placeholder
    }

    /// Get resource usage statistics
    pub fn get_stats(&self) -> ResourceStats {
        let cpu_hist = self.cpu_history.lock().expect("Operation failed");
        let memory_hist = self.memory_history.lock().expect("Operation failed");
        let network_hist = self.network_history.lock().expect("Operation failed");

        let avg_cpu = if cpu_hist.is_empty() {
            0.0
        } else {
            cpu_hist.iter().sum::<f64>() / cpu_hist.len() as f64
        };

        let max_cpu = cpu_hist.iter().fold(0.0f64, |a, &b| a.max(b));

        let avg_memory = if memory_hist.is_empty() {
            0
        } else {
            memory_hist.iter().sum::<usize>() / memory_hist.len()
        };

        let max_memory = memory_hist.iter().max().copied().unwrap_or(0);

        let total_network_in: u64 = network_hist.iter().map(|(bytes_in_, _)| *bytes_in_).sum();
        let total_network_out: u64 = network_hist.iter().map(|(_, bytes_out)| *bytes_out).sum();

        ResourceStats {
            avg_cpu_usage: avg_cpu,
            max_cpu_usage: max_cpu,
            avg_memory_usage: avg_memory,
            max_memory_usage: max_memory,
            total_network_in,
            total_network_out,
            sample_count: cpu_hist.len(),
        }
    }
}

impl Default for SystemResourceMonitor {
    fn default() -> Self {
        Self::new(Duration::from_secs(1))
    }
}

/// Resource usage statistics
#[derive(Debug, Clone)]
pub struct ResourceStats {
    /// Average CPU usage (0.0 to 1.0)
    pub avg_cpu_usage: f64,
    /// Maximum CPU usage (0.0 to 1.0)
    pub max_cpu_usage: f64,
    /// Average memory usage (bytes)
    pub avg_memory_usage: usize,
    /// Maximum memory usage (bytes)
    pub max_memory_usage: usize,
    /// Total network bytes received
    pub total_network_in: u64,
    /// Total network bytes sent
    pub total_network_out: u64,
    /// Number of samples collected
    pub sample_count: usize,
}

/// Differential profiler for comparing performance between runs
#[derive(Debug)]
pub struct DifferentialProfiler {
    /// Baseline profiling data
    baseline: Option<ProfileSnapshot>,
    /// Current profiling data
    current: Option<ProfileSnapshot>,
}

/// Snapshot of profiling data at a point in time
#[derive(Debug, Clone)]
pub struct ProfileSnapshot {
    /// Timing data
    pub timings: HashMap<String, TimingEntry>,
    /// Memory data
    pub memory: HashMap<String, MemoryEntry>,
    /// Resource usage at snapshot time
    pub resources: Option<ResourceStats>,
    /// Timestamp of snapshot
    pub timestamp: std::time::Instant,
    /// Optional label for the snapshot
    pub label: Option<String>,
}

impl DifferentialProfiler {
    /// Create a new differential profiler
    pub fn new() -> Self {
        Self {
            baseline: None,
            current: None,
        }
    }

    /// Set the baseline snapshot
    pub fn setbaseline(&mut self, profiler: &Profiler, label: Option<String>) {
        self.baseline = Some(ProfileSnapshot {
            timings: profiler.timings().clone(),
            memory: profiler.memory().clone(),
            resources: None,
            timestamp: std::time::Instant::now(),
            label,
        });
    }

    /// Set the current snapshot
    pub fn set_current(&mut self, profiler: &Profiler, label: Option<String>) {
        self.current = Some(ProfileSnapshot {
            timings: profiler.timings().clone(),
            memory: profiler.memory().clone(),
            resources: None,
            timestamp: std::time::Instant::now(),
            label,
        });
    }

    /// Generate a differential report
    pub fn generate_diff_report(&self) -> Option<DifferentialReport> {
        if let (Some(baseline), Some(current)) = (&self.baseline, &self.current) {
            Some(DifferentialReport::new(baseline, current))
        } else {
            None
        }
    }
}

impl Default for DifferentialProfiler {
    fn default() -> Self {
        Self::new()
    }
}

/// Differential profiling report
#[derive(Debug)]
pub struct DifferentialReport {
    /// Timing differences
    pub timing_diffs: HashMap<String, TimingDiff>,
    /// Memory differences
    pub memory_diffs: HashMap<String, MemoryDiff>,
    /// Overall performance change
    pub overall_change: PerformanceChange,
    /// Report generation timestamp
    pub generated_at: std::time::Instant,
}

impl DifferentialReport {
    /// Create a new differential report
    pub fn new(baseline: &ProfileSnapshot, current: &ProfileSnapshot) -> Self {
        let mut timing_diffs = HashMap::new();
        let mut memory_diffs = HashMap::new();

        // Calculate timing differences
        for (operation, current_timing) in &current.timings {
            if let Some(baseline_timing) = baseline.timings.get(operation) {
                timing_diffs.insert(
                    operation.clone(),
                    TimingDiff::new(baseline_timing, current_timing),
                );
            }
        }

        // Calculate memory differences
        for (operation, current_memory) in &current.memory {
            if let Some(baseline_memory) = baseline.memory.get(operation) {
                memory_diffs.insert(
                    operation.clone(),
                    MemoryDiff::new(baseline_memory, current_memory),
                );
            }
        }

        // Calculate overall performance change
        let overall_change = PerformanceChange::calculate(&timing_diffs, &memory_diffs);

        Self {
            timing_diffs,
            memory_diffs,
            overall_change,
            generated_at: std::time::Instant::now(),
        }
    }

    /// Print the differential report
    pub fn print(&self) {
        println!("\n=== Differential Profiling Report ===");

        if !self.timing_diffs.is_empty() {
            println!("\nTiming Changes:");
            println!(
                "{:<30} {:<15} {:<15} {:<15}",
                "Operation", "Baseline (ms)", "Current (ms)", "Change (%)"
            );
            println!("{}", "-".repeat(80));

            for (operation, diff) in &self.timing_diffs {
                println!(
                    "{:<30} {:<15.2} {:<15.2} {:>+14.1}%",
                    operation,
                    diff.baseline_avg.as_secs_f64() * 1000.0,
                    diff.current_avg.as_secs_f64() * 1000.0,
                    diff.percentage_change
                );
            }
        }

        if !self.memory_diffs.is_empty() {
            println!("\nMemory Changes:");
            println!(
                "{:<30} {:<15} {:<15} {:<15}",
                "Operation", "Baseline (KB)", "Current (KB)", "Change (%)"
            );
            println!("{}", "-".repeat(80));

            for (operation, diff) in &self.memory_diffs {
                println!(
                    "{:<30} {:<15.2} {:<15.2} {:>+14.1}%",
                    operation,
                    diff.baseline_avg / 1024.0,
                    diff.current_avg / 1024.0,
                    diff.percentage_change
                );
            }
        }

        println!("\nOverall Performance:");
        println!(
            "  • Timing Change: {:+.1}%",
            self.overall_change.timing_change
        );
        println!(
            "  • Memory Change: {:+.1}%",
            self.overall_change.memory_change
        );
        println!("  • Recommendation: {}", self.overall_change.recommendation);
    }
}

/// Timing difference between baseline and current
#[derive(Debug)]
pub struct TimingDiff {
    /// Baseline average duration
    pub baseline_avg: Duration,
    /// Current average duration
    pub current_avg: Duration,
    /// Percentage change (positive = slower, negative = faster)
    pub percentage_change: f64,
}

impl TimingDiff {
    /// Create a new timing difference
    pub fn new(baseline: &TimingEntry, current: &TimingEntry) -> Self {
        let baseline_avg = baseline.average_duration();
        let current_avg = current.average_duration();
        let percentage_change = if baseline_avg.as_nanos() > 0 {
            ((current_avg.as_nanos() as f64 - baseline_avg.as_nanos() as f64)
                / baseline_avg.as_nanos() as f64)
                * 100.0
        } else {
            0.0
        };

        Self {
            baseline_avg,
            current_avg,
            percentage_change,
        }
    }
}

/// Memory difference between baseline and current
#[derive(Debug)]
pub struct MemoryDiff {
    /// Baseline average memory usage
    pub baseline_avg: f64,
    /// Current average memory usage
    pub current_avg: f64,
    /// Percentage change (positive = more memory, negative = less memory)
    pub percentage_change: f64,
}

impl MemoryDiff {
    /// Create a new memory difference
    pub fn new(baseline: &MemoryEntry, current: &MemoryEntry) -> Self {
        let baseline_avg = if baseline.allocations() > 0 {
            baseline.total_delta() as f64 / baseline.allocations() as f64
        } else {
            0.0
        };

        let current_avg = if current.allocations() > 0 {
            current.total_delta() as f64 / current.allocations() as f64
        } else {
            0.0
        };

        let percentage_change = if baseline_avg.abs() > 0.0 {
            ((current_avg - baseline_avg) / baseline_avg.abs()) * 100.0
        } else {
            0.0
        };

        Self {
            baseline_avg,
            current_avg,
            percentage_change,
        }
    }
}

/// Overall performance change summary
#[derive(Debug)]
pub struct PerformanceChange {
    /// Overall timing change percentage
    pub timing_change: f64,
    /// Overall memory change percentage
    pub memory_change: f64,
    /// Performance recommendation
    pub recommendation: String,
}

impl PerformanceChange {
    /// Calculate overall performance change
    pub fn calculate(
        timing_diffs: &HashMap<String, TimingDiff>,
        memory_diffs: &HashMap<String, MemoryDiff>,
    ) -> Self {
        let timing_change = if timing_diffs.is_empty() {
            0.0
        } else {
            timing_diffs
                .values()
                .map(|diff| diff.percentage_change)
                .sum::<f64>()
                / timing_diffs.len() as f64
        };

        let memory_change = if memory_diffs.is_empty() {
            0.0
        } else {
            memory_diffs
                .values()
                .map(|diff| diff.percentage_change)
                .sum::<f64>()
                / memory_diffs.len() as f64
        };

        let recommendation = match (timing_change > 5.0, memory_change > 10.0) {
            (true, true) => "Performance degraded significantly in both time and memory. Review recent changes.".to_string(),
            (true, false) => "Execution time increased. Consider profiling hot paths for optimization opportunities.".to_string(),
            (false, true) => "Memory usage increased. Review memory allocation patterns and consider optimization.".to_string(),
            (false, false) => {
                if timing_change < -5.0 || memory_change < -10.0 {
                    "Performance improved! Consider documenting the optimizations made.".to_string()
                } else {
                    "Performance is stable with minimal changes.".to_string()
                }
            }
        };

        Self {
            timing_change,
            memory_change,
            recommendation,
        }
    }
}

/// Performance profiler with export capabilities
#[derive(Debug)]
pub struct ExportableProfiler {
    /// Base profiler
    profiler: Profiler,
    /// Additional metadata
    metadata: HashMap<String, String>,
}

impl ExportableProfiler {
    /// Create a new exportable profiler
    pub fn new() -> Self {
        Self {
            profiler: Profiler::new(),
            metadata: HashMap::new(),
        }
    }

    /// Add metadata
    pub fn add_metadata(&mut self, key: String, value: String) {
        self.metadata.insert(key, value);
    }

    /// Export profiling data to JSON
    pub fn export_to_json(&self, path: &str) -> Result<(), std::io::Error> {
        use std::fs::File;
        use std::io::BufWriter;

        let file = File::create(path)?;
        let mut writer = BufWriter::new(file);

        // In a real implementation, you would use serde to serialize the data
        // For now, we'll create a simple JSON structure manually
        let json_data = format!(
            r#"{{
                "metadata": {:#?},
                "timings": {:#?},
                "memory": {:#?}
            }}"#,
            self.metadata,
            self.profiler.timings(),
            self.profiler.memory()
        );

        std::io::Write::write_all(&mut writer, json_data.as_bytes())?;
        Ok(())
    }

    /// Export profiling data to CSV
    pub fn export_to_csv(&self, path: &str) -> Result<(), std::io::Error> {
        let file = File::create(path)?;
        let mut writer = BufWriter::new(file);

        // Write timing data
        writeln!(writer, "Operation,Calls,Total_ms,Average_ms,Max_ms")?;
        for (operation, timing) in self.profiler.timings() {
            writeln!(
                writer,
                "{},{},{:.2},{:.2},{:.2}",
                operation,
                timing.calls(),
                timing.total_duration().as_secs_f64() * 1000.0,
                timing.average_duration().as_secs_f64() * 1000.0,
                timing.max_duration().as_secs_f64() * 1000.0
            )?;
        }

        writer.flush()?;
        Ok(())
    }

    /// Get access to the underlying profiler
    pub fn profiler(&mut self) -> &mut Profiler {
        &mut self.profiler
    }
}

impl Default for ExportableProfiler {
    fn default() -> Self {
        Self::new()
    }
}
