//! # Ultra-Fast Rate Meter
//!
//! High-performance rate calculations with tumbling-window tracking.
//!
//! ## Features
//!
//! - **Sub-microsecond tick operations** - Blazingly fast rate tracking
//! - **Tumbling window counters** - Per-second, per-minute, and per-hour
//!   event counters that reset atomically at each window boundary. This is a
//!   *tumbling* (non-overlapping) window, not a sliding window.
//! - **Multiple time windows** - Second, minute, hour rates
//! - **Lock-free** - Never blocks, never waits
//! - **Zero allocations** - Pure atomic operations
//! - **Rate limiting** - Built-in support for throttling
//!
//! ## Accuracy & concurrency contract
//!
//! `RateMeter` trades a small amount of accuracy under contention for
//! lock-free hot-path performance. Callers should be aware of the following
//! best-effort semantics:
//!
//! - **Window-boundary event-loss bound.** When two or more threads cross a
//!   second/minute/hour boundary concurrently, one thread wins the boundary
//!   CAS and `store()`s its delta into the new window; threads that lost the
//!   CAS fall back to `fetch_add()` — but if they were carrying ticks intended
//!   for the prior window, those ticks land in the new window's counter
//!   instead. The total event count (`total_events`) is always correct; only
//!   the per-window counts may be off by up to `(num_threads − 1)` at each
//!   boundary.
//! - **`tick_if_under_limit` TOCTOU overshoot.** `can_allow` → `tick` is not
//!   a single atomic operation. Multiple threads can each observe a passing
//!   `can_allow` and then `tick`, briefly overshooting the limit by up to
//!   `num_threads − 1` events. For strict admission control, see the
//!   `TokenBucket` primitive (introduced in 0.9.5).
//!
//! Both effects are bounded and self-correcting on the next window roll. For
//! observability use cases (dashboards, alerting) the error is well below the
//! sampling noise floor; for billing or hard-limit enforcement, use a
//! token-bucket primitive instead.

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

/// Ultra-fast rate meter with tumbling-window counters.
///
/// Tracks events per second, per minute, and per hour with minimal overhead.
/// Counters reset at each second/minute/hour boundary (tumbling windows, not
/// sliding). Cache-line aligned to prevent false sharing.
#[repr(align(64))]
pub struct RateMeter {
    /// Total events seen (monotonic counter)
    total_events: AtomicU64,
    /// Current second's events
    current_second_events: AtomicU32,
    /// Current minute's events  
    current_minute_events: AtomicU32,
    /// Current hour's events
    current_hour_events: AtomicU32,
    /// Last update timestamp (seconds since epoch)
    last_second: AtomicU64,
    /// Last minute timestamp
    last_minute: AtomicU64,
    /// Last hour timestamp
    last_hour: AtomicU64,
    /// Window size for rate calculations (nanoseconds)
    window_ns: u64,
    /// Creation timestamp
    created_at: Instant,
}

/// Rate statistics
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct RateStats {
    /// Total events recorded
    pub total_events: u64,
    /// Events per second (current window)
    pub per_second: f64,
    /// Events per minute (current window)
    pub per_minute: f64,
    /// Events per hour (current window)
    pub per_hour: f64,
    /// Average rate since creation
    pub average_rate: f64,
    /// Time since creation
    pub age: Duration,
    /// Current window fill percentage
    pub window_fill: f64,
}

impl RateMeter {
    /// Create new rate meter with 1-second window
    #[inline]
    pub fn new() -> Self {
        Self::with_window(Duration::from_secs(1))
    }

    /// Create rate meter with custom window size
    #[inline]
    pub fn with_window(window: Duration) -> Self {
        Self {
            total_events: AtomicU64::new(0),
            current_second_events: AtomicU32::new(0),
            current_minute_events: AtomicU32::new(0),
            current_hour_events: AtomicU32::new(0),
            last_second: AtomicU64::new(0),
            last_minute: AtomicU64::new(0),
            last_hour: AtomicU64::new(0),
            window_ns: window.as_nanos() as u64,
            created_at: Instant::now(),
        }
    }

