//! # Atomic Gauge with IEEE 754 Optimization
//!
//! Ultra-fast atomic gauge using bit manipulation for f64 values.
//!
//! ## Features
//!
//! - **Atomic f64 operations** - Using IEEE 754 bit manipulation
//! - **Sub-5ns updates** - Direct bit-level atomic operations
//! - **Zero allocations** - Pure stack operations
//! - **Lock-free** - Never blocks, never waits
//! - **Cache optimized** - Aligned to prevent false sharing

use crate::{MetricsError, Result};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};

/// Ultra-fast atomic gauge for f64 values
///
/// Uses IEEE 754 bit manipulation for atomic operations on floating-point values.
/// Cache-line aligned to prevent false sharing.
#[repr(align(64))]
pub struct Gauge {
    /// Gauge value stored as IEEE 754 bits in atomic u64
    value: AtomicU64,
    /// Creation timestamp for statistics
    created_at: Instant,
}

/// Gauge statistics
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct GaugeStats {
    /// Current gauge value
    pub value: f64,
    /// Time since gauge creation
    pub age: Duration,
    /// Number of updates (not tracked by default for performance)
    pub updates: Option<u64>,
}

impl Gauge {
    /// Create new gauge starting at zero
    #[inline]
    pub fn new() -> Self {
        Self {
            value: AtomicU64::new(0.0_f64.to_bits()),
            created_at: Instant::now(),
        }
    }

