//! Node statistics tracking
//!
//! Provides detailed statistics for pipeline nodes to understand
//! data flow, backpressure behavior, and timing.

use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Instant;

/// Statistics pour un node audio
#[derive(Debug)]
pub struct NodeStats {
    /// Nom du node pour identification
    pub name: String,

    /// Instant de démarrage du node
    pub start_time: Instant,

    /// Nombre total de segments reçus
    pub segments_received: AtomicUsize,

    /// Nombre total de segments envoyés
    pub segments_sent: AtomicUsize,

    /// Nombre total de bytes traités
    pub bytes_processed: AtomicU64,

    /// Nombre de fois où l'envoi a été bloqué (backpressure)
    pub backpressure_blocks: AtomicUsize,

    /// Temps total passé bloqué en millisecondes
    pub backpressure_time_ms: AtomicU64,

    /// Timestamp du premier segment (secondes)
    pub first_segment_timestamp: AtomicU64, // Stocké comme u64 * 1000 pour précision

    /// Timestamp du dernier segment (secondes)
    pub last_segment_timestamp: AtomicU64, // Stocké comme u64 * 1000 pour précision
}

impl NodeStats {
    pub fn new(name: impl Into<String>) -> Arc<Self> {
        Arc::new(Self {
            name: name.into(),
            start_time: Instant::now(),
            segments_received: AtomicUsize::new(0),
            segments_sent: AtomicUsize::new(0),
            bytes_processed: AtomicU64::new(0),
            backpressure_blocks: AtomicUsize::new(0),
            backpressure_time_ms: AtomicU64::new(0),
            first_segment_timestamp: AtomicU64::new(u64::MAX),
            last_segment_timestamp: AtomicU64::new(0),
        })
    }

    /// Enregistre la réception d'un segment
    pub fn record_segment_received(&self, timestamp_sec: f64) {
        self.segments_received.fetch_add(1, Ordering::Relaxed);

        let ts_millis = (timestamp_sec * 1000.0) as u64;

        // Update first timestamp (atomic min)
        let mut current = self.first_segment_timestamp.load(Ordering::Relaxed);
        while current > ts_millis {
            match self.first_segment_timestamp.compare_exchange_weak(
                current,
                ts_millis,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(x) => current = x,
            }
        }

        // Update last timestamp (atomic max)
        let mut current = self.last_segment_timestamp.load(Ordering::Relaxed);
        while current < ts_millis {
            match self.last_segment_timestamp.compare_exchange_weak(
                current,
                ts_millis,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(x) => current = x,
            }
        }
    }

    /// Enregistre l'envoi d'un segment
    pub fn record_segment_sent(&self, bytes: usize) {
        self.segments_sent.fetch_add(1, Ordering::Relaxed);
        self.bytes_processed
            .fetch_add(bytes as u64, Ordering::Relaxed);
    }

    /// Enregistre un événement de backpressure
    pub fn record_backpressure(&self, duration_ms: u64) {
        self.backpressure_blocks.fetch_add(1, Ordering::Relaxed);
        self.backpressure_time_ms
            .fetch_add(duration_ms, Ordering::Relaxed);
    }

    /// Retourne un rapport formaté des statistiques
    pub fn report(&self) -> String {
        let elapsed = self.start_time.elapsed().as_secs_f64();
        let received = self.segments_received.load(Ordering::Relaxed);
        let sent = self.segments_sent.load(Ordering::Relaxed);
        let bytes = self.bytes_processed.load(Ordering::Relaxed);
        let bp_blocks = self.backpressure_blocks.load(Ordering::Relaxed);
        let bp_time_ms = self.backpressure_time_ms.load(Ordering::Relaxed);

        let first_ts = self.first_segment_timestamp.load(Ordering::Relaxed);
        let last_ts = self.last_segment_timestamp.load(Ordering::Relaxed);

        let first_ts_sec = if first_ts == u64::MAX {
            0.0
        } else {
            first_ts as f64 / 1000.0
        };
        let last_ts_sec = last_ts as f64 / 1000.0;
        let audio_duration = last_ts_sec - first_ts_sec;

        let mb = bytes as f64 / 1_048_576.0;
        let throughput_mbps = if elapsed > 0.0 { mb / elapsed } else { 0.0 };

        format!(
            "[{}]\n\
             Elapsed: {:.1}s | Received: {} | Sent: {} | Lost: {}\n\
             Data: {:.1} MB | Throughput: {:.2} MB/s\n\
             Audio: {:.1}s (first: {:.1}s, last: {:.1}s) | Real-time ratio: {:.1}%\n\
             Backpressure: {} blocks, {:.2}s total ({:.1}% of time)",
            self.name,
            elapsed,
            received,
            sent,
            received.saturating_sub(sent),
            mb,
            throughput_mbps,
            audio_duration,
            first_ts_sec,
            last_ts_sec,
            if audio_duration > 0.0 {
                (elapsed / audio_duration) * 100.0
            } else {
                0.0
            },
            bp_blocks,
            bp_time_ms as f64 / 1000.0,
            if elapsed > 0.0 {
                (bp_time_ms as f64 / 1000.0 / elapsed) * 100.0
            } else {
                0.0
            }
        )
    }
}