    /// Record an event - THE FASTEST PATH
    ///
    /// This is optimized for maximum speed:
    /// - Single atomic increment for total
    /// - Lazy window updates only when needed
    /// - Branch prediction friendly
    #[inline(always)]
    pub fn tick(&self) {
        self.tick_n(1);
    }

    /// Try to record a single event with overflow checks
    ///
    /// Returns `Err(MetricsError::Overflow)` if incrementing the total or any
    /// current window counter would overflow. On success returns `Ok(())`.
    ///
    /// Example
    /// ```
    /// use metrics_lib::{RateMeter, MetricsError};
    /// let m = RateMeter::new();
    /// m.try_tick().unwrap();
    /// assert_eq!(m.total(), 1);
    /// ```
    #[inline(always)]
    pub fn try_tick(&self) -> Result<()> {
        self.try_tick_n(1)
    }

    /// Record N events at once
    #[inline(always)]
    pub fn tick_n(&self, n: u32) {
        if n == 0 {
            return;
        }

        // Always increment total (fastest path)
        self.total_events.fetch_add(n as u64, Ordering::Relaxed);

        // Update windows (lazy - only when needed)
        let now = self.get_unix_timestamp();
        self.update_windows(now, n);
    }

    /// Try to record N events at once with overflow checks
    ///
    /// - Returns `Ok(())` on success.
    /// - Returns `Err(MetricsError::Overflow)` if incrementing any of the
    ///   involved counters would overflow (`total_events`, `current_second_events`,
    ///   `current_minute_events`, or `current_hour_events`).
    ///
    /// Example
    /// ```
    /// use metrics_lib::{RateMeter, MetricsError};
    /// let m = RateMeter::new();
    /// assert!(m.try_tick_n(5).is_ok());
    /// assert_eq!(m.total(), 5);
    /// ```
    #[inline(always)]
    pub fn try_tick_n(&self, n: u32) -> Result<()> {
        if n == 0 {
            return Ok(());
        }

        // Check total_events overflow
        let total = self.total_events.load(Ordering::Relaxed);
        if total.checked_add(n as u64).is_none() {
            return Err(MetricsError::Overflow);
        }

        // Pre-check window counters roughly (race-safe best-effort). Since windows reset, we
        // only guard against immediate and obvious overflow.
        let sec = self.current_second_events.load(Ordering::Relaxed);
        if sec.checked_add(n).is_none() {
            return Err(MetricsError::Overflow);
        }
        let min = self.current_minute_events.load(Ordering::Relaxed);
        if min.checked_add(n).is_none() {
            return Err(MetricsError::Overflow);
        }
        let hour = self.current_hour_events.load(Ordering::Relaxed);
        if hour.checked_add(n).is_none() {
            return Err(MetricsError::Overflow);
        }

        // Apply updates
        self.total_events.fetch_add(n as u64, Ordering::Relaxed);
        let now = self.get_unix_timestamp();
        self.update_windows(now, n);
        Ok(())
    }

    /// Get current rate (events per second in current window)
    ///
    /// Note: This method is `#[must_use]`. The returned rate conveys the
    /// current measurement; ignoring it may indicate a logic bug.
    #[must_use]
    #[inline]
    pub fn rate(&self) -> f64 {
        let now = self.get_unix_timestamp();
        self.update_windows(now, 0);

        let events = self.current_second_events.load(Ordering::Relaxed);
        events as f64
    }

    /// Get rate per second
    ///
    /// Note: `#[must_use]` — see [`Self::rate`].
    #[must_use]
    #[inline]
    pub fn rate_per_second(&self) -> f64 {
        self.rate()
    }

