//! F3 Debug Overlay
//!
//! Displays performance metrics and system info similar to Minecraft's F3 screen.
//! Press F3 to toggle visibility.

use eframe::egui;
use std::collections::VecDeque;
use std::time::{Duration, Instant};

const FRAME_HISTORY_SIZE: usize = 60; // Track last 60 frames for FPS stats
const DEVICE_REFRESH_INTERVAL: Duration = Duration::from_secs(2); // Refresh devices every 2 seconds
const MEMORY_REFRESH_INTERVAL: Duration = Duration::from_millis(500); // Refresh memory every 500ms

/// Statistics displayed in debug overlay
#[derive(Debug, Clone)]
pub struct DebugStats {
    pub fps_current: f32,      // Current frame FPS (unsmoothed)
    pub fps_min: f32,          // Minimum FPS over last 60 frames
    pub fps_avg: f32,          // Average FPS over last 60 frames
    pub fps_max: f32,          // Maximum FPS over last 60 frames
    pub frame_time_ms: f32,    // Current frame time in milliseconds
    pub memory_physical_mb: usize,
    pub memory_virtual_mb: usize,
    pub gpu_name: String,
    pub gpu_backend: String,
    pub gpu_driver: String,
    pub midi_devices: Vec<String>,
    pub audio_input_devices: Vec<String>,
    pub has_pointer: bool,

    // Performance metrics for each section
    pub timing_memory_us: u64,
    pub timing_gpu_us: u64,
    pub timing_midi_us: u64,
    pub timing_audio_us: u64,
    pub timing_pointer_us: u64,
    pub timing_total_us: u64,
}

/// Collects and aggregates debug statistics
pub struct DebugStatsCollector {
    frame_times: VecDeque<Duration>,
    last_frame_time: Option<Instant>,
    cached_audio_devices: Vec<String>,
    last_device_refresh: Option<Instant>,
    cached_memory_physical_mb: usize,
    cached_memory_virtual_mb: usize,
    last_memory_refresh: Option<Instant>,
}

impl DebugStatsCollector {
    pub fn new() -> Self {
        Self {
            frame_times: VecDeque::with_capacity(FRAME_HISTORY_SIZE),
            last_frame_time: None,
            cached_audio_devices: Vec::new(),
            last_device_refresh: None,
            cached_memory_physical_mb: 0,
            cached_memory_virtual_mb: 0,
            last_memory_refresh: None,
        }
    }

    /// Collect current debug statistics
    pub fn collect(
        &mut self,
        ctx: &egui::Context,
        gpu_info: &Option<wgpu::AdapterInfo>,
        audio_controller: Option<&std::sync::Arc<std::sync::Mutex<daw_backend::EngineController>>>,
    ) -> DebugStats {
        let collection_start = Instant::now();

        // Calculate actual frame time based on real elapsed time
        let now = Instant::now();
        let frame_duration = if let Some(last_time) = self.last_frame_time {
            now.duration_since(last_time)
        } else {
            Duration::from_secs_f32(1.0 / 60.0) // Default to 60 FPS for first frame
        };
        self.last_frame_time = Some(now);

        // Store frame duration in history
        self.frame_times.push_back(frame_duration);
        if self.frame_times.len() > FRAME_HISTORY_SIZE {
            self.frame_times.pop_front();
        }

        // Calculate FPS stats from actual frame durations
        let frame_time_ms = frame_duration.as_secs_f32() * 1000.0;
        let fps_current = 1.0 / frame_duration.as_secs_f32();

        let (fps_min, fps_avg, fps_max) = if !self.frame_times.is_empty() {
            let fps_values: Vec<f32> = self.frame_times
                .iter()
                .map(|dt| 1.0 / dt.as_secs_f32())
                .collect();
            let min = fps_values.iter().copied().fold(f32::INFINITY, f32::min);
            let max = fps_values.iter().copied().fold(f32::NEG_INFINITY, f32::max);
            let sum: f32 = fps_values.iter().sum();
            let avg = sum / fps_values.len() as f32;
            (min, avg, max)
        } else {
            (fps_current, fps_current, fps_current)
        };

        // Collect memory stats with timing - cache and refresh every 500ms
        let t0 = Instant::now();
        let should_refresh_memory = self.last_memory_refresh
            .map(|last| now.duration_since(last) >= MEMORY_REFRESH_INTERVAL)
            .unwrap_or(true);

        if should_refresh_memory {
            if let Some(usage) = memory_stats::memory_stats() {
                self.cached_memory_physical_mb = usage.physical_mem / 1024 / 1024;
                self.cached_memory_virtual_mb = usage.virtual_mem / 1024 / 1024;
            }
            self.last_memory_refresh = Some(now);
        }

        let memory_physical_mb = self.cached_memory_physical_mb;
        let memory_virtual_mb = self.cached_memory_virtual_mb;
        let timing_memory_us = t0.elapsed().as_micros() as u64;

        // Extract GPU info with timing
        let t1 = Instant::now();
        let (gpu_name, gpu_backend, gpu_driver) = if let Some(info) = gpu_info {
            (
                info.name.clone(),
                format!("{:?}", info.backend),
                format!("{} ({})", info.driver, info.driver_info),
            )
        } else {
            ("Unknown".to_string(), "Unknown".to_string(), "Unknown".to_string())
        };
        let timing_gpu_us = t1.elapsed().as_micros() as u64;

        // Collect MIDI devices with timing
        let t2 = Instant::now();
        let midi_devices = if let Some(_controller) = audio_controller {
            // TODO: Add method to audio controller to get MIDI device names
            // For now, return empty vec
            vec![]
        } else {
            vec![]
        };
        let timing_midi_us = t2.elapsed().as_micros() as u64;

        // Refresh audio input devices only every 2 seconds to avoid performance issues
        let t3 = Instant::now();
        let should_refresh_devices = self.last_device_refresh
            .map(|last| now.duration_since(last) >= DEVICE_REFRESH_INTERVAL)
            .unwrap_or(true);

        if should_refresh_devices {
            self.cached_audio_devices = enumerate_audio_input_devices();
            self.last_device_refresh = Some(now);
        }

        let audio_input_devices = self.cached_audio_devices.clone();
        let timing_audio_us = t3.elapsed().as_micros() as u64;

        // Detect pointer usage with timing
        let t4 = Instant::now();
        let has_pointer = ctx.input(|i| {
            i.pointer.is_decidedly_dragging()
                || i.pointer.any_down()
                || i.pointer.any_pressed()
        });
        let timing_pointer_us = t4.elapsed().as_micros() as u64;

        let timing_total_us = collection_start.elapsed().as_micros() as u64;

        DebugStats {
            fps_current,
            fps_min,
            fps_avg,
            fps_max,
            frame_time_ms,
            memory_physical_mb,
            memory_virtual_mb,
            gpu_name,
            gpu_backend,
            gpu_driver,
            midi_devices,
            audio_input_devices,
            has_pointer,
            timing_memory_us,
            timing_gpu_us,
            timing_midi_us,
            timing_audio_us,
            timing_pointer_us,
            timing_total_us,
        }
    }
}

