//! Performance metrics and monitoring

use dashmap::DashMap;
use std::sync::Mutex;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::{Duration, Instant};

/// Server metrics collector
pub struct Metrics {
    // Request metrics
    total_requests: AtomicU64,
    successful_requests: AtomicU64,
    failed_requests: AtomicU64,

    // Timing metrics
    total_response_time: AtomicU64, // in microseconds
    min_response_time: AtomicU64,
    max_response_time: AtomicU64,

    // Connection metrics
    active_connections: AtomicUsize,
    total_connections: AtomicU64,

    // Worker metrics
    worker_restarts: AtomicU64,

    // Response status codes
    status_2xx: AtomicU64,
    status_3xx: AtomicU64,
    status_4xx: AtomicU64,
    status_5xx: AtomicU64,

    // Start time
    start_time: Instant,

    // Per-worker metrics (tracked in logger process)
    worker_stats: DashMap<u32, WorkerStats>,

    // Latency histogram (for percentiles)
    latency_buckets: Option<Mutex<Vec<u64>>>,
}

impl Metrics {
    /// Create a new metrics collector
    pub fn new(percentiles_enabled: bool) -> Self {
        let latency_buckets = if percentiles_enabled {
            Some(Mutex::new(vec![0u64; LATENCY_BUCKETS.len() + 1]))
        } else {
            None
        };
        Self {
            total_requests: AtomicU64::new(0),
            successful_requests: AtomicU64::new(0),
            failed_requests: AtomicU64::new(0),
            total_response_time: AtomicU64::new(0),
            min_response_time: AtomicU64::new(u64::MAX),
            max_response_time: AtomicU64::new(0),
            active_connections: AtomicUsize::new(0),
            total_connections: AtomicU64::new(0),
            worker_restarts: AtomicU64::new(0),
            status_2xx: AtomicU64::new(0),
            status_3xx: AtomicU64::new(0),
            status_4xx: AtomicU64::new(0),
            status_5xx: AtomicU64::new(0),
            start_time: Instant::now(),
            worker_stats: DashMap::new(),
            latency_buckets,
        }
    }

    /// Record a request
    pub fn record_request(
        &self,
        status: u16,
        duration: Duration,
        worker_id: Option<u32>,
        pid: Option<i32>,
    ) {
        self.total_requests.fetch_add(1, Ordering::Relaxed);

        if status >= 200 && status < 300 {
            self.successful_requests.fetch_add(1, Ordering::Relaxed);
            self.status_2xx.fetch_add(1, Ordering::Relaxed);
        } else if status >= 300 && status < 400 {
            self.status_3xx.fetch_add(1, Ordering::Relaxed);
        } else if status >= 400 && status < 500 {
            self.failed_requests.fetch_add(1, Ordering::Relaxed);
            self.status_4xx.fetch_add(1, Ordering::Relaxed);
        } else if status >= 500 {
            self.failed_requests.fetch_add(1, Ordering::Relaxed);
            self.status_5xx.fetch_add(1, Ordering::Relaxed);
        }

        // Record timing
        let micros = duration.as_micros() as u64;
        self.total_response_time
            .fetch_add(micros, Ordering::Relaxed);

        // Update min
        let mut current_min = self.min_response_time.load(Ordering::Relaxed);
        while micros < current_min {
            match self.min_response_time.compare_exchange(
                current_min,
                micros,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(x) => current_min = x,
            }
        }

        // Update max
        let mut current_max = self.max_response_time.load(Ordering::Relaxed);
        while micros > current_max {
            match self.max_response_time.compare_exchange(
                current_max,
                micros,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(x) => current_max = x,
            }
        }

        if let Some(buckets) = &self.latency_buckets {
            if let Ok(mut guard) = buckets.lock() {
                let idx = latency_bucket_index(micros);
                guard[idx] = guard[idx].saturating_add(1);
            }
        }

        if let Some(worker_id) = worker_id {
            self.record_worker_request(worker_id, pid, status);
        }
    }