    /// Get rate per minute
    ///
    /// Note: `#[must_use]`. The returned value reflects the current window
    /// state; dropping it may hide incorrect flow.
    #[must_use]
    #[inline]
    pub fn rate_per_minute(&self) -> f64 {
        let now = self.get_unix_timestamp();
        self.update_windows(now, 0);

        let events = self.current_minute_events.load(Ordering::Relaxed);
        events as f64
    }

    /// Get rate per hour
    ///
    /// Note: `#[must_use]`. The returned value reflects the current window
    /// state; dropping it may hide incorrect flow.
    #[must_use]
    #[inline]
    pub fn rate_per_hour(&self) -> f64 {
        let now = self.get_unix_timestamp();
        self.update_windows(now, 0);

        let events = self.current_hour_events.load(Ordering::Relaxed);
        events as f64
    }

    /// Get total events since creation
    ///
    /// Note: `#[must_use]`. Total is often used for invariants and sanity
    /// checks; ignoring it may indicate a bug.
    #[must_use]
    #[inline(always)]
    pub fn total(&self) -> u64 {
        self.total_events.load(Ordering::Relaxed)
    }

    /// Check if rate exceeds limit
    ///
    /// Note: `#[must_use]`. The boolean result determines control flow; if you
    /// don't branch on it, consider whether the call is needed.
    #[must_use]
    #[inline]
    pub fn exceeds_rate(&self, limit: f64) -> bool {
        self.rate() > limit
    }

    /// Check if we can allow N more events without exceeding limit
    ///
    /// Note: `#[must_use]`. The boolean result determines control flow; if you
    /// don't branch on it, consider whether the call is needed.
    #[must_use]
    #[inline]
    pub fn can_allow(&self, n: u32, limit: f64) -> bool {
        let current_rate = self.rate();
        (current_rate + n as f64) <= limit
    }

    /// Rate limiting - tick only if under limit
    ///
    /// Note: `#[must_use]`. The boolean result indicates whether a tick was
    /// recorded; ignoring it may hide throttling decisions.
    #[must_use]
    #[inline]
    pub fn tick_if_under_limit(&self, limit: f64) -> bool {
        if self.can_allow(1, limit) {
            self.tick();
            true
        } else {
            false
        }
    }

    /// Try to tick if under limit, with overflow checks
    ///
    /// Attempts to record a single event only if the current rate would not
    /// exceed `limit`. Returns:
    /// - `Ok(true)` if the event was recorded.
    /// - `Ok(false)` if the event would exceed the limit (no change made).
    /// - `Err(MetricsError::Overflow)` if counters would overflow while recording.
    ///
    /// Example
    /// ```
    /// use metrics_lib::RateMeter;
    /// let m = RateMeter::new();
    /// assert!(m.try_tick_if_under_limit(10.0).unwrap());
    /// ```
    #[inline]
    pub fn try_tick_if_under_limit(&self, limit: f64) -> Result<bool> {
        if self.can_allow(1, limit) {
            self.try_tick()?;
            Ok(true)
        } else {
            Ok(false)
        }
    }

    /// Burst rate limiting - allow N events if under limit
    ///
    /// Note: `#[must_use]`. The boolean result indicates whether a burst was
    /// recorded; ignoring it may hide throttling decisions.
    #[must_use]
    #[inline]
    pub fn tick_burst_if_under_limit(&self, n: u32, limit: f64) -> bool {
        if self.can_allow(n, limit) {
            self.tick_n(n);
            true
        } else {
            false
        }
    }

    /// Reset all counters
    #[inline]
    pub fn reset(&self) {
        let now = self.get_unix_timestamp();

        self.total_events.store(0, Ordering::SeqCst);
        self.current_second_events.store(0, Ordering::SeqCst);
        self.current_minute_events.store(0, Ordering::SeqCst);
        self.current_hour_events.store(0, Ordering::SeqCst);
        self.last_second.store(now, Ordering::SeqCst);
        self.last_minute.store(now / 60, Ordering::SeqCst);
        self.last_hour.store(now / 3600, Ordering::SeqCst);
    }