    /// Try to add to current value with validation
    ///
    /// Returns `Err(MetricsError::InvalidValue)` if `delta` is not finite (NaN or ±inf)
    /// or if the resulting value would become non-finite. Returns
    /// `Err(MetricsError::Overflow)` if the sum overflows to a non-finite value.
    ///
    /// Example
    /// ```
    /// use metrics_lib::{Gauge, MetricsError};
    /// let g = Gauge::with_value(1.5);
    /// g.try_add(2.5).unwrap();
    /// assert_eq!(g.get(), 4.0);
    /// assert!(matches!(g.try_add(f64::INFINITY), Err(MetricsError::InvalidValue{..})));
    /// ```
    #[inline]
    pub fn try_add(&self, delta: f64) -> Result<()> {
        if delta == 0.0 {
            return Ok(());
        }
        if !delta.is_finite() {
            return Err(MetricsError::InvalidValue {
                reason: "delta is not finite",
            });
        }

        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);
            let new_value = current_value + delta;
            if !new_value.is_finite() {
                return Err(MetricsError::Overflow);
            }
            let new_bits = new_value.to_bits();

            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => return Ok(()),
                Err(_) => continue,
            }
        }
    }

    /// Try to subtract from current value (validated)
    ///
    /// Validation semantics are identical to [`Gauge::try_add`] but apply to
    /// subtraction (`-delta`).
    ///
    /// Example
    /// ```
    /// use metrics_lib::Gauge;
    /// let g = Gauge::with_value(10.0);
    /// g.try_sub(4.0).unwrap();
    /// assert_eq!(g.get(), 6.0);
    /// ```
    #[inline]
    pub fn try_sub(&self, delta: f64) -> Result<()> {
        self.try_add(-delta)
    }

    /// Create gauge with initial value
    #[inline]
    pub fn with_value(initial: f64) -> Self {
        Self {
            value: AtomicU64::new(initial.to_bits()),
            created_at: Instant::now(),
        }
    }

    /// Set gauge value - THE FASTEST PATH
    ///
    /// This is optimized for maximum speed:
    /// - Convert f64 to IEEE 754 bits
    /// - Single atomic store instruction
    /// - Relaxed memory ordering for speed
    /// - Inlined for zero function call overhead
    #[inline(always)]
    pub fn set(&self, value: f64) {
        self.value.store(value.to_bits(), Ordering::Relaxed);
    }

    /// Try to set gauge value with validation
    ///
    /// Returns `Err(MetricsError::InvalidValue)` if `value` is not finite (NaN or ±inf).
    ///
    /// Example
    /// ```
    /// use metrics_lib::{Gauge, MetricsError};
    /// let g = Gauge::new();
    /// assert!(g.try_set(42.0).is_ok());
    /// assert!(matches!(g.try_set(f64::NAN), Err(MetricsError::InvalidValue{..})));
    /// ```
    #[inline]
    pub fn try_set(&self, value: f64) -> Result<()> {
        if !value.is_finite() {
            return Err(MetricsError::InvalidValue {
                reason: "value is not finite",
            });
        }
        self.set(value);
        Ok(())
    }

    /// Get current value - single atomic load
    ///
    /// Note: `#[must_use]`. The returned value represents current gauge state;
    /// ignoring it may indicate a logic bug.
    #[must_use]
    #[inline(always)]
    pub fn get(&self) -> f64 {
        f64::from_bits(self.value.load(Ordering::Relaxed))
    }

    /// Add to current value - atomic compare-and-swap loop
    ///
    /// # Non-finite protection
    /// If `delta` is non-finite (NaN, ±∞), this is a **no-op**.
    /// If the resulting sum would also be non-finite the gauge retains its
    /// current value (silent no-op). Use [`Gauge::try_add`] to receive an
    /// explicit error instead.
    #[inline]
    pub fn add(&self, delta: f64) {
        if delta == 0.0 || !delta.is_finite() {
            return;
        }

        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);
            let new_value = current_value + delta;
            if !new_value.is_finite() {
                return; // Silently no-op — use try_add for error reporting
            }
            let new_bits = new_value.to_bits();

            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(_) => continue, // Retry with latest value
            }
        }
    }

    /// Subtract from current value
    ///
    /// Non-finite and overflow protection is identical to [`Gauge::add`].
    #[inline]
    pub fn sub(&self, delta: f64) {
        self.add(-delta);
    }

    /// Set to maximum of current value and new value
    #[inline]
    pub fn set_max(&self, value: f64) {
        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);

            if value <= current_value {
                break; // Current value is already larger
            }

            let new_bits = value.to_bits();
            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(_) => continue, // Retry
            }
        }
    }

    /// Try to set to maximum of current value and new value
    ///
    /// Returns `Err(MetricsError::InvalidValue)` if `value` is not finite.
    /// Otherwise sets the gauge to `max(current, value)` and returns `Ok(())`.
    ///
    /// Example
    /// ```
    /// use metrics_lib::{Gauge, MetricsError};
    /// let g = Gauge::with_value(5.0);
    /// g.try_set_max(10.0).unwrap();
    /// assert_eq!(g.get(), 10.0);
    /// assert!(matches!(g.try_set_max(f64::INFINITY), Err(MetricsError::InvalidValue{..})));
    /// ```
    #[inline]
    pub fn try_set_max(&self, value: f64) -> Result<()> {
        if !value.is_finite() {
            return Err(MetricsError::InvalidValue {
                reason: "value is not finite",
            });
        }
        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);
            if value <= current_value {
                return Ok(());
            }
            let new_bits = value.to_bits();
            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => return Ok(()),
                Err(_) => continue,
            }
        }
    }

    /// Set to minimum of current value and new value
    #[inline]
    pub fn set_min(&self, value: f64) {
        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);

            if value >= current_value {
                break; // Current value is already smaller
            }

            let new_bits = value.to_bits();
            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(_) => continue, // Retry
            }
        }
    }

    /// Try to set to minimum of current value and new value
    ///
    /// Returns `Err(MetricsError::InvalidValue)` if `value` is not finite.
    /// Otherwise sets the gauge to `min(current, value)` and returns `Ok(())`.
    ///
    /// Example
    /// ```
    /// use metrics_lib::{Gauge, MetricsError};
    /// let g = Gauge::with_value(10.0);
    /// g.try_set_min(7.0).unwrap();
    /// assert_eq!(g.get(), 7.0);
    /// assert!(matches!(g.try_set_min(f64::NAN), Err(MetricsError::InvalidValue{..})));
    /// ```
    #[inline]
    pub fn try_set_min(&self, value: f64) -> Result<()> {
        if !value.is_finite() {
            return Err(MetricsError::InvalidValue {
                reason: "value is not finite",
            });
        }
        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);
            if value >= current_value {
                return Ok(());
            }
            let new_bits = value.to_bits();
            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => return Ok(()),
                Err(_) => continue,
            }
        }
    }

    /// Atomic compare-and-swap
    ///
    /// Returns Ok(previous_value) if successful, Err(current_value) if failed
    #[inline]
    pub fn compare_and_swap(&self, expected: f64, new: f64) -> core::result::Result<f64, f64> {
        let expected_bits = expected.to_bits();
        let new_bits = new.to_bits();

        match self.value.compare_exchange(
            expected_bits,
            new_bits,
            Ordering::SeqCst,
            Ordering::SeqCst,
        ) {
            Ok(prev_bits) => Ok(f64::from_bits(prev_bits)),
            Err(current_bits) => Err(f64::from_bits(current_bits)),
        }
    }

    /// Reset to zero
    #[inline]
    pub fn reset(&self) {
        self.set(0.0);
    }

    /// Multiply current value by factor
    ///
    /// If `factor` is non-finite, or if the product would be non-finite,
    /// this is a **no-op** (the gauge retains its current value).
    #[inline]
    pub fn multiply(&self, factor: f64) {
        if factor == 1.0 || !factor.is_finite() {
            return;
        }

        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);
            let new_value = current_value * factor;
            if !new_value.is_finite() {
                return; // Guard against overflow to ±Inf or NaN propagation
            }
            let new_bits = new_value.to_bits();

            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(_) => continue,
            }
        }
    }

    /// Divide current value by divisor
    ///
    /// If `divisor` is zero, non-finite, or the result would be non-finite,
    /// this is a **no-op** (the gauge retains its current value). Non-finite
    /// product protection is handled transitively by [`Gauge::multiply`].
    #[inline]
    pub fn divide(&self, divisor: f64) {
        if divisor == 0.0 || divisor == 1.0 || !divisor.is_finite() {
            return;
        }
        self.multiply(1.0 / divisor);
    }

    /// Set to absolute value of current value
    #[inline]
    pub fn abs(&self) {
        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);

            if current_value >= 0.0 {
                break; // Already positive
            }

            let abs_value = current_value.abs();
            let abs_bits = abs_value.to_bits();

            match self.value.compare_exchange_weak(
                current_bits,
                abs_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(_) => continue,
            }
        }
    }

    /// Clamp value to range [min, max]
    #[inline]
    pub fn clamp(&self, min: f64, max: f64) {
        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);
            let clamped_value = current_value.clamp(min, max);

            if (current_value - clamped_value).abs() < f64::EPSILON {
                break; // Already in range
            }

            let clamped_bits = clamped_value.to_bits();

            match self.value.compare_exchange_weak(
                current_bits,
                clamped_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(_) => continue,
            }
        }
    }

    /// Exponential moving average update
    ///
    /// new_value = alpha * sample + (1 - alpha) * old_value
    #[inline]
    pub fn update_ema(&self, sample: f64, alpha: f64) {
        let alpha = alpha.clamp(0.0, 1.0);

        loop {
            let current_bits = self.value.load(Ordering::Relaxed);
            let current_value = f64::from_bits(current_bits);
            let new_value = alpha * sample + (1.0 - alpha) * current_value;
            let new_bits = new_value.to_bits();

            match self.value.compare_exchange_weak(
                current_bits,
                new_bits,
                Ordering::Relaxed,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(_) => continue,
            }
        }
    }

    /// Get comprehensive statistics
    ///
    /// Note: `#[must_use]`. Statistics summarize current state; dropping the
    /// result may indicate a logic bug.
    #[must_use]
    pub fn stats(&self) -> GaugeStats {
        GaugeStats {
            value: self.get(),
            age: self.created_at.elapsed(),
            updates: None, // Not tracked by default for performance
        }
    }

    /// Get age since creation
    ///
    /// Note: `#[must_use]`. Age is often used for derived metrics; don't call
    /// this for side effects.
    #[must_use]
    #[inline]
    pub fn age(&self) -> Duration {
        self.created_at.elapsed()
    }

    /// Check if gauge is zero
    ///
    /// Note: `#[must_use]`. The boolean result determines control flow.
    #[must_use]
    #[inline]
    pub fn is_zero(&self) -> bool {
        self.get() == 0.0
    }

    /// Check if value is positive
    ///
    /// Note: `#[must_use]`. The boolean result determines control flow.
    #[must_use]
    #[inline]
    pub fn is_positive(&self) -> bool {
        self.get() > 0.0
    }

    /// Check if value is negative
    ///
    /// Note: `#[must_use]`. The boolean result determines control flow.
    #[must_use]
    #[inline]
    pub fn is_negative(&self) -> bool {
        self.get() < 0.0
    }

    /// Check if value is finite (not NaN or infinity)
    ///
    /// Note: `#[must_use]`. The boolean result determines control flow.
    #[must_use]
    #[inline]
    pub fn is_finite(&self) -> bool {
        self.get().is_finite()
    }
}