    /// Increment active connections
    pub fn increment_connections(&self) {
        self.active_connections.fetch_add(1, Ordering::Relaxed);
        self.total_connections.fetch_add(1, Ordering::Relaxed);
    }

    /// Decrement active connections
    pub fn decrement_connections(&self) {
        self.active_connections.fetch_sub(1, Ordering::Relaxed);
    }

    /// Record worker restart
    pub fn record_worker_restart(&self) {
        self.worker_restarts.fetch_add(1, Ordering::Relaxed);
    }

    /// Get current metrics snapshot
    pub fn snapshot(&self) -> MetricsSnapshot {
        let total_requests = self.total_requests.load(Ordering::Relaxed);
        let total_time_micros = self.total_response_time.load(Ordering::Relaxed);

        let avg_response_time = if total_requests > 0 {
            Duration::from_micros(total_time_micros / total_requests)
        } else {
            Duration::from_micros(0)
        };

        MetricsSnapshot {
            uptime: self.start_time.elapsed(),
            total_requests,
            successful_requests: self.successful_requests.load(Ordering::Relaxed),
            failed_requests: self.failed_requests.load(Ordering::Relaxed),
            avg_response_time,
            min_response_time: Duration::from_micros(
                self.min_response_time.load(Ordering::Relaxed),
            ),
            max_response_time: Duration::from_micros(
                self.max_response_time.load(Ordering::Relaxed),
            ),
            p50_response_time_micros: self.percentile(0.50),
            p95_response_time_micros: self.percentile(0.95),
            p99_response_time_micros: self.percentile(0.99),
            p999_response_time_micros: self.percentile(0.999),
            active_connections: self.active_connections.load(Ordering::Relaxed),
            total_connections: self.total_connections.load(Ordering::Relaxed),
            worker_restarts: self.worker_restarts.load(Ordering::Relaxed),
            status_2xx: self.status_2xx.load(Ordering::Relaxed),
            status_3xx: self.status_3xx.load(Ordering::Relaxed),
            status_4xx: self.status_4xx.load(Ordering::Relaxed),
            status_5xx: self.status_5xx.load(Ordering::Relaxed),
            workers: self.snapshot_workers(),
        }
    }

    /// Reset all metrics
    pub fn reset(&self) {
        self.total_requests.store(0, Ordering::Relaxed);
        self.successful_requests.store(0, Ordering::Relaxed);
        self.failed_requests.store(0, Ordering::Relaxed);
        self.total_response_time.store(0, Ordering::Relaxed);
        self.min_response_time.store(u64::MAX, Ordering::Relaxed);
        self.max_response_time.store(0, Ordering::Relaxed);
        self.worker_restarts.store(0, Ordering::Relaxed);
        self.status_2xx.store(0, Ordering::Relaxed);
        self.status_3xx.store(0, Ordering::Relaxed);
        self.status_4xx.store(0, Ordering::Relaxed);
        self.status_5xx.store(0, Ordering::Relaxed);
        if let Some(buckets) = &self.latency_buckets {
            if let Ok(mut guard) = buckets.lock() {
                for bucket in guard.iter_mut() {
                    *bucket = 0;
                }
            }
        }
        self.worker_stats.clear();
    }

    pub fn record_worker_stats(
        &self,
        worker_id: u32,
        pid: u32,
        active_connections: usize,
        total_requests: u64,
    ) {
        let now = Instant::now();
        let mut entry = self
            .worker_stats
            .entry(worker_id)
            .or_insert_with(|| WorkerStats {
                pid: Some(pid as i32),
                first_seen: now,
                last_seen: now,
                restarts: 0,
                total_requests: 0,
                successful_requests: 0,
                failed_requests: 0,
                status_4xx: 0,
                status_5xx: 0,
                active_connections: None,
            });

        if let Some(current) = entry.pid {
            if current != pid as i32 {
                entry.restarts = entry.restarts.saturating_add(1);
                entry.first_seen = now;
                entry.total_requests = 0;
                entry.successful_requests = 0;
                entry.failed_requests = 0;
                entry.status_4xx = 0;
                entry.status_5xx = 0;
            }
        }
        entry.pid = Some(pid as i32);
        entry.last_seen = now;
        entry.active_connections = Some(active_connections);
        if total_requests > entry.total_requests {
            entry.total_requests = total_requests;
        }
    }