    /// Get comprehensive statistics
    ///
    /// Note: `#[must_use]`. Statistics summarize current state; dropping the
    /// result may indicate a logic bug.
    #[must_use]
    pub fn stats(&self) -> RateStats {
        let now = self.get_unix_timestamp();
        self.update_windows(now, 0);

        let total_events = self.total();
        let per_second = self.current_second_events.load(Ordering::Relaxed) as f64;
        let per_minute = self.current_minute_events.load(Ordering::Relaxed) as f64;
        let per_hour = self.current_hour_events.load(Ordering::Relaxed) as f64;

        let age = self.created_at.elapsed();
        let average_rate = if age.as_secs_f64() > 0.0 {
            total_events as f64 / age.as_secs_f64()
        } else {
            0.0
        };

        // Calculate window fill (how much of the window has data)
        let window_fill = if self.window_ns > 0 {
            let window_seconds = self.window_ns as f64 / 1_000_000_000.0;
            let elapsed_in_window = age.as_secs_f64().min(window_seconds);
            (elapsed_in_window / window_seconds * 100.0).min(100.0)
        } else {
            100.0
        };

        RateStats {
            total_events,
            per_second,
            per_minute,
            per_hour,
            average_rate,
            age,
            window_fill,
        }
    }

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

    /// Check if rate meter is empty (no events recorded)
    ///
    /// Note: `#[must_use]`. The boolean result determines control flow.
    #[must_use]
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.total() == 0
    }

    // Internal helper methods

    #[inline(always)]
    fn get_unix_timestamp(&self) -> u64 {
        // Use the current system time in seconds since UNIX_EPOCH.
        // The previous implementation added `created_at.elapsed()` to now, which
        // double-counted time and could skew window transitions.
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs()
    }

    #[inline]
    fn update_windows(&self, now: u64, new_events: u32) {
        // Update second window
        let current_second = now;
        let last_second = self.last_second.load(Ordering::Relaxed);

        if current_second != last_second {
            // New second - reset counter
            if self
                .last_second
                .compare_exchange(
                    last_second,
                    current_second,
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                )
                .is_ok()
            {
                self.current_second_events
                    .store(new_events, Ordering::Relaxed);
            } else {
                // Another thread updated, add to current
                self.current_second_events
                    .fetch_add(new_events, Ordering::Relaxed);
            }
        } else if new_events > 0 {
            // Same second - add events
            self.current_second_events
                .fetch_add(new_events, Ordering::Relaxed);
        }

        // Update minute window
        let current_minute = now / 60;
        let last_minute = self.last_minute.load(Ordering::Relaxed);

        if current_minute != last_minute {
            if self
                .last_minute
                .compare_exchange(
                    last_minute,
                    current_minute,
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                )
                .is_ok()
            {
                self.current_minute_events
                    .store(new_events, Ordering::Relaxed);
            } else {
                self.current_minute_events
                    .fetch_add(new_events, Ordering::Relaxed);
            }
        } else if new_events > 0 {
            self.current_minute_events
                .fetch_add(new_events, Ordering::Relaxed);
        }

        // Update hour window
        let current_hour = now / 3600;
        let last_hour = self.last_hour.load(Ordering::Relaxed);

        if current_hour != last_hour {
            if self
                .last_hour
                .compare_exchange(
                    last_hour,
                    current_hour,
                    Ordering::Relaxed,
                    Ordering::Relaxed,
                )
                .is_ok()
            {
                self.current_hour_events
                    .store(new_events, Ordering::Relaxed);
            } else {
                self.current_hour_events
                    .fetch_add(new_events, Ordering::Relaxed);
            }
        } else if new_events > 0 {
            self.current_hour_events
                .fetch_add(new_events, Ordering::Relaxed);
        }
    }
}

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

