//! Backpressure monitoring for streaming channels
//!
//! Provides real-time monitoring of channel buffer utilization with configurable
//! thresholds for warning and critical states.

use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use tracing::{error, warn};

/// Backpressure monitoring status
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackpressureStatus {
    /// Buffer utilization is healthy (< warning threshold)
    Healthy { utilization_pct: u8 },

    /// Buffer utilization is approaching limits (>= warning threshold)
    Warning { utilization_pct: u8 },

    /// Buffer utilization is critical (>= critical threshold)
    Critical { utilization_pct: u8 },

    /// Buffer is completely full
    Full,
}

impl BackpressureStatus {
    /// Returns true if the status indicates a warning or worse condition
    pub fn is_warning(&self) -> bool {
        matches!(
            self,
            Self::Warning { .. } | Self::Critical { .. } | Self::Full
        )
    }

    /// Returns true if the status indicates a critical condition
    pub fn is_critical(&self) -> bool {
        matches!(self, Self::Critical { .. } | Self::Full)
    }

    /// Returns the utilization percentage (0-100)
    pub fn utilization_pct(&self) -> u8 {
        match *self {
            Self::Healthy { utilization_pct }
            | Self::Warning { utilization_pct }
            | Self::Critical { utilization_pct } => utilization_pct,
            Self::Full => 100,
        }
    }
}

/// Configuration for backpressure monitoring
#[derive(Debug, Clone)]
pub struct BackpressureConfig {
    /// Buffer capacity for utilization calculation
    pub buffer_capacity: usize,

    /// Warning threshold (0.0-1.0). Default: 0.8 (80%)
    pub warning_threshold: f32,

    /// Critical threshold (0.0-1.0). Default: 0.95 (95%)
    pub critical_threshold: f32,

    /// Metric label prefix for Prometheus
    pub metric_prefix: String,
}

impl Default for BackpressureConfig {
    fn default() -> Self {
        Self {
            buffer_capacity: 1000,
            warning_threshold: 0.8,
            critical_threshold: 0.95,
            metric_prefix: "stream".to_string(),
        }
    }
}

/// Backpressure monitor for streaming channels
///
/// Tracks buffer utilization and provides real-time status with configurable thresholds.
///
/// Designed for minimal overhead (<100ns per check) to maintain HFT performance targets.
#[derive(Debug)]
pub struct BackpressureMonitor {
    config: BackpressureConfig,

    // Atomic counters for lock-free operation
    messages_sent: Arc<AtomicU64>,
    messages_dropped: Arc<AtomicU64>,
    warnings_triggered: Arc<AtomicU64>,
    critical_triggered: Arc<AtomicU64>,
}

impl BackpressureMonitor {
    /// Create a new backpressure monitor with the given configuration
    pub fn new(config: BackpressureConfig) -> Self {
        Self {
            config,
            messages_sent: Arc::new(AtomicU64::new(0)),
            messages_dropped: Arc::new(AtomicU64::new(0)),
            warnings_triggered: Arc::new(AtomicU64::new(0)),
            critical_triggered: Arc::new(AtomicU64::new(0)),
        }
    }

    /// Create a backpressure monitor with default configuration
    pub fn with_capacity(buffer_capacity: usize, metric_prefix: impl Into<String>) -> Self {
        let config = BackpressureConfig {
            buffer_capacity,
            metric_prefix: metric_prefix.into(),
            ..Default::default()
        };
        Self::new(config)
    }

    /// Check current buffer utilization and return status
    ///
    /// # Arguments
    /// * `current_size` - Current number of items in the buffer
    ///
    /// # Performance
    ///
    /// This method is designed to be called frequently (per-message) with minimal overhead.
    ///
    /// All operations are lock-free and use atomic operations only.
    #[inline]
    pub fn check(&self, current_size: usize) -> BackpressureStatus {
        // Fast path: buffer is full
        if current_size >= self.config.buffer_capacity {
            return BackpressureStatus::Full;
        }

        // Calculate utilization percentage
        let utilization = current_size as f32 / self.config.buffer_capacity as f32;
        let utilization_pct = (utilization * 100.0) as u8;

        // Determine status based on thresholds
        if utilization >= self.config.critical_threshold {
            self.critical_triggered.fetch_add(1, Ordering::Relaxed);
            BackpressureStatus::Critical { utilization_pct }
        } else if utilization >= self.config.warning_threshold {
            self.warnings_triggered.fetch_add(1, Ordering::Relaxed);
            BackpressureStatus::Warning { utilization_pct }
        } else {
            BackpressureStatus::Healthy { utilization_pct }
        }
    }

