#[cfg(not(coverage))]
impl PreviewBinding {
    pub fn set_enabled(&self, enabled: bool) {
        let was_enabled = self.enabled.swap(enabled, Ordering::AcqRel);
        match (was_enabled, enabled) {
            (false, true) => {
                self.active_bindings.fetch_add(1, Ordering::AcqRel);
            }
            (true, false) => {
                self.active_bindings.fetch_sub(1, Ordering::AcqRel);
            }
            _ => {}
        }
    }

    pub fn close(&self) {
        if !self.alive.swap(false, Ordering::AcqRel) {
            return;
        }
        if self.enabled.swap(false, Ordering::AcqRel) {
            self.active_bindings.fetch_sub(1, Ordering::AcqRel);
        }
    }

    #[cfg(test)]
    pub(crate) fn test_stub() -> Self {
        Self {
            enabled: Arc::new(AtomicBool::new(true)),
            alive: Arc::new(AtomicBool::new(true)),
            active_bindings: Arc::new(AtomicUsize::new(1)),
        }
    }
}

#[cfg(not(coverage))]
#[derive(Clone)]
struct PreviewFeed {
    shared: Arc<Mutex<SharedPreviewState>>,
    session_active: Arc<AtomicBool>,
    active_bindings: Arc<AtomicUsize>,
    running: Arc<AtomicBool>,
    worker: Arc<Mutex<Option<std::thread::JoinHandle<()>>>>,
    profile: PreviewProfile,
    disabled: bool,
}

#[cfg(not(coverage))]
struct SharedPreviewState {
    latest: Option<PreviewFrame>,
    recorders: Vec<std::sync::mpsc::SyncSender<PreviewFrameSnapshot>>,
    status: String,
    generation: u64,
    clear_picture: bool,
    last_logged_error: Option<String>,
    last_logged_status: Option<String>,
    telemetry: PreviewTelemetry,
}

#[cfg(not(coverage))]
impl SharedPreviewState {
    fn new() -> Self {
        Self {
            latest: None,
            recorders: Vec::new(),
            status: PREVIEW_IDLE_STATUS.to_string(),
            generation: 1,
            clear_picture: true,
            last_logged_error: None,
            last_logged_status: None,
            telemetry: PreviewTelemetry::default(),
        }
    }

    fn set_status(&mut self, status: impl Into<String>, clear_picture: bool) {
        let status = status.into();
        let changed = self.status != status || clear_picture;
        self.status = status.clone();
        if clear_picture {
            self.latest = None;
            self.clear_picture = true;
        }
        if !looks_like_preview_problem(&status) {
            self.last_logged_error = None;
        }
        if changed {
            self.generation = self.generation.saturating_add(1);
        }
    }

    fn push_frame(&mut self, frame: PreviewFrame) {
        self.telemetry.record_presented_frame();
        if !self.recorders.is_empty() {
            let snapshot = frame.snapshot();
            self.recorders.retain(|sender| {
                match sender.try_send(snapshot.clone()) {
                    Ok(()) | Err(std::sync::mpsc::TrySendError::Full(_)) => true,
                    Err(std::sync::mpsc::TrySendError::Disconnected(_)) => false,
                }
            });
        }
        self.latest = Some(frame);
        self.clear_picture = false;
        self.last_logged_error = None;
        if self.status != "Live" {
            self.status = "Live".to_string();
            self.generation = self.generation.saturating_add(1);
        }
    }
}

#[cfg(not(coverage))]
#[derive(Debug, Default)]
struct PreviewTelemetry {
    packet_times: VecDeque<Instant>,
    frame_times: VecDeque<Instant>,
    packet_intervals_ms: VecDeque<(Instant, f32)>,
    frame_intervals_ms: VecDeque<(Instant, f32)>,
    packet_losses: VecDeque<(Instant, u64)>,
    dropped_deltas: VecDeque<(Instant, u64)>,
    queue_depth_samples: VecDeque<(Instant, u32)>,
    last_packet_at: Option<Instant>,
    last_frame_at: Option<Instant>,
    last_seq: Option<u64>,
    last_dropped_total: Option<u64>,
    latest_server_fps: u32,
    latest_server_process_cpu_tenths: u32,
    latest_queue_depth: u32,
    latest_server_source_gap_peak_ms: u32,
    latest_server_send_gap_peak_ms: u32,
    latest_server_queue_peak: u32,
    latest_server_encoder_label: String,
    decoder_label: String,
    stream_caps_label: String,
    decoded_caps_label: String,
    rendered_caps_label: String,
}

#[cfg(not(coverage))]
impl PreviewTelemetry {
    #[allow(clippy::too_many_arguments)]
    fn record_packet(
        &mut self,
        seq: u64,
        server_fps: u32,
        dropped_total: u64,
        queue_depth: u32,
        server_source_gap_peak_ms: u32,
        server_send_gap_peak_ms: u32,
        server_queue_peak: u32,
        server_encoder_label: &str,
        server_process_cpu_tenths: u32,
    ) {
        self.record_packet_at(
            Instant::now(),
            seq,
            server_fps,
            dropped_total,
            queue_depth,
            server_source_gap_peak_ms,
            server_send_gap_peak_ms,
            server_queue_peak,
            server_encoder_label,
            server_process_cpu_tenths,
        );
    }