impl std::fmt::Display for RateMeter {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "RateMeter({:.1}/s, {} total)", self.rate(), self.total())
    }
}

impl std::fmt::Debug for RateMeter {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let stats = self.stats();
        f.debug_struct("RateMeter")
            .field("total_events", &stats.total_events)
            .field("per_second", &stats.per_second)
            .field("per_minute", &stats.per_minute)
            .field("average_rate", &stats.average_rate)
            .field("age", &stats.age)
            .finish()
    }
}

// Thread safety
// RateMeter is composed of atomic types (Send + Sync) and `Instant`/`u64`
// (Send + Sync). The compiler derives Send + Sync automatically.

/// Specialized rate meters for common use cases
pub mod specialized {
    use super::*;

    /// API rate limiter (requests per second)
    #[repr(align(64))]
    pub struct ApiRateLimiter {
        meter: RateMeter,
        limit: AtomicU32, // requests per second
    }

    impl ApiRateLimiter {
        /// Create API rate limiter with requests per second limit
        #[inline]
        pub fn new(requests_per_second: u32) -> Self {
            Self {
                meter: RateMeter::new(),
                limit: AtomicU32::new(requests_per_second),
            }
        }

        /// Try to make a request - returns true if allowed
        #[inline]
        pub fn try_request(&self) -> bool {
            let limit = self.limit.load(Ordering::Relaxed) as f64;
            self.meter.tick_if_under_limit(limit)
        }

        /// Try to make N requests - returns true if all allowed
        #[inline]
        pub fn try_requests(&self, n: u32) -> bool {
            let limit = self.limit.load(Ordering::Relaxed) as f64;
            self.meter.tick_burst_if_under_limit(n, limit)
        }

        /// Update the rate limit
        #[inline]
        pub fn set_limit(&self, requests_per_second: u32) {
            self.limit.store(requests_per_second, Ordering::Relaxed);
        }

        /// Get current limit
        #[inline]
        pub fn get_limit(&self) -> u32 {
            self.limit.load(Ordering::Relaxed)
        }

        /// Get current rate
        #[inline]
        pub fn current_rate(&self) -> f64 {
            self.meter.rate()
        }

        /// Get total requests
        #[inline]
        pub fn total_requests(&self) -> u64 {
            self.meter.total()
        }

        /// Check if currently over limit
        #[inline]
        pub fn is_over_limit(&self) -> bool {
            let limit = self.limit.load(Ordering::Relaxed) as f64;
            self.meter.rate() > limit
        }

        /// Reset the rate limiter
        #[inline]
        pub fn reset(&self) {
            self.meter.reset();
        }
    }

    impl Default for ApiRateLimiter {
        fn default() -> Self {
            Self::new(1000)
        } // 1000 req/s default
    }

    /// Throughput meter for measuring data rates
    #[repr(align(64))]
    pub struct ThroughputMeter {
        meter: RateMeter,
    }

    impl ThroughputMeter {
        /// Create new throughput meter
        #[inline]
        pub fn new() -> Self {
            Self {
                meter: RateMeter::new(),
            }
        }

        /// Record bytes transferred
        #[inline(always)]
        pub fn record_bytes(&self, bytes: u64) {
            self.meter.tick_n(bytes as u32);
        }

        /// Get bytes per second
        #[inline]
        pub fn bytes_per_second(&self) -> f64 {
            self.meter.rate()
        }

        /// Get kilobytes per second
        #[inline]
        pub fn kb_per_second(&self) -> f64 {
            self.meter.rate() / 1024.0
        }

        /// Get megabytes per second
        #[inline]
        pub fn mb_per_second(&self) -> f64 {
            self.meter.rate() / (1024.0 * 1024.0)
        }

        /// Get gigabytes per second
        #[inline]
        pub fn gb_per_second(&self) -> f64 {
            self.meter.rate() / (1024.0 * 1024.0 * 1024.0)
        }