    /// Record a successful message send
    #[inline]
    pub fn record_send(&self) {
        self.messages_sent.fetch_add(1, Ordering::Relaxed);
    }

    /// Record a dropped message (send timeout or buffer full)
    #[inline]
    pub fn record_drop(&self) {
        self.messages_dropped.fetch_add(1, Ordering::Relaxed);
    }

    /// Get total messages sent (for metrics)
    pub fn messages_sent(&self) -> u64 {
        self.messages_sent.load(Ordering::Relaxed)
    }

    /// Get total messages dropped (for metrics)
    pub fn messages_dropped(&self) -> u64 {
        self.messages_dropped.load(Ordering::Relaxed)
    }

    /// Get total warning events (for metrics)
    pub fn warnings_triggered(&self) -> u64 {
        self.warnings_triggered.load(Ordering::Relaxed)
    }

    /// Get total critical events (for metrics)
    pub fn critical_triggered(&self) -> u64 {
        self.critical_triggered.load(Ordering::Relaxed)
    }

    /// Get the metric prefix for this monitor
    pub fn metric_prefix(&self) -> &str {
        &self.config.metric_prefix
    }

    /// Log backpressure status if warning or critical
    pub fn log_status(&self, status: &BackpressureStatus, stream_name: &str) {
        match status {
            BackpressureStatus::Warning { utilization_pct } => {
                warn!(
                    stream = stream_name,
                    utilization_pct = utilization_pct,
                    threshold = (self.config.warning_threshold * 100.0) as u8,
                    "Stream backpressure warning"
                );
            },
            BackpressureStatus::Critical { utilization_pct } => {
                error!(
                    stream = stream_name,
                    utilization_pct = utilization_pct,
                    threshold = (self.config.critical_threshold * 100.0) as u8,
                    "Stream backpressure critical"
                );
            },
            BackpressureStatus::Full => {
                error!(
                    stream = stream_name,
                    "Stream buffer full - messages will be dropped"
                );
            },
            _ => {},
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_backpressure_status_healthy() {
        let config = BackpressureConfig {
            buffer_capacity: 1000,
            warning_threshold: 0.8,
            critical_threshold: 0.95,
            metric_prefix: "test".to_string(),
        };
        let monitor = BackpressureMonitor::new(config);

        // 50% utilization should be healthy
        let status = monitor.check(500);
        assert!(matches!(status, BackpressureStatus::Healthy { .. }));
        assert_eq!(status.utilization_pct(), 50);
        assert!(!status.is_warning());
    }

    #[test]
    fn test_backpressure_status_warning() {
        let config = BackpressureConfig {
            buffer_capacity: 1000,
            warning_threshold: 0.8,
            critical_threshold: 0.95,
            metric_prefix: "test".to_string(),
        };
        let monitor = BackpressureMonitor::new(config);

        // 85% utilization should trigger warning
        let status = monitor.check(850);
        assert!(matches!(status, BackpressureStatus::Warning { .. }));
        assert_eq!(status.utilization_pct(), 85);
        assert!(status.is_warning());
        assert!(!status.is_critical());
    }

    #[test]
    fn test_backpressure_status_critical() {
        let config = BackpressureConfig {
            buffer_capacity: 1000,
            warning_threshold: 0.8,
            critical_threshold: 0.95,
            metric_prefix: "test".to_string(),
        };
        let monitor = BackpressureMonitor::new(config);

        // 98% utilization should trigger critical
        let status = monitor.check(980);
        assert!(matches!(status, BackpressureStatus::Critical { .. }));
        assert_eq!(status.utilization_pct(), 98);
        assert!(status.is_warning());
        assert!(status.is_critical());
    }

    #[test]
    fn test_backpressure_status_full() {
        let config = BackpressureConfig {
            buffer_capacity: 1000,
            warning_threshold: 0.8,
            critical_threshold: 0.95,
            metric_prefix: "test".to_string(),
        };
        let monitor = BackpressureMonitor::new(config);

        // 100% utilization should be full
        let status = monitor.check(1000);
        assert_eq!(status, BackpressureStatus::Full);
        assert_eq!(status.utilization_pct(), 100);
        assert!(status.is_critical());
    }

    #[test]
    fn test_message_counting() {
        let monitor = BackpressureMonitor::with_capacity(1000, "test");

        assert_eq!(monitor.messages_sent(), 0);
        assert_eq!(monitor.messages_dropped(), 0);

        monitor.record_send();
        monitor.record_send();
        monitor.record_drop();

        assert_eq!(monitor.messages_sent(), 2);
        assert_eq!(monitor.messages_dropped(), 1);
    }
}