impl Default for Gauge {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl std::fmt::Display for Gauge {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Gauge({})", self.get())
    }
}

impl std::fmt::Debug for Gauge {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Gauge")
            .field("value", &self.get())
            .field("age", &self.age())
            .field("is_finite", &self.is_finite())
            .finish()
    }
}

// Thread safety - Gauge is Send + Sync
// Gauge is composed of `AtomicU64` (Send + Sync) and `Instant` (Send + Sync).
// The compiler derives Send + Sync automatically; no explicit unsafe impl needed.

/// Specialized gauge types for common use cases
pub mod specialized {
    use super::*;

    /// Percentage gauge (0.0 to 100.0)
    #[repr(align(64))]
    pub struct PercentageGauge {
        inner: Gauge,
    }

    impl PercentageGauge {
        /// Create new percentage gauge
        #[inline]
        pub fn new() -> Self {
            Self {
                inner: Gauge::new(),
            }
        }

        /// Set percentage (automatically clamped to 0.0-100.0)
        #[inline(always)]
        pub fn set_percentage(&self, percentage: f64) {
            let clamped = percentage.clamp(0.0, 100.0);
            self.inner.set(clamped);
        }

        /// Get percentage
        #[inline(always)]
        pub fn get_percentage(&self) -> f64 {
            self.inner.get()
        }