        /// Get total bytes transferred
        #[inline]
        pub fn total_bytes(&self) -> u64 {
            self.meter.total()
        }

        /// Reset the throughput meter
        #[inline]
        pub fn reset(&self) {
            self.meter.reset();
        }
    }

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

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

    #[test]
    fn test_basic_operations() {
        let meter = RateMeter::new();

        assert!(meter.is_empty());
        assert_eq!(meter.total(), 0);
        assert_eq!(meter.rate(), 0.0);

        meter.tick();
        assert!(!meter.is_empty());
        assert_eq!(meter.total(), 1);

        meter.tick_n(5);
        assert_eq!(meter.total(), 6);
    }

    #[test]
    fn test_rate_calculations() {
        let meter = RateMeter::new();

        // Record events rapidly in same second
        for _ in 0..100 {
            meter.tick();
        }

        let rate = meter.rate();
        assert_eq!(rate, 100.0);
        assert_eq!(meter.rate_per_second(), 100.0);
    }

    #[test]
    fn test_multiple_windows() {
        let meter = RateMeter::new();

        // Record events
        for _ in 0..60 {
            meter.tick();
        }

        let stats = meter.stats();
        assert_eq!(stats.total_events, 60);
        assert_eq!(stats.per_second, 60.0);
        assert_eq!(stats.per_minute, 60.0);
        assert_eq!(stats.per_hour, 60.0);
    }

    #[test]
    fn test_rate_limiting() {
        let meter = RateMeter::new();

        // Allow requests under limit
        assert!(meter.tick_if_under_limit(10.0));
        assert!(meter.tick_if_under_limit(10.0));

        // Add more to approach limit
        meter.tick_n(8);

        // Should now be at/over limit
        assert!(!meter.tick_if_under_limit(10.0));
        assert!(meter.exceeds_rate(9.0));
        assert!(!meter.exceeds_rate(11.0));
    }

    #[test]
    fn test_burst_rate_limiting() {
        let meter = RateMeter::new();

        // Try burst under limit
        assert!(meter.tick_burst_if_under_limit(5, 10.0));
        assert_eq!(meter.total(), 5);

        // Try burst that would exceed limit
        assert!(!meter.tick_burst_if_under_limit(10, 10.0));
        assert_eq!(meter.total(), 5); // Should be unchanged

        // Try smaller burst that fits
        assert!(meter.tick_burst_if_under_limit(3, 10.0));
        assert_eq!(meter.total(), 8);
    }

    #[test]
    fn test_can_allow() {
        let meter = RateMeter::new();

        meter.tick_n(5);

        assert!(meter.can_allow(3, 10.0)); // 5 + 3 = 8 <= 10
        assert!(!meter.can_allow(6, 10.0)); // 5 + 6 = 11 > 10
        assert!(meter.can_allow(5, 10.0)); // 5 + 5 = 10 <= 10
    }

    #[test]
    fn test_reset() {
        let meter = RateMeter::new();

        meter.tick_n(100);
        assert_eq!(meter.total(), 100);
        assert!(meter.rate() > 0.0);

        meter.reset();
        assert_eq!(meter.total(), 0);
        assert_eq!(meter.rate(), 0.0);
        assert!(meter.is_empty());
    }

    #[test]
    fn test_statistics() {
        let meter = RateMeter::new();

        meter.tick_n(50);

        let stats = meter.stats();
        assert_eq!(stats.total_events, 50);
        assert_eq!(stats.per_second, 50.0);
        assert!(stats.average_rate > 0.0);
        assert!(stats.age > Duration::from_nanos(0));
        assert!(stats.window_fill >= 0.0);
    }

