impl UpstreamMediaRuntime {
#[must_use]
/// Keeps `new` explicit because it sits on server upstream media scheduling, where timing choices directly affect lip sync.
/// Inputs are the typed parameters; output is the return value or side effect.
pub fn new() -> Self {
        Self {
            next_session_id: AtomicU64::new(0),
            next_camera_generation: AtomicU64::new(0),
            next_microphone_generation: AtomicU64::new(0),
            microphone_sink_gate: Arc::new(Semaphore::new(1)),
            camera_playout_offset_us: AtomicI64::new(playout_offset_us(UpstreamMediaKind::Camera)),
            microphone_playout_offset_us: AtomicI64::new(playout_offset_us(
                UpstreamMediaKind::Microphone,
            )),
            state: Mutex::new(RuntimeState::default()),
        }
    }

    pub fn set_playout_offsets(&self, camera_offset_us: i64, microphone_offset_us: i64) {
        self.camera_playout_offset_us
            .store(camera_offset_us, Ordering::Relaxed);
        self.microphone_playout_offset_us
            .store(microphone_offset_us, Ordering::Relaxed);
    }

    #[must_use]
    pub fn playout_offsets(&self) -> (i64, i64) {
        (
            self.camera_playout_offset_us.load(Ordering::Relaxed),
            self.microphone_playout_offset_us.load(Ordering::Relaxed),
        )
    }

    #[must_use]
    pub fn activate_camera(&self) -> UpstreamStreamLease {
        self.activate(UpstreamMediaKind::Camera)
    }

    #[must_use]
    pub fn activate_microphone(&self) -> UpstreamStreamLease {
        self.activate(UpstreamMediaKind::Microphone)
    }

    #[must_use]
    pub fn activate_bundled_session(&self) -> UpstreamBundledLeases {
        self.activate_bundled()
    }

    pub async fn reserve_microphone_sink(&self, generation: u64) -> Option<OwnedSemaphorePermit> {
        let permit = self
            .microphone_sink_gate
            .clone()
            .acquire_owned()
            .await
            .ok()?;
        self.is_microphone_active(generation).then_some(permit)
    }

    #[must_use]
    pub fn is_camera_active(&self, generation: u64) -> bool {
        self.is_active(UpstreamMediaKind::Camera, generation)
    }

    #[must_use]
    pub fn is_microphone_active(&self, generation: u64) -> bool {
        self.is_active(UpstreamMediaKind::Microphone, generation)
    }

    pub fn close_camera(&self, generation: u64) {
        self.close(UpstreamMediaKind::Camera, generation);
    }

    pub fn close_microphone(&self, generation: u64) {
        self.close(UpstreamMediaKind::Microphone, generation);
    }

    pub fn soft_recover_microphone(&self) {
        let lease = self.activate_microphone();
        self.close_microphone(lease.generation);
    }

    /// Keeps `record_client_timing` explicit because it sits on server upstream media scheduling, where timing choices directly affect lip sync.
    /// Inputs are the typed parameters; output is the return value or side effect.
    pub fn record_client_timing(&self, kind: UpstreamMediaKind, timing: UpstreamClientTiming) {
        let mut state = self
            .state
            .lock()
            .expect("upstream media state mutex poisoned");
        let sample = TimingSample {
            capture_pts_us: timing.capture_pts_us,
            send_pts_us: timing.send_pts_us,
            queue_age_ms: timing.queue_age_ms,
            received_at: Instant::now(),
        };
        match kind {
            UpstreamMediaKind::Camera => {
                state.latest_camera_timing = Some(sample);
                state.latest_camera_remote_pts_us = Some(timing.capture_pts_us);
                state
                    .camera_client_queue_age_window_ms
                    .push(f64::from(timing.queue_age_ms));
            }
            UpstreamMediaKind::Microphone => {
                state.latest_microphone_timing = Some(sample);
                state.latest_microphone_remote_pts_us = Some(timing.capture_pts_us);
                state
                    .microphone_client_queue_age_window_ms
                    .push(f64::from(timing.queue_age_ms));
            }
        }
        record_timing_pair(&mut state);
    }

    pub fn mark_audio_presented(&self, local_pts_us: u64, due_at: Instant) {
        let mut state = self
            .state
            .lock()
            .expect("upstream media state mutex poisoned");
        state.last_audio_presented_pts_us = Some(local_pts_us);
        record_presentation(&mut state, UpstreamMediaKind::Microphone, due_at);
        state.phase = UpstreamSyncPhase::Live;
        state.last_reason = "v2 audio handed to UAC".to_string();
    }

    pub fn mark_video_presented(&self, local_pts_us: u64, due_at: Instant) {
        let mut state = self
            .state
            .lock()
            .expect("upstream media state mutex poisoned");
        state.last_video_presented_pts_us = Some(local_pts_us);
        let now = Instant::now();
        state.latest_camera_live_lag_ms = state
            .latest_camera_timing
            .map(|sample| f64::from(sample.queue_age_ms) + age_ms(now, sample.received_at));
        record_presentation(&mut state, UpstreamMediaKind::Camera, due_at);
        state.phase = UpstreamSyncPhase::Live;
        state.last_reason = "v2 video handed to UVC".to_string();
    }

    pub fn mark_video_presented_with_live_lag(
        &self,
        local_pts_us: u64,
        due_at: Instant,
        live_lag_ms: f64,
    ) {
        let mut state = self
            .state
            .lock()
            .expect("upstream media state mutex poisoned");
        state.last_video_presented_pts_us = Some(local_pts_us);
        state.latest_camera_live_lag_ms = Some(live_lag_ms.max(0.0));
        record_presentation(&mut state, UpstreamMediaKind::Camera, due_at);
        state.phase = UpstreamSyncPhase::Live;
        state.last_reason = "v2 video handed to UVC".to_string();
    }

    pub fn record_video_freeze(&self, reason: impl Into<String>) {
        let mut state = self
            .state
            .lock()
            .expect("upstream media state mutex poisoned");
        state.video_freezes = state.video_freezes.saturating_add(1);
        state.phase = UpstreamSyncPhase::Healing;
        state.last_reason = reason.into();
    }
}