        /// Set from ratio (0.0-1.0 becomes 0.0-100.0)
        #[inline(always)]
        pub fn set_ratio(&self, ratio: f64) {
            let percentage = (ratio * 100.0).clamp(0.0, 100.0);
            self.inner.set(percentage);
        }

        /// Get as ratio (0.0-1.0)
        #[inline(always)]
        pub fn get_ratio(&self) -> f64 {
            self.inner.get() / 100.0
        }

        /// Check if at maximum (100%)
        #[inline]
        pub fn is_full(&self) -> bool {
            (self.inner.get() - 100.0).abs() < f64::EPSILON
        }

        /// Check if at minimum (0%)
        #[inline]
        pub fn is_empty(&self) -> bool {
            self.inner.get() < f64::EPSILON
        }

        /// Add percentage (clamped to valid range)
        #[inline]
        pub fn add_percentage(&self, delta: f64) {
            loop {
                let current = self.get_percentage();
                let new_value = (current + delta).clamp(0.0, 100.0);

                if (current - new_value).abs() < f64::EPSILON {
                    break; // No change needed
                }

                match self.inner.compare_and_swap(current, new_value) {
                    Ok(_) => break,
                    Err(_) => continue, // Retry
                }
            }
        }

        /// Get gauge statistics
        pub fn stats(&self) -> GaugeStats {
            self.inner.stats()
        }
    }

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

