//! Tier 0 metrics: event counter snapshots.
//!
//! Computed from [`SlidingWindowCounters`](super::SlidingWindowCounters)
//! data accumulated by `TrackedDelivery` ack/nack operations.

/// A point-in-time snapshot of event counters for a single queue.
///
/// Produced by [`SlidingWindowCounters::snapshot_and_reset()`](super::SlidingWindowCounters::snapshot_and_reset).
#[derive(Debug, Clone, Copy)]
pub struct Tier0Snapshot {
    /// Total jobs processed in this window.
    pub processed_count: u64,
    /// Jobs that completed successfully (acked).
    pub success_count: u64,
    /// Jobs that failed (nacked/rejected without requeue).
    pub failure_count: u64,
    /// Sum of all processing durations in milliseconds.
    pub total_duration_ms: u64,
}

impl Tier0Snapshot {
    /// Jobs processed per second.
    ///
    /// Returns `0.0` if `window_seconds` is zero.
    pub fn throughput(&self, window_seconds: f64) -> f64 {
        if window_seconds <= 0.0 {
            return 0.0;
        }
        self.processed_count as f64 / window_seconds
    }

    /// Percentage of jobs that succeeded (0.0–100.0).
    ///
    /// Returns `100.0` if no jobs were processed (vacuous truth).
    pub fn success_rate(&self) -> f64 {
        if self.processed_count == 0 {
            return 100.0;
        }
        (self.success_count as f64 / self.processed_count as f64) * 100.0
    }

    /// Percentage of jobs that failed (0.0–100.0).
    ///
    /// Returns `0.0` if no jobs were processed.
    pub fn error_rate(&self) -> f64 {
        if self.processed_count == 0 {
            return 0.0;
        }
        (self.failure_count as f64 / self.processed_count as f64) * 100.0
    }

    /// Average processing time in milliseconds.
    ///
    /// Returns `0.0` if no jobs were processed.
    pub fn avg_processing_ms(&self) -> f64 {
        if self.processed_count == 0 {
            return 0.0;
        }
        self.total_duration_ms as f64 / self.processed_count as f64
    }
}

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

    #[test]
    fn empty_snapshot_defaults() {
        let snap = Tier0Snapshot {
            processed_count: 0,
            success_count: 0,
            failure_count: 0,
            total_duration_ms: 0,
        };
        assert_eq!(snap.throughput(60.0), 0.0);
        assert_eq!(snap.success_rate(), 100.0);
        assert_eq!(snap.error_rate(), 0.0);
        assert_eq!(snap.avg_processing_ms(), 0.0);
    }

    #[test]
    fn throughput_calculation() {
        let snap = Tier0Snapshot {
            processed_count: 120,
            success_count: 100,
            failure_count: 20,
            total_duration_ms: 12000,
        };
        assert!((snap.throughput(60.0) - 2.0).abs() < f64::EPSILON);
    }

    #[test]
    fn throughput_zero_window() {
        let snap = Tier0Snapshot {
            processed_count: 10,
            success_count: 10,
            failure_count: 0,
            total_duration_ms: 1000,
        };
        assert_eq!(snap.throughput(0.0), 0.0);
    }

    #[test]
    fn success_and_error_rates() {
        let snap = Tier0Snapshot {
            processed_count: 100,
            success_count: 75,
            failure_count: 25,
            total_duration_ms: 10000,
        };
        assert!((snap.success_rate() - 75.0).abs() < f64::EPSILON);
        assert!((snap.error_rate() - 25.0).abs() < f64::EPSILON);
    }

    #[test]
    fn all_success() {
        let snap = Tier0Snapshot {
            processed_count: 50,
            success_count: 50,
            failure_count: 0,
            total_duration_ms: 5000,
        };
        assert!((snap.success_rate() - 100.0).abs() < f64::EPSILON);
        assert_eq!(snap.error_rate(), 0.0);
    }

    #[test]
    fn avg_processing_ms() {
        let snap = Tier0Snapshot {
            processed_count: 4,
            success_count: 3,
            failure_count: 1,
            total_duration_ms: 400,
        };
        assert!((snap.avg_processing_ms() - 100.0).abs() < f64::EPSILON);
    }
}