    #[test]
    fn test_api_rate_limiter() {
        let limiter = specialized::ApiRateLimiter::new(10);

        // Should allow requests under limit
        for _ in 0..10 {
            assert!(limiter.try_request());
        }

        // Should deny request over limit
        assert!(!limiter.try_request());

        // Check status - rate is at limit (10), not over it since request was denied
        assert_eq!(limiter.current_rate(), 10.0);
        assert_eq!(limiter.total_requests(), 10);
        assert_eq!(limiter.get_limit(), 10);

        // Update limit
        limiter.set_limit(20);
        assert_eq!(limiter.get_limit(), 20);
        assert!(!limiter.is_over_limit()); // Now under new limit

        // Test burst requests
        limiter.reset();
        assert!(limiter.try_requests(5));
        assert_eq!(limiter.total_requests(), 5);

        assert!(!limiter.try_requests(20)); // Would exceed limit
        assert_eq!(limiter.total_requests(), 5); // Unchanged
    }

    #[test]
    fn test_throughput_meter() {
        let meter = specialized::ThroughputMeter::new();

        meter.record_bytes(1024); // 1 KB
        assert_eq!(meter.bytes_per_second(), 1024.0);
        assert_eq!(meter.kb_per_second(), 1.0);
        assert_eq!(meter.total_bytes(), 1024);

        meter.record_bytes(1024 * 1024); // 1 MB more
        assert_eq!(meter.total_bytes(), 1024 + 1024 * 1024);
        assert!((meter.mb_per_second() - 1.001).abs() < 0.01);
    }

    #[test]
    fn test_high_concurrency() {
        let meter = Arc::new(RateMeter::new());
        let num_threads = 50;
        let ticks_per_thread = 1000;

        let handles: Vec<_> = (0..num_threads)
            .map(|_| {
                let meter = Arc::clone(&meter);
                thread::spawn(move || {
                    for _ in 0..ticks_per_thread {
                        meter.tick();
                    }
                })
            })
            .collect();

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

        assert_eq!(meter.total(), num_threads * ticks_per_thread);

        let stats = meter.stats();
        assert!(stats.average_rate > 0.0);
        assert_eq!(stats.total_events, num_threads * ticks_per_thread);
    }

    #[test]
    fn test_concurrent_rate_limiting() {
        let limiter = Arc::new(specialized::ApiRateLimiter::new(100));
        let num_threads = 20;

        let handles: Vec<_> = (0..num_threads)
            .map(|_| {
                let limiter = Arc::clone(&limiter);
                thread::spawn(move || {
                    let mut successful = 0;
                    for _ in 0..10 {
                        if limiter.try_request() {
                            successful += 1;
                        }
                    }
                    successful
                })
            })
            .collect();

        let total_successful: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();

        // Should be limited to around 100 requests.
        // Allow tolerance for concurrent overshoot across varied CI runners.
        // Cap by total attempts (num_threads * 10) and relax to up to 2x the limit in extreme cases.
        // Under coverage instrumentation, timing can skew more; keep a generous bound.
        let total_attempts = num_threads * 10;
        let strict_cap = 2 * 100; // 2x limit
        let upper_bound = if cfg!(coverage) {
            total_attempts.min(strict_cap.max(160))
        } else {
            total_attempts.min(strict_cap)
        };
        assert!(
            total_successful <= upper_bound,
            "total_successful={total_successful} > upper_bound={upper_bound}",
        );
        assert!(
            total_successful >= 90,
            "total_successful={total_successful} < lower_bound=90",
        ); // Account for timing variations
    }

    #[test]
    fn test_display_and_debug() {
        let meter = RateMeter::new();
        meter.tick_n(42);

        let display_str = format!("{meter}");
        assert!(display_str.contains("RateMeter"));
        assert!(display_str.contains("42 total"));

        let debug_str = format!("{meter:?}");
        assert!(debug_str.contains("RateMeter"));
        assert!(debug_str.contains("total_events"));
    }

    #[test]
    fn test_custom_window() {
        let meter = RateMeter::with_window(Duration::from_secs(5));

        meter.tick_n(10);
        assert_eq!(meter.total(), 10);
        assert_eq!(meter.rate(), 10.0);

        let stats = meter.stats();
        assert!(stats.window_fill >= 0.0);
    }