    fn record_worker_request(&self, worker_id: u32, pid: Option<i32>, status: u16) {
        let now = Instant::now();
        let mut entry = self
            .worker_stats
            .entry(worker_id)
            .or_insert_with(|| WorkerStats {
                pid,
                first_seen: now,
                last_seen: now,
                restarts: 0,
                total_requests: 0,
                successful_requests: 0,
                failed_requests: 0,
                status_4xx: 0,
                status_5xx: 0,
                active_connections: None,
            });

        if let Some(pid) = pid {
            if let Some(current) = entry.pid {
                if current != pid {
                    entry.restarts = entry.restarts.saturating_add(1);
                    entry.first_seen = now;
                    entry.total_requests = 0;
                    entry.successful_requests = 0;
                    entry.failed_requests = 0;
                    entry.status_4xx = 0;
                    entry.status_5xx = 0;
                }
            }
            entry.pid = Some(pid);
        }

        entry.last_seen = now;
        entry.total_requests = entry.total_requests.saturating_add(1);
        if status >= 200 && status < 300 {
            entry.successful_requests = entry.successful_requests.saturating_add(1);
        } else if status >= 400 && status < 500 {
            entry.failed_requests = entry.failed_requests.saturating_add(1);
            entry.status_4xx = entry.status_4xx.saturating_add(1);
        } else if status >= 500 {
            entry.failed_requests = entry.failed_requests.saturating_add(1);
            entry.status_5xx = entry.status_5xx.saturating_add(1);
        }
    }

    fn snapshot_workers(&self) -> Vec<WorkerMetricsSnapshot> {
        let now = Instant::now();
        self.worker_stats
            .iter()
            .map(|entry| {
                let stats = entry.value();
                let uptime = now.saturating_duration_since(stats.first_seen);
                let uptime_secs = uptime.as_secs();
                let total_requests = stats.total_requests;
                let success_rate = if total_requests == 0 {
                    0.0
                } else {
                    stats.successful_requests as f64 / total_requests as f64
                };
                let error_rate = if total_requests == 0 {
                    0.0
                } else {
                    stats.failed_requests as f64 / total_requests as f64
                };
                let rps = if uptime_secs > 0 {
                    total_requests as f64 / uptime_secs as f64
                } else {
                    0.0
                };
                let active = now.duration_since(stats.last_seen) <= Duration::from_secs(15);
                WorkerMetricsSnapshot {
                    id: *entry.key(),
                    pid: stats.pid,
                    active,
                    restarts: stats.restarts,
                    uptime,
                    total_requests,
                    success_rate,
                    error_rate,
                    rps,
                    status_4xx: stats.status_4xx,
                    status_5xx: stats.status_5xx,
                    active_connections: stats.active_connections,
                }
            })
            .collect()
    }

    fn percentile(&self, quantile: f64) -> Option<u64> {
        let buckets = self.latency_buckets.as_ref()?;
        let guard = buckets.lock().ok()?;
        let total: u64 = guard.iter().sum();
        if total == 0 {
            return None;
        }
        let target = (total as f64 * quantile).ceil() as u64;
        let mut cumulative = 0u64;
        for (idx, count) in guard.iter().enumerate() {
            cumulative = cumulative.saturating_add(*count);
            if cumulative >= target {
                return Some(latency_bucket_upper_bound(idx));
            }
        }
        Some(*LATENCY_BUCKETS.last().unwrap_or(&0))
    }
}

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