/// Enumerate audio input devices using cpal
fn enumerate_audio_input_devices() -> Vec<String> {
    use cpal::traits::{HostTrait, DeviceTrait};

    let host = cpal::default_host();
    host.input_devices()
        .ok()
        .map(|devices| {
            devices
                .filter_map(|d| d.name().ok())
                .collect()
        })
        .unwrap_or_default()
}

/// Render the debug overlay in-window using egui::Area
pub fn render_debug_overlay(ctx: &egui::Context, stats: &DebugStats) {
    egui::Area::new(egui::Id::new("debug_overlay_area"))
        .fixed_pos(egui::pos2(10.0, 10.0))
        .show(ctx, |ui| {
            egui::Frame::new()
                .fill(egui::Color32::from_black_alpha(200))
                .inner_margin(8.0)
                .show(ui, |ui| {
                    // Use monospace font for alignment
                    ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);

                    // Performance section
                    ui.colored_label(egui::Color32::YELLOW, "Performance:");
                    ui.label(format!(
                        "FPS: {:.1} (min: {:.1} / avg: {:.1} / max: {:.1})",
                        stats.fps_current, stats.fps_min, stats.fps_avg, stats.fps_max
                    ));
                    ui.label(format!("Frame time: {:.2} ms", stats.frame_time_ms));

                    ui.add_space(8.0);

                    // Memory section with timing
                    ui.colored_label(egui::Color32::YELLOW, format!("Memory: ({}µs)", stats.timing_memory_us));
                    ui.label(format!("Physical: {} MB", stats.memory_physical_mb));
                    ui.label(format!("Virtual: {} MB", stats.memory_virtual_mb));

                    ui.add_space(8.0);

                    // Graphics section with timing
                    ui.colored_label(egui::Color32::YELLOW, format!("Graphics: ({}µs)", stats.timing_gpu_us));
                    ui.label(format!("GPU: {}", stats.gpu_name));
                    ui.label(format!("Backend: {}", stats.gpu_backend));
                    ui.label(format!("Driver: {}", stats.gpu_driver));

                    ui.add_space(8.0);

                    // Input devices section with timing
                    ui.colored_label(egui::Color32::YELLOW, format!("Input Devices: ({}µs)",
                        stats.timing_midi_us + stats.timing_audio_us + stats.timing_pointer_us));

                    if stats.has_pointer {
                        ui.label(format!("• Mouse/Trackpad ({}µs)", stats.timing_pointer_us));
                    }

                    if !stats.audio_input_devices.is_empty() {
                        ui.label(format!("• {} Audio Input(s) ({}µs)",
                            stats.audio_input_devices.len(), stats.timing_audio_us));
                        for device in &stats.audio_input_devices {
                            ui.label(format!("  - {}", device));
                        }
                    }

                    if !stats.midi_devices.is_empty() {
                        ui.label(format!("• {} MIDI Device(s) ({}µs)",
                            stats.midi_devices.len(), stats.timing_midi_us));
                        for device in &stats.midi_devices {
                            ui.label(format!("  - {}", device));
                        }
                    }

                    ui.add_space(8.0);
                    ui.separator();
                    ui.colored_label(egui::Color32::CYAN, format!("Collection time: {}µs ({:.2}ms)",
                        stats.timing_total_us, stats.timing_total_us as f32 / 1000.0));
                    ui.colored_label(egui::Color32::GRAY, "Press F3 to close");
                });
        });
}
