//! # TapMetrics — the "observe" half of "drop + observe" (slice-1 §3.8)
//!
//! Atomic counters shared between `TapAudioPipe` (hot path — inbound/outbound
//! drops, playout overflow/underflow) and `TapClient` (seq gaps, unknown
//! frames, malformed frames, reconnect attempts). All ops are
//! `fetch_add(_, Ordering::Relaxed)` — we're using the counts for
//! observability, not synchronization, so relaxed ordering is correct +
//! cheapest. A snapshot read is taken at log time (e.g. once per second or
//! on tap-disconnect).

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

/// Bounded playout ring capacity (spec §4.1: 5 frames = 100 ms at 20 ms/frame).
/// Tunable *constant* (no runtime config in slice-2; a future-rung concern).
pub const TAP_PLAYOUT_FRAMES: usize = 5;

#[derive(Debug, Default)]
pub struct TapMetrics {
    pub inbound_dropped: AtomicU64,
    pub outbound_dropped: AtomicU64,
    pub playout_overflow: AtomicU64,
    pub playout_underflow: AtomicU64,
    pub seq_gaps: AtomicU64,
    pub unknown_frames: AtomicU64,
    pub malformed_frames: AtomicU64,
    pub reconnect_attempts: AtomicU64,
    /// slice-4 §3.3: count of in-flight `audio_out` frames dropped from
    /// `rx_audio_out` during `barge_in_flush`. The drain makes the resume
    /// condition race-free — the first `audio_out` observed post-barge is
    /// provably post-barge.
    pub barge_drained_inflight: AtomicU64,
}

impl TapMetrics {
    pub fn new() -> Arc<Self> {
        Arc::new(Self::default())
    }

    /// Read a consistent-ish snapshot. Not atomic across fields (each is
    /// independently read), but for log/observability purposes that's fine —
    /// we're reporting "approximately this many X happened," not synchronizing.
    pub fn snapshot(&self) -> MetricsSnapshot {
        MetricsSnapshot {
            inbound_dropped: self.inbound_dropped.load(Ordering::Relaxed),
            outbound_dropped: self.outbound_dropped.load(Ordering::Relaxed),
            playout_overflow: self.playout_overflow.load(Ordering::Relaxed),
            playout_underflow: self.playout_underflow.load(Ordering::Relaxed),
            seq_gaps: self.seq_gaps.load(Ordering::Relaxed),
            unknown_frames: self.unknown_frames.load(Ordering::Relaxed),
            malformed_frames: self.malformed_frames.load(Ordering::Relaxed),
            reconnect_attempts: self.reconnect_attempts.load(Ordering::Relaxed),
            barge_drained_inflight: self.barge_drained_inflight.load(Ordering::Relaxed),
        }
    }
}

#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct MetricsSnapshot {
    pub inbound_dropped: u64,
    pub outbound_dropped: u64,
    pub playout_overflow: u64,
    pub playout_underflow: u64,
    pub seq_gaps: u64,
    pub unknown_frames: u64,
    pub malformed_frames: u64,
    pub reconnect_attempts: u64,
    pub barge_drained_inflight: u64,
}