/// Snapshot of metrics at a point in time
#[derive(Debug, Clone)]
pub struct MetricsSnapshot {
    pub uptime: Duration,
    pub total_requests: u64,
    pub successful_requests: u64,
    pub failed_requests: u64,
    pub avg_response_time: Duration,
    pub min_response_time: Duration,
    pub max_response_time: Duration,
    pub p50_response_time_micros: Option<u64>,
    pub p95_response_time_micros: Option<u64>,
    pub p99_response_time_micros: Option<u64>,
    pub p999_response_time_micros: Option<u64>,
    pub active_connections: usize,
    pub total_connections: u64,
    pub worker_restarts: u64,
    pub status_2xx: u64,
    pub status_3xx: u64,
    pub status_4xx: u64,
    pub status_5xx: u64,
    pub workers: Vec<WorkerMetricsSnapshot>,
}

impl MetricsSnapshot {
    /// Format metrics as human-readable string
    pub fn format(&self) -> String {
        format!(
            "Uptime: {:?}\n\
            Total Requests: {}\n\
            Successful: {} ({:.2}%)\n\
            Failed: {} ({:.2}%)\n\
            Avg Response Time: {:?}\n\
            Min Response Time: {:?}\n\
            Max Response Time: {:?}\n\
            Active Connections: {}\n\
            Total Connections: {}\n\
            Worker Restarts: {}\n\
            Status Codes:\n\
                2xx: {}\n\
                3xx: {}\n\
                4xx: {}\n\
                5xx: {}",
            self.uptime,
            self.total_requests,
            self.successful_requests,
            self.success_rate() * 100.0,
            self.failed_requests,
            self.failure_rate() * 100.0,
            self.avg_response_time,
            self.min_response_time,
            self.max_response_time,
            self.active_connections,
            self.total_connections,
            self.worker_restarts,
            self.status_2xx,
            self.status_3xx,
            self.status_4xx,
            self.status_5xx,
        )
    }

    /// Calculate success rate
    pub fn success_rate(&self) -> f64 {
        if self.total_requests == 0 {
            0.0
        } else {
            self.successful_requests as f64 / self.total_requests as f64
        }
    }

    /// Calculate failure rate
    pub fn failure_rate(&self) -> f64 {
        if self.total_requests == 0 {
            0.0
        } else {
            self.failed_requests as f64 / self.total_requests as f64
        }
    }

    /// Calculate requests per second
    pub fn requests_per_second(&self) -> f64 {
        let secs = self.uptime.as_secs_f64();
        if secs > 0.0 {
            self.total_requests as f64 / secs
        } else {
            0.0
        }
    }
}

#[derive(Debug, Clone)]
pub struct WorkerMetricsSnapshot {
    pub id: u32,
    pub pid: Option<i32>,
    pub active: bool,
    pub restarts: u32,
    pub uptime: Duration,
    pub total_requests: u64,
    pub success_rate: f64,
    pub error_rate: f64,
    pub rps: f64,
    pub status_4xx: u64,
    pub status_5xx: u64,
    pub active_connections: Option<usize>,
}

#[derive(Debug, Clone)]
struct WorkerStats {
    pid: Option<i32>,
    first_seen: Instant,
    last_seen: Instant,
    restarts: u32,
    total_requests: u64,
    successful_requests: u64,
    failed_requests: u64,
    status_4xx: u64,
    status_5xx: u64,
    active_connections: Option<usize>,
}

const LATENCY_BUCKETS: [u64; 20] = [
    50,
    100,
    200,
    500,
    1_000,
    2_000,
    5_000,
    10_000,
    20_000,
    50_000,
    100_000,
    200_000,
    500_000,
    1_000_000,
    2_000_000,
    5_000_000,
    10_000_000,
    20_000_000,
    50_000_000,
    100_000_000,
];

fn latency_bucket_index(micros: u64) -> usize {
    for (idx, bound) in LATENCY_BUCKETS.iter().enumerate() {
        if micros <= *bound {
            return idx;
        }
    }
    LATENCY_BUCKETS.len()
}

fn latency_bucket_upper_bound(idx: usize) -> u64 {
    if idx >= LATENCY_BUCKETS.len() {
        *LATENCY_BUCKETS.last().unwrap_or(&0)
    } else {
        LATENCY_BUCKETS[idx]
    }
}