    // New tests for try_ variants and overflow/error conditions
    #[test]
    fn test_try_tick_and_try_tick_n_ok() {
        let meter = RateMeter::new();
        assert!(meter.try_tick().is_ok());
        assert!(meter.try_tick_n(5).is_ok());
        assert_eq!(meter.total(), 6);
    }

    #[test]
    fn test_try_tick_n_total_overflow() {
        let meter = RateMeter::new();
        // Directly set near overflow
        meter.total_events.store(u64::MAX - 1, Ordering::Relaxed);
        // Adding 2 should overflow
        let err = meter.try_tick_n(2).unwrap_err();
        assert_eq!(err, MetricsError::Overflow);
    }

    #[test]
    fn test_try_tick_n_window_overflow() {
        let meter = RateMeter::new();
        // Force current windows to near overflow and ensure we are in the same second/minute/hour
        let now = meter.get_unix_timestamp();
        meter.last_second.store(now, Ordering::Relaxed);
        meter.last_minute.store(now / 60, Ordering::Relaxed);
        meter.last_hour.store(now / 3600, Ordering::Relaxed);

        meter
            .current_second_events
            .store(u32::MAX - 1, Ordering::Relaxed);
        meter
            .current_minute_events
            .store(u32::MAX - 1, Ordering::Relaxed);
        meter
            .current_hour_events
            .store(u32::MAX - 1, Ordering::Relaxed);

        // Any n >= 2 should trip the pre-check overflow guard
        let err = meter.try_tick_n(2).unwrap_err();
        assert_eq!(err, MetricsError::Overflow);
    }

    #[test]
    fn test_try_tick_if_under_limit() {
        let meter = RateMeter::new();
        // Under limit should tick and return true
        assert!(meter.try_tick_if_under_limit(10.0).unwrap());
        // Drive rate up close to limit
        assert!(meter.try_tick_n(8).is_ok());
        // Now at 9; allow one more at limit 10
        assert!(meter.try_tick_if_under_limit(10.0).unwrap());
        // Next should be denied (would exceed)
        assert!(!meter.try_tick_if_under_limit(10.0).unwrap());
    }
}

#[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_rate_meter_tick() {
        let meter = RateMeter::new();
        let iterations = 10_000_000;

        let start = Instant::now();
        for _ in 0..iterations {
            meter.tick();
        }
        let elapsed = start.elapsed();

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

        // Throughput-only smoke check; Criterion is the regression detector.
        assert_eq!(meter.total(), iterations);
    }

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

        let start = Instant::now();
        for i in 0..iterations {
            meter.tick_n((i % 10) + 1);
        }
        let elapsed = start.elapsed();

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

        // Throughput-only smoke check; Criterion is the regression detector.
    }

    #[cfg_attr(not(feature = "bench-tests"), ignore)]
    #[test]
    fn bench_rate_calculation() {
        let meter = RateMeter::new();

        // Fill with data
        meter.tick_n(1000);

        let iterations = 1_000_000;
        let start = Instant::now();

        for _ in 0..iterations {
            let _ = meter.rate();
        }

        let elapsed = start.elapsed();
        println!(
            "RateMeter rate: {:.2} ns/op",
            elapsed.as_nanos() as f64 / iterations as f64
        );

        // Throughput-only smoke check; Criterion is the regression detector.
    }

    #[cfg_attr(not(feature = "bench-tests"), ignore)]
    #[test]
    fn bench_api_rate_limiter() {
        let limiter = specialized::ApiRateLimiter::new(1_000_000); // High limit
        let iterations = 1_000_000;

        let start = Instant::now();
        for _ in 0..iterations {
            let _ = limiter.try_request();
        }
        let elapsed = start.elapsed();

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

        // Throughput-only smoke check; Criterion is the regression detector.
    }
}