    #[allow(clippy::too_many_arguments)]
    fn record_packet_at(
        &mut self,
        now: Instant,
        seq: u64,
        server_fps: u32,
        dropped_total: u64,
        queue_depth: u32,
        server_source_gap_peak_ms: u32,
        server_send_gap_peak_ms: u32,
        server_queue_peak: u32,
        server_encoder_label: &str,
        server_process_cpu_tenths: u32,
    ) {
        self.trim(now);
        self.packet_times.push_back(now);
        if let Some(previous) = self.last_packet_at.replace(now) {
            self.packet_intervals_ms.push_back((
                now,
                now.saturating_duration_since(previous).as_secs_f32() * 1000.0,
            ));
        }
        if seq > 0 {
            if let Some(previous_seq) = self.last_seq
                && seq > previous_seq + 1
            {
                self.packet_losses
                    .push_back((now, seq.saturating_sub(previous_seq + 1)));
            }
            self.last_seq = Some(seq);
        }
        if let Some(previous_dropped) = self.last_dropped_total
            && dropped_total > previous_dropped
        {
            self.dropped_deltas
                .push_back((now, dropped_total.saturating_sub(previous_dropped)));
        }
        self.last_dropped_total = Some(dropped_total);
        self.latest_server_fps = server_fps.max(1);
        self.latest_server_process_cpu_tenths = server_process_cpu_tenths;
        self.latest_queue_depth = queue_depth;
        self.latest_server_source_gap_peak_ms = server_source_gap_peak_ms;
        self.latest_server_send_gap_peak_ms = server_send_gap_peak_ms;
        self.latest_server_queue_peak = server_queue_peak.max(queue_depth);
        if !server_encoder_label.is_empty() {
            self.latest_server_encoder_label = server_encoder_label.to_string();
        }
        self.queue_depth_samples.push_back((now, queue_depth));
        self.trim(now);
    }

    fn record_presented_frame(&mut self) {
        self.record_presented_frame_at(Instant::now());
    }

    fn record_presented_frame_at(&mut self, now: Instant) {
        self.trim(now);
        if let Some(previous) = self.last_frame_at.replace(now) {
            self.frame_intervals_ms.push_back((
                now,
                now.saturating_duration_since(previous).as_secs_f32() * 1000.0,
            ));
        }
        self.frame_times.push_back(now);
    }

    fn note_decoder(&mut self, decoder_label: &str) {
        if !decoder_label.is_empty() {
            self.decoder_label = decoder_label.to_string();
        }
    }

    fn note_stream_caps(&mut self, caps_label: &str) {
        if !caps_label.is_empty() {
            self.stream_caps_label = caps_label.to_string();
        }
    }

    fn note_decoded_caps(&mut self, caps_label: &str) {
        if !caps_label.is_empty() {
            self.decoded_caps_label = caps_label.to_string();
        }
    }

    fn note_rendered_caps(&mut self, caps_label: &str) {
        if !caps_label.is_empty() {
            self.rendered_caps_label = caps_label.to_string();
        }
    }

    fn snapshot(&mut self) -> PreviewMetricsSnapshot {
        self.snapshot_at(Instant::now())
    }

    fn snapshot_at(&mut self, now: Instant) -> PreviewMetricsSnapshot {
        self.trim(now);
        let receive_fps = events_per_second(&self.packet_times, now);
        let present_fps = events_per_second(&self.frame_times, now);
        let delivered = self.packet_times.len() as u64;
        let packet_losses: u64 = self.packet_losses.iter().map(|(_, loss)| *loss).sum();
        let packet_loss_pct = if delivered + packet_losses == 0 {
            0.0
        } else {
            packet_losses as f32 * 100.0 / (delivered + packet_losses) as f32
        };
        let dropped_frames: u64 = self
            .dropped_deltas
            .iter()
            .map(|(_, dropped)| *dropped)
            .sum();
        let queue_depth_peak = self
            .queue_depth_samples
            .iter()
            .map(|(_, depth)| *depth)
            .max()
            .unwrap_or(self.latest_queue_depth);
        PreviewMetricsSnapshot {
            receive_fps,
            present_fps,
            server_fps: self.latest_server_fps as f32,
            server_process_cpu_pct: self.latest_server_process_cpu_tenths as f32 / 10.0,
            stream_spread_ms: compute_jitter_ms(&self.packet_intervals_ms),
            packet_loss_pct,
            dropped_frames,
            queue_depth: self.latest_queue_depth,
            queue_depth_peak,
            packet_gap_peak_ms: compute_peak_gap_ms(&self.packet_intervals_ms),
            present_gap_peak_ms: compute_peak_gap_ms(&self.frame_intervals_ms),
            server_source_gap_peak_ms: self.latest_server_source_gap_peak_ms as f32,
            server_send_gap_peak_ms: self.latest_server_send_gap_peak_ms as f32,
            server_queue_peak: self.latest_server_queue_peak,
            server_encoder_label: self.latest_server_encoder_label.clone(),
            decoder_label: self.decoder_label.clone(),
            stream_caps_label: self.stream_caps_label.clone(),
            decoded_caps_label: self.decoded_caps_label.clone(),
            rendered_caps_label: self.rendered_caps_label.clone(),
        }
    }

    fn trim(&mut self, now: Instant) {
        trim_instant_queue(&mut self.packet_times, now);
        trim_instant_queue(&mut self.frame_times, now);
        trim_value_queue(&mut self.packet_intervals_ms, now);
        trim_value_queue(&mut self.frame_intervals_ms, now);
        trim_value_queue(&mut self.packet_losses, now);
        trim_value_queue(&mut self.dropped_deltas, now);
        trim_value_queue(&mut self.queue_depth_samples, now);
    }
}