    impl std::fmt::Display for PercentageGauge {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            write!(f, "PercentageGauge({}%)", self.get_percentage())
        }
    }

    /// CPU usage gauge (specialized percentage gauge)
    pub type CpuGauge = PercentageGauge;

    /// Memory gauge for byte values
    #[repr(align(64))]
    pub struct MemoryGauge {
        bytes: Gauge,
    }

    impl MemoryGauge {
        /// Create new memory gauge
        #[inline]
        pub fn new() -> Self {
            Self {
                bytes: Gauge::new(),
            }
        }

        /// Set memory usage in bytes
        #[inline(always)]
        pub fn set_bytes(&self, bytes: u64) {
            self.bytes.set(bytes as f64);
        }

        /// Get memory usage in bytes
        #[inline]
        pub fn get_bytes(&self) -> u64 {
            self.bytes.get() as u64
        }

        /// Get memory usage in KB
        #[inline]
        pub fn get_kb(&self) -> f64 {
            self.bytes.get() / 1024.0
        }

        /// Get memory usage in MB
        #[inline]
        pub fn get_mb(&self) -> f64 {
            self.bytes.get() / (1024.0 * 1024.0)
        }

        /// Get memory usage in GB
        #[inline]
        pub fn get_gb(&self) -> f64 {
            self.bytes.get() / (1024.0 * 1024.0 * 1024.0)
        }

        /// Add bytes
        #[inline]
        pub fn add_bytes(&self, bytes: i64) {
            self.bytes.add(bytes as f64);
        }

        /// Get gauge statistics
        pub fn stats(&self) -> GaugeStats {
            self.bytes.stats()
        }
    }

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

    impl std::fmt::Display for MemoryGauge {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            let mb = self.get_mb();
            if mb >= 1024.0 {
                write!(f, "MemoryGauge({:.2} GB)", self.get_gb())
            } else {
                write!(f, "MemoryGauge({mb:.2} MB)")
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::Arc;
    use std::thread;

    #[test]
    fn test_basic_operations() {
        let gauge = Gauge::new();

        assert_eq!(gauge.get(), 0.0);
        assert!(gauge.is_zero());
        assert!(gauge.is_finite());

        gauge.set(42.5);
        assert_eq!(gauge.get(), 42.5);
        assert!(!gauge.is_zero());
        assert!(gauge.is_positive());

        gauge.add(7.5);
        assert_eq!(gauge.get(), 50.0);

        gauge.sub(10.0);
        assert_eq!(gauge.get(), 40.0);

        gauge.reset();
        assert_eq!(gauge.get(), 0.0);
    }

    #[test]
    fn test_mathematical_operations() {
        let gauge = Gauge::with_value(10.0);

        gauge.multiply(2.0);
        assert_eq!(gauge.get(), 20.0);

        gauge.divide(4.0);
        assert_eq!(gauge.get(), 5.0);

        gauge.set(-15.0);
        assert!(gauge.is_negative());

        gauge.abs();
        assert_eq!(gauge.get(), 15.0);
        assert!(gauge.is_positive());

        gauge.clamp(5.0, 10.0);
        assert_eq!(gauge.get(), 10.0);
    }

    #[test]
    fn test_min_max_operations() {
        let gauge = Gauge::with_value(10.0);

        gauge.set_max(15.0);
        assert_eq!(gauge.get(), 15.0);

        gauge.set_max(12.0); // Should not change
        assert_eq!(gauge.get(), 15.0);

        gauge.set_min(8.0);
        assert_eq!(gauge.get(), 8.0);

        gauge.set_min(12.0); // Should not change
        assert_eq!(gauge.get(), 8.0);
    }

    #[test]
    fn test_compare_and_swap() {
        let gauge = Gauge::with_value(10.0);

        // Successful swap
        assert_eq!(gauge.compare_and_swap(10.0, 20.0), Ok(10.0));
        assert_eq!(gauge.get(), 20.0);

        // Failed swap
        assert_eq!(gauge.compare_and_swap(10.0, 30.0), Err(20.0));
        assert_eq!(gauge.get(), 20.0);
    }

    #[test]
    fn test_ema_update() {
        let gauge = Gauge::with_value(10.0);

        // EMA with alpha = 0.5: 0.5 * 20 + 0.5 * 10 = 15
        gauge.update_ema(20.0, 0.5);
        assert_eq!(gauge.get(), 15.0);

        // EMA with alpha = 0.3: 0.3 * 30 + 0.7 * 15 = 19.5
        gauge.update_ema(30.0, 0.3);
        assert_eq!(gauge.get(), 19.5);
    }

    #[test]
    fn test_percentage_gauge() {
        let gauge = specialized::PercentageGauge::new();

        gauge.set_percentage(75.5);
        assert_eq!(gauge.get_percentage(), 75.5);
        assert!((gauge.get_ratio() - 0.755).abs() < f64::EPSILON);

        gauge.set_ratio(0.9);
        assert_eq!(gauge.get_percentage(), 90.0);

        // Test clamping
        gauge.set_percentage(150.0);
        assert_eq!(gauge.get_percentage(), 100.0);
        assert!(gauge.is_full());

        gauge.set_percentage(-10.0);
        assert_eq!(gauge.get_percentage(), 0.0);
        assert!(gauge.is_empty());

        // Test add with clamping
        gauge.set_percentage(95.0);
        gauge.add_percentage(10.0);
        assert_eq!(gauge.get_percentage(), 100.0);
    }

    #[test]
    fn test_memory_gauge() {
        let gauge = specialized::MemoryGauge::new();

        gauge.set_bytes(1024 * 1024 * 1024); // 1GB

        assert_eq!(gauge.get_bytes(), 1024 * 1024 * 1024);
        assert!((gauge.get_mb() - 1024.0).abs() < 0.1);
        assert!((gauge.get_gb() - 1.0).abs() < 0.001);

        gauge.add_bytes(1024 * 1024); // Add 1MB
        assert!(gauge.get_mb() > 1024.0);
    }

    #[test]
    fn test_statistics() {
        let gauge = Gauge::with_value(42.0);

        let stats = gauge.stats();
        assert_eq!(stats.value, 42.0);
        assert!(stats.age <= gauge.age());
        assert!(stats.updates.is_none()); // Not tracked by default
    }

    #[test]
    fn test_high_concurrency() {
        let gauge = Arc::new(Gauge::new());
        let num_threads = 100;
        let operations_per_thread = 1000;

        let handles: Vec<_> = (0..num_threads)
            .map(|thread_id| {
                let gauge = Arc::clone(&gauge);
                thread::spawn(move || {
                    for i in 0..operations_per_thread {
                        let value = (thread_id * operations_per_thread + i) as f64;
                        gauge.set(value);
                        gauge.add(0.1);
                        gauge.multiply(1.001);
                    }
                })
            })
            .collect();

        for handle in handles {
            handle.join().unwrap();
        }

        // Just check that we can read the final value without panicking
        let final_value = gauge.get();
        assert!(final_value.is_finite());

        let stats = gauge.stats();
        assert!(stats.age <= gauge.age());
    }

    #[test]
    fn test_special_values() {
        let gauge = Gauge::new();

        // Test infinity handling
        gauge.set(f64::INFINITY);
        assert!(!gauge.is_finite());

        // Test NaN handling
        gauge.set(f64::NAN);
        assert!(!gauge.is_finite());

        // Reset to normal value
        gauge.set(42.0);
        assert!(gauge.is_finite());
    }

    #[test]
    fn test_display_and_debug() {
        let gauge = Gauge::with_value(42.5);

        let display_str = format!("{gauge}");
        assert!(display_str.contains("42.5"));

        let debug_str = format!("{gauge:?}");
        assert!(debug_str.contains("Gauge"));
        assert!(debug_str.contains("42.5"));
    }

    #[test]
    fn test_try_add_validation_and_overflow() {
        let gauge = Gauge::new();

        // Invalid deltas
        assert!(matches!(
            gauge.try_add(f64::NAN),
            Err(MetricsError::InvalidValue { .. })
        ));
        assert!(matches!(
            gauge.try_add(f64::INFINITY),
            Err(MetricsError::InvalidValue { .. })
        ));

        // Overflow on result becoming non-finite
        let gauge2 = Gauge::with_value(f64::MAX / 2.0);
        assert!(matches!(
            gauge2.try_add(f64::MAX),
            Err(MetricsError::Overflow)
        ));
    }

    #[test]
    fn test_try_set_and_min_max_validation() {
        let gauge = Gauge::new();
        assert!(matches!(
            gauge.try_set(f64::NAN),
            Err(MetricsError::InvalidValue { .. })
        ));

        // try_set_max invalid
        assert!(matches!(
            gauge.try_set_max(f64::INFINITY),
            Err(MetricsError::InvalidValue { .. })
        ));

        // try_set_min invalid
        assert!(matches!(
            gauge.try_set_min(f64::NAN),
            Err(MetricsError::InvalidValue { .. })
        ));
    }

    #[test]
    fn test_ema_alpha_boundaries() {
        let gauge = Gauge::with_value(10.0);

        // alpha < 0 should clamp to 0 -> unchanged
        gauge.update_ema(100.0, -1.0);
        assert_eq!(gauge.get(), 10.0);

        // alpha > 1 should clamp to 1 -> equals sample
        gauge.update_ema(100.0, 2.0);
        assert_eq!(gauge.get(), 100.0);

        // alpha 0 -> unchanged; alpha 1 -> exact sample
        gauge.set(5.0);
        gauge.update_ema(20.0, 0.0);
        assert_eq!(gauge.get(), 5.0);
        gauge.update_ema(20.0, 1.0);
        assert_eq!(gauge.get(), 20.0);
    }

    #[test]
    fn test_non_finite_math_helpers_are_noops() {
        let gauge = Gauge::with_value(12.0);

        gauge.add(f64::NAN);
        assert_eq!(gauge.get(), 12.0);

        gauge.multiply(f64::INFINITY);
        assert_eq!(gauge.get(), 12.0);

        gauge.divide(0.0);
        assert_eq!(gauge.get(), 12.0);

        let huge = Gauge::with_value(f64::MAX / 2.0);
        huge.multiply(4.0);
        assert_eq!(huge.get(), f64::MAX / 2.0);
    }
}

#[cfg(all(test, feature = "bench-tests", not(tarpaulin), not(coverage)))]
#[allow(unused_imports)]
mod benchmarks {
    use super::*;
    use std::time::Instant;

    #[cfg_attr(not(feature = "bench-tests"), ignore)]
    #[test]
    fn bench_gauge_set() {
        let gauge = Gauge::new();
        let iterations = 10_000_000;

        let start = Instant::now();
        for i in 0..iterations {
            gauge.set(i as f64);
        }
        let elapsed = start.elapsed();

        println!(
            "Gauge set: {:.2} ns/op",
            elapsed.as_nanos() as f64 / iterations as f64
        );

        // Throughput-only smoke check; Criterion is the regression detector.
        assert_eq!(gauge.get(), (iterations - 1) as f64);
    }

    #[cfg_attr(not(feature = "bench-tests"), ignore)]
    #[test]
    fn bench_gauge_add() {
        let gauge = Gauge::new();
        let iterations = 1_000_000;

        let start = Instant::now();
        for _ in 0..iterations {
            gauge.add(1.0);
        }
        let elapsed = start.elapsed();

        println!(
            "Gauge add: {:.2} ns/op",
            elapsed.as_nanos() as f64 / iterations as f64
        );

        // Throughput-only smoke check; Criterion is the regression detector.
        assert_eq!(gauge.get(), iterations as f64);
    }

    #[cfg_attr(not(feature = "bench-tests"), ignore)]
    #[test]
    fn bench_gauge_get() {
        let gauge = Gauge::with_value(42.5);
        let iterations = 100_000_000;

        let start = Instant::now();
        let mut sum = 0.0;
        for _ in 0..iterations {
            sum += gauge.get();
        }
        let elapsed = start.elapsed();

        println!(
            "Gauge get: {:.2} ns/op",
            elapsed.as_nanos() as f64 / iterations as f64
        );

        // Prevent optimization elision; Criterion is the regression detector.
        assert_eq!(sum, 42.5 * iterations as f64);
    }
}
