// SPDX-License-Identifier: AGPL-3.0-or-later

pub mod frame_pool;
#[cfg(feature = "wgpu")]
pub mod gpu_loss;

use parking_lot::RwLock;
use std::collections::HashMap;
use std::ffi::c_void;
use std::sync::Arc;
use std::sync::OnceLock;

use crate::frame_pool::PooledFrame;

#[cfg(target_os = "linux")]
use std::os::fd::OwnedFd;

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DmabufDesc {
    pub plane_count: u8,
    pub width: u32,
    pub height: u32,
    pub drm_format: u32,
    pub modifier: u64,
    pub strides: [u32; 4],
    pub offsets: [u32; 4],
    pub device_uuid: [u8; 16],
    pub timestamp_us: i64,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SharedTextureDesc {
    pub handle: u64,
    pub width: u32,
    pub height: u32,
    pub dxgi_format: u32,
    pub timestamp_us: i64,
}

pub const FRAME_DATA_BYTES_CAP: usize = 1 << 30;

pub trait SharedFrameBytes: Send + Sync {
    fn bytes(&self) -> &[u8];
}

enum FrameDataRepr {
    Owned(Vec<u8>),
    Shared {
        source: Arc<dyn SharedFrameBytes>,
        len: usize,
        _capacity_token: Option<PooledFrame>,
    },
}

pub struct FrameData {
    repr: FrameDataRepr,
}

impl FrameData {
    pub fn from_owned(data: Vec<u8>) -> Self {
        assert!(data.len() <= FRAME_DATA_BYTES_CAP);
        Self {
            repr: FrameDataRepr::Owned(data),
        }
    }

    pub fn from_shared(
        source: Arc<dyn SharedFrameBytes>,
        len: usize,
        capacity_token: Option<PooledFrame>,
    ) -> Self {
        assert!(len <= FRAME_DATA_BYTES_CAP);
        assert!(len <= source.bytes().len());
        Self {
            repr: FrameDataRepr::Shared {
                source,
                len,
                _capacity_token: capacity_token,
            },
        }
    }

    pub fn as_slice(&self) -> &[u8] {
        match &self.repr {
            FrameDataRepr::Owned(data) => data.as_slice(),
            FrameDataRepr::Shared { source, len, .. } => {
                let bytes = source.bytes();
                assert!(*len <= bytes.len());
                &bytes[..*len]
            }
        }
    }

    pub fn len(&self) -> usize {
        let len = match &self.repr {
            FrameDataRepr::Owned(data) => data.len(),
            FrameDataRepr::Shared { len, .. } => *len,
        };
        assert!(len <= FRAME_DATA_BYTES_CAP);
        len
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn is_shared(&self) -> bool {
        matches!(self.repr, FrameDataRepr::Shared { .. })
    }

    pub fn into_vec(self) -> Vec<u8> {
        match self.repr {
            FrameDataRepr::Owned(data) => data,
            FrameDataRepr::Shared { source, len, .. } => {
                let bytes = source.bytes();
                assert!(len <= bytes.len());
                bytes[..len].to_vec()
            }
        }
    }
}

impl From<Vec<u8>> for FrameData {
    fn from(data: Vec<u8>) -> Self {
        Self::from_owned(data)
    }
}

impl std::ops::Deref for FrameData {
    type Target = [u8];

    fn deref(&self) -> &[u8] {
        self.as_slice()
    }
}

pub struct Nv12Frame {
    pub data: FrameData,
    pub width: u32,
    pub height: u32,
    pub stride_y: u32,
    pub stride_uv: u32,
    pub timestamp_us: i64,
}

pub struct BgraFrame {
    pub data: Vec<u8>,
    pub width: u32,
    pub height: u32,
    pub stride: u32,
    pub timestamp_us: i64,
}

#[cfg(target_os = "macos")]
pub struct MacCvPixelBufferFrame {
    pixel_buffer: *mut c_void,
    pub width: u32,
    pub height: u32,
    pub pixel_format: u32,
    pub timestamp_us: i64,
}

#[cfg(target_os = "macos")]
unsafe impl Send for MacCvPixelBufferFrame {}
#[cfg(target_os = "macos")]
unsafe impl Sync for MacCvPixelBufferFrame {}

#[cfg(target_os = "macos")]
impl MacCvPixelBufferFrame {
    pub unsafe fn from_retained(
        pixel_buffer: *mut c_void,
        width: u32,
        height: u32,
        pixel_format: u32,
        timestamp_us: i64,
    ) -> Self {
        Self {
            pixel_buffer,
            width,
            height,
            pixel_format,
            timestamp_us,
        }
    }

    pub fn pixel_buffer_ptr(&self) -> *mut c_void {
        self.pixel_buffer
    }

    pub fn into_raw_pixel_buffer(mut self) -> *mut c_void {
        let pb = self.pixel_buffer;
        self.pixel_buffer = std::ptr::null_mut();
        std::mem::forget(self);
        pb
    }
}

#[cfg(target_os = "macos")]
impl Drop for MacCvPixelBufferFrame {
    fn drop(&mut self) {
        if !self.pixel_buffer.is_null() {
            unsafe { CFRelease(self.pixel_buffer as *const c_void) };
            self.pixel_buffer = std::ptr::null_mut();
        }
    }
}

#[cfg(target_os = "macos")]
#[link(name = "CoreFoundation", kind = "framework")]
unsafe extern "C" {
    fn CFRelease(cf: *const c_void);
}

#[cfg(target_os = "linux")]
pub struct DmabufFrame {
    pub desc: DmabufDesc,
    pub fds: Vec<OwnedFd>,
}

pub enum ScreenFrame {
    Nv12(Nv12Frame),
    Bgra(BgraFrame),
    #[cfg(target_os = "macos")]
    MacCvPixelBuffer(MacCvPixelBufferFrame),
    #[cfg(target_os = "linux")]
    Dmabuf(DmabufFrame),
    #[cfg(target_os = "windows")]
    SharedTexture(SharedTextureDesc),
}

impl ScreenFrame {
    pub fn timestamp_us(&self) -> i64 {
        match self {
            Self::Nv12(f) => f.timestamp_us,
            Self::Bgra(f) => f.timestamp_us,
            #[cfg(target_os = "macos")]
            Self::MacCvPixelBuffer(f) => f.timestamp_us,
            #[cfg(target_os = "linux")]
            Self::Dmabuf(f) => f.desc.timestamp_us,
            #[cfg(target_os = "windows")]
            Self::SharedTexture(d) => d.timestamp_us,
        }
    }

    pub fn dimensions(&self) -> (u32, u32) {
        match self {
            Self::Nv12(f) => (f.width, f.height),
            Self::Bgra(f) => (f.width, f.height),
            #[cfg(target_os = "macos")]
            Self::MacCvPixelBuffer(f) => (f.width, f.height),
            #[cfg(target_os = "linux")]
            Self::Dmabuf(f) => (f.desc.width, f.desc.height),
            #[cfg(target_os = "windows")]
            Self::SharedTexture(d) => (d.width, d.height),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EnqueueOutcome {
    Accepted,
    Coalesced,
    Rejected,
}

pub trait ScreenFrameSink: Send + Sync {
    fn enqueue(&self, frame: ScreenFrame) -> EnqueueOutcome;
}

pub const NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC: u32 = u32::from_be_bytes(*b"FXSH");
pub const NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION: u32 = 2;

pub const NATIVE_SCREEN_FRAME_SINK_ACCEPTED: u32 = 1;
pub const NATIVE_SCREEN_FRAME_SINK_COALESCED: u32 = 2;
pub const NATIVE_SCREEN_FRAME_SINK_REJECTED: u32 = 3;

pub type NativeScreenFrameSinkRetainFn = unsafe extern "C" fn(context: *const c_void);
pub type NativeScreenFrameSinkReleaseFn = unsafe extern "C" fn(context: *const c_void);
pub type NativeScreenFrameSinkEnqueueNv12Fn = unsafe extern "C" fn(
    context: *const c_void,
    data: *const u8,
    data_len: usize,
    width: u32,
    height: u32,
    stride_y: u32,
    stride_uv: u32,
    timestamp_us: i64,
) -> u32;
pub type NativeScreenFrameSinkEnqueueBgraFn = unsafe extern "C" fn(
    context: *const c_void,
    data: *const u8,
    data_len: usize,
    width: u32,
    height: u32,
    stride: u32,
    timestamp_us: i64,
) -> u32;
pub type NativeScreenFrameSinkEnqueueMacCvPixelBufferFn = unsafe extern "C" fn(
    context: *const c_void,
    pixel_buffer: *mut c_void,
    width: u32,
    height: u32,
    pixel_format: u32,
    timestamp_us: i64,
) -> u32;
pub type NativeScreenFrameSinkEnqueueDmabufFn = unsafe extern "C" fn(
    context: *const c_void,
    desc: DmabufDesc,
    fds: *const i32,
    fd_count: usize,
) -> u32;
pub type NativeScreenFrameSinkEnqueueSharedTextureFn =
    unsafe extern "C" fn(context: *const c_void, desc: SharedTextureDesc) -> u32;
pub type NativeScreenFrameSinkEnqueueScreenAudioFn = unsafe extern "C" fn(
    context: *const c_void,
    samples: *const f32,
    num_frames: u32,
    channels: u32,
    sample_rate_hz: u32,
    timestamp_us: i64,
) -> u32;

#[repr(C)]
#[derive(Clone, Copy)]
pub struct NativeScreenFrameSinkHandle {
    pub magic: u32,
    pub version: u32,
    pub context: *const c_void,
    pub retain: NativeScreenFrameSinkRetainFn,
    pub release: NativeScreenFrameSinkReleaseFn,
    pub enqueue_nv12: Option<NativeScreenFrameSinkEnqueueNv12Fn>,
    pub enqueue_bgra: Option<NativeScreenFrameSinkEnqueueBgraFn>,
    pub enqueue_mac_cv_pixel_buffer: Option<NativeScreenFrameSinkEnqueueMacCvPixelBufferFn>,
    pub enqueue_dmabuf: Option<NativeScreenFrameSinkEnqueueDmabufFn>,
    pub enqueue_shared_texture: Option<NativeScreenFrameSinkEnqueueSharedTextureFn>,
    pub enqueue_screen_audio: Option<NativeScreenFrameSinkEnqueueScreenAudioFn>,
}

unsafe impl Send for NativeScreenFrameSinkHandle {}
unsafe impl Sync for NativeScreenFrameSinkHandle {}

impl NativeScreenFrameSinkHandle {
    pub fn is_valid(&self) -> bool {
        self.magic == NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC
            && self.version == NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION
            && !self.context.is_null()
    }

    pub unsafe fn retain_ref(&self) -> Option<NativeScreenFrameSinkHandleRef> {
        if !self.is_valid() {
            return None;
        }
        unsafe { (self.retain)(self.context) };
        Some(NativeScreenFrameSinkHandleRef { handle: *self })
    }

    pub unsafe fn retain_from_raw(raw: *const Self) -> Option<NativeScreenFrameSinkHandleRef> {
        if raw.is_null() {
            return None;
        }
        unsafe { (*raw).retain_ref() }
    }

    pub fn native_outcome(outcome: EnqueueOutcome) -> u32 {
        match outcome {
            EnqueueOutcome::Accepted => NATIVE_SCREEN_FRAME_SINK_ACCEPTED,
            EnqueueOutcome::Coalesced => NATIVE_SCREEN_FRAME_SINK_COALESCED,
            EnqueueOutcome::Rejected => NATIVE_SCREEN_FRAME_SINK_REJECTED,
        }
    }

    pub fn outcome_from_native(value: u32) -> EnqueueOutcome {
        match value {
            NATIVE_SCREEN_FRAME_SINK_ACCEPTED => EnqueueOutcome::Accepted,
            NATIVE_SCREEN_FRAME_SINK_COALESCED => EnqueueOutcome::Coalesced,
            _ => EnqueueOutcome::Rejected,
        }
    }
}

pub struct NativeScreenFrameSinkHandleRef {
    handle: NativeScreenFrameSinkHandle,
}

unsafe impl Send for NativeScreenFrameSinkHandleRef {}
unsafe impl Sync for NativeScreenFrameSinkHandleRef {}

impl NativeScreenFrameSinkHandleRef {
    pub fn handle(&self) -> &NativeScreenFrameSinkHandle {
        &self.handle
    }

    pub fn enqueue_nv12_copy(
        &self,
        data: &[u8],
        width: u32,
        height: u32,
        stride_y: u32,
        stride_uv: u32,
        timestamp_us: i64,
    ) -> EnqueueOutcome {
        let Some(enqueue) = self.handle.enqueue_nv12 else {
            return EnqueueOutcome::Rejected;
        };
        NativeScreenFrameSinkHandle::outcome_from_native(unsafe {
            enqueue(
                self.handle.context,
                data.as_ptr(),
                data.len(),
                width,
                height,
                stride_y,
                stride_uv,
                timestamp_us,
            )
        })
    }

    pub fn enqueue_bgra_copy(
        &self,
        data: &[u8],
        width: u32,
        height: u32,
        stride: u32,
        timestamp_us: i64,
    ) -> EnqueueOutcome {
        let Some(enqueue) = self.handle.enqueue_bgra else {
            return EnqueueOutcome::Rejected;
        };
        NativeScreenFrameSinkHandle::outcome_from_native(unsafe {
            enqueue(
                self.handle.context,
                data.as_ptr(),
                data.len(),
                width,
                height,
                stride,
                timestamp_us,
            )
        })
    }

    #[cfg(target_os = "macos")]
    pub unsafe fn enqueue_mac_cv_pixel_buffer(
        &self,
        pixel_buffer: *mut c_void,
        width: u32,
        height: u32,
        pixel_format: u32,
        timestamp_us: i64,
    ) -> EnqueueOutcome {
        let Some(enqueue) = self.handle.enqueue_mac_cv_pixel_buffer else {
            return EnqueueOutcome::Rejected;
        };
        NativeScreenFrameSinkHandle::outcome_from_native(unsafe {
            enqueue(
                self.handle.context,
                pixel_buffer,
                width,
                height,
                pixel_format,
                timestamp_us,
            )
        })
    }

    #[cfg(target_os = "linux")]
    pub unsafe fn enqueue_dmabuf_take_fds(&self, desc: DmabufDesc, fds: &[i32]) -> EnqueueOutcome {
        let Some(enqueue) = self.handle.enqueue_dmabuf else {
            return EnqueueOutcome::Rejected;
        };
        NativeScreenFrameSinkHandle::outcome_from_native(unsafe {
            enqueue(self.handle.context, desc, fds.as_ptr(), fds.len())
        })
    }

    #[cfg(target_os = "windows")]
    pub fn enqueue_shared_texture(&self, desc: SharedTextureDesc) -> EnqueueOutcome {
        let Some(enqueue) = self.handle.enqueue_shared_texture else {
            return EnqueueOutcome::Rejected;
        };
        NativeScreenFrameSinkHandle::outcome_from_native(unsafe {
            enqueue(self.handle.context, desc)
        })
    }

    pub fn supports_screen_audio(&self) -> bool {
        self.handle.enqueue_screen_audio.is_some()
    }

    pub fn enqueue_screen_audio_f32(
        &self,
        samples: &[f32],
        num_frames: u32,
        channels: u32,
        sample_rate_hz: u32,
        timestamp_us: i64,
    ) -> EnqueueOutcome {
        let Some(enqueue) = self.handle.enqueue_screen_audio else {
            return EnqueueOutcome::Rejected;
        };
        if channels == 0 || num_frames == 0 {
            return EnqueueOutcome::Rejected;
        }
        let expected_samples = (num_frames as usize).checked_mul(channels as usize);
        if expected_samples != Some(samples.len()) {
            return EnqueueOutcome::Rejected;
        }
        NativeScreenFrameSinkHandle::outcome_from_native(unsafe {
            enqueue(
                self.handle.context,
                samples.as_ptr(),
                num_frames,
                channels,
                sample_rate_hz,
                timestamp_us,
            )
        })
    }
}

impl Drop for NativeScreenFrameSinkHandleRef {
    fn drop(&mut self) {
        if self.handle.is_valid() {
            unsafe { (self.handle.release)(self.handle.context) };
        }
    }
}

pub const STAGING_PAIR_LEN: usize = 2;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StagingSlotState {
    Empty,
    Submitted,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StagingError {
    WouldOverwrite,
}

pub trait StagingBackend {
    fn write<F: FnOnce(&mut [u8])>(&mut self, fill: F);
    fn read<R, F: FnOnce(&[u8]) -> R>(&self, read: F) -> R;
    fn is_ready(&self) -> bool;
    fn is_idle(&self) -> bool;
}

pub struct CpuStagingBackend {
    buffer: Vec<u8>,
}

impl CpuStagingBackend {
    pub fn new(byte_len: usize) -> Self {
        assert!(byte_len > 0, "staging backend length must be positive");
        assert!(
            byte_len <= 1 << 30,
            "staging backend length exceeds sanity cap"
        );
        Self {
            buffer: vec![0u8; byte_len],
        }
    }

    pub fn len(&self) -> usize {
        self.buffer.len()
    }

    pub fn is_empty(&self) -> bool {
        self.buffer.is_empty()
    }
}

impl StagingBackend for CpuStagingBackend {
    fn write<F: FnOnce(&mut [u8])>(&mut self, fill: F) {
        fill(&mut self.buffer);
    }

    fn read<R, F: FnOnce(&[u8]) -> R>(&self, read: F) -> R {
        read(&self.buffer)
    }

    fn is_ready(&self) -> bool {
        true
    }

    fn is_idle(&self) -> bool {
        true
    }
}

pub struct StagingSurfacePair<B: StagingBackend> {
    backends: [B; STAGING_PAIR_LEN],
    slot_states: [StagingSlotState; STAGING_PAIR_LEN],
    next_submit: u64,
    next_map: u64,
    skipped_count: u64,
    overwrite_dropped_count: u64,
}

impl<B: StagingBackend> StagingSurfacePair<B> {
    pub fn new(backends: [B; STAGING_PAIR_LEN]) -> Self {
        assert_eq!(
            STAGING_PAIR_LEN, 2,
            "OBS staging pair is exactly two surfaces"
        );
        assert!(
            backends[0].is_idle(),
            "backend zero must be idle at construction"
        );
        assert!(
            backends[1].is_idle(),
            "backend one must be idle at construction"
        );
        Self {
            backends,
            slot_states: [StagingSlotState::Empty; STAGING_PAIR_LEN],
            next_submit: 0,
            next_map: 0,
            skipped_count: 0,
            overwrite_dropped_count: 0,
        }
    }

    pub fn next_submit_sequence(&self) -> u64 {
        self.next_submit
    }

    pub fn next_map_sequence(&self) -> u64 {
        self.next_map
    }

    pub fn skipped_count(&self) -> u64 {
        self.skipped_count
    }

    pub fn overwrite_dropped_count(&self) -> u64 {
        self.overwrite_dropped_count
    }

    pub fn submit<F: FnOnce(&mut [u8])>(
        &mut self,
        sequence: u64,
        fill: F,
    ) -> Result<(), StagingError> {
        assert_eq!(sequence, self.next_submit, "submit must use next sequence");
        assert!(
            self.next_map <= self.next_submit,
            "map sequence must trail submit"
        );
        let slot = (sequence & 1) as usize;
        assert!(slot < STAGING_PAIR_LEN, "slot index within pair bounds");
        if self.slot_states[slot] != StagingSlotState::Empty {
            self.overwrite_dropped_count = self.overwrite_dropped_count.wrapping_add(1);
            return Err(StagingError::WouldOverwrite);
        }
        assert!(
            self.backends[slot].is_idle(),
            "backend reports busy on empty slot"
        );
        self.backends[slot].write(fill);
        self.slot_states[slot] = StagingSlotState::Submitted;
        self.next_submit = self.next_submit.wrapping_add(1);
        Ok(())
    }

    pub fn try_map<R, F: FnOnce(&[u8]) -> R>(&mut self, sequence: u64, read: F) -> Option<R> {
        assert!(
            self.next_map < self.next_submit,
            "cannot map an unsubmitted sequence"
        );
        assert_eq!(
            sequence, self.next_map,
            "try_map must use next pending sequence"
        );
        let slot = (sequence & 1) as usize;
        assert!(slot < STAGING_PAIR_LEN, "slot index within pair bounds");
        assert_eq!(
            self.slot_states[slot],
            StagingSlotState::Submitted,
            "double map of unsubmitted slot"
        );
        if !self.backends[slot].is_ready() {
            self.skipped_count = self.skipped_count.wrapping_add(1);
            self.slot_states[slot] = StagingSlotState::Empty;
            self.next_map = self.next_map.wrapping_add(1);
            return None;
        }
        let value = self.backends[slot].read(read);
        self.slot_states[slot] = StagingSlotState::Empty;
        self.next_map = self.next_map.wrapping_add(1);
        Some(value)
    }

    pub fn backend(&self, slot: usize) -> &B {
        assert!(slot < STAGING_PAIR_LEN, "slot index within pair bounds");
        assert!(
            slot < self.backends.len(),
            "slot index within backend storage"
        );
        &self.backends[slot]
    }
}

type SinkMap = HashMap<String, Arc<dyn ScreenFrameSink>>;
static REGISTRY: OnceLock<RwLock<SinkMap>> = OnceLock::new();

fn registry() -> &'static RwLock<SinkMap> {
    REGISTRY.get_or_init(|| RwLock::new(HashMap::new()))
}

pub fn register_sink(capture_id: impl Into<String>, sink: Arc<dyn ScreenFrameSink>) {
    registry().write().insert(capture_id.into(), sink);
}

pub fn unregister_sink(capture_id: &str) -> Option<Arc<dyn ScreenFrameSink>> {
    registry().write().remove(capture_id)
}

pub fn get_sink(capture_id: &str) -> Option<Arc<dyn ScreenFrameSink>> {
    registry().read().get(capture_id).cloned()
}

pub fn clear_all_sinks() {
    registry().write().clear();
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::atomic::{AtomicU32, Ordering};

    struct CountingSink {
        accepted: AtomicU32,
    }

    impl ScreenFrameSink for CountingSink {
        fn enqueue(&self, _frame: ScreenFrame) -> EnqueueOutcome {
            self.accepted.fetch_add(1, Ordering::Relaxed);
            EnqueueOutcome::Accepted
        }
    }

    fn fresh_id(label: &str) -> String {
        format!("test:{}:{}", label, std::process::id())
    }

    fn solid_nv12_frame(width: u32, height: u32) -> ScreenFrame {
        let y = (width * height) as usize;
        let uv = (width * (height / 2)) as usize;
        let mut data = vec![0u8; y + uv];
        data[..y].fill(16);
        data[y..].fill(128);
        ScreenFrame::Nv12(Nv12Frame {
            data: data.into(),
            width,
            height,
            stride_y: width,
            stride_uv: width,
            timestamp_us: 0,
        })
    }

    #[test]
    fn sink_registration_round_trips_through_registry() {
        let id = fresh_id("round-trip");
        let sink = Arc::new(CountingSink {
            accepted: AtomicU32::new(0),
        });
        register_sink(id.clone(), sink.clone());

        let resolved = get_sink(&id).expect("sink must be resolvable");
        assert_eq!(
            resolved.enqueue(solid_nv12_frame(64, 64)),
            EnqueueOutcome::Accepted,
        );
        assert_eq!(sink.accepted.load(Ordering::Relaxed), 1);

        let removed = unregister_sink(&id).expect("sink must be removable");
        assert_eq!(
            removed.enqueue(solid_nv12_frame(64, 64)),
            EnqueueOutcome::Accepted,
        );
        assert!(get_sink(&id).is_none());
        assert!(unregister_sink(&id).is_none());
    }

    #[test]
    fn screen_frame_exposes_timestamp_and_dimensions() {
        let frame = solid_nv12_frame(640, 480);
        assert_eq!(frame.dimensions(), (640, 480));
        assert_eq!(frame.timestamp_us(), 0);
    }

    struct RejectingSink;
    impl ScreenFrameSink for RejectingSink {
        fn enqueue(&self, _frame: ScreenFrame) -> EnqueueOutcome {
            EnqueueOutcome::Rejected
        }
    }

    #[test]
    fn concurrent_producers_all_deliver_to_a_single_sink() {
        let id = fresh_id("concurrent-producers");
        let sink = Arc::new(CountingSink {
            accepted: AtomicU32::new(0),
        });
        register_sink(id.clone(), sink.clone());

        let producers = 8;
        let frames_per_producer = 64;
        let handles: Vec<_> = (0..producers)
            .map(|p| {
                let id = id.clone();
                std::thread::spawn(move || {
                    for f in 0..frames_per_producer {
                        let resolved = get_sink(&id).expect("sink missing during producer race");
                        assert_eq!(
                            resolved.enqueue(solid_nv12_frame(32, 32)),
                            EnqueueOutcome::Accepted,
                            "producer {} frame {}",
                            p,
                            f
                        );
                    }
                })
            })
            .collect();
        for h in handles {
            h.join().expect("producer panicked");
        }
        unregister_sink(&id);
        assert_eq!(
            sink.accepted.load(Ordering::Relaxed),
            producers * frames_per_producer
        );
    }

    #[test]
    fn sinks_for_distinct_ids_do_not_cross_route() {
        let id_a = fresh_id("isolation-a");
        let id_b = fresh_id("isolation-b");
        let sink_a = Arc::new(CountingSink {
            accepted: AtomicU32::new(0),
        });
        let sink_b: Arc<dyn ScreenFrameSink> = Arc::new(RejectingSink);
        register_sink(id_a.clone(), sink_a.clone());
        register_sink(id_b.clone(), sink_b.clone());

        assert_eq!(
            get_sink(&id_a).unwrap().enqueue(solid_nv12_frame(8, 8)),
            EnqueueOutcome::Accepted
        );
        assert_eq!(
            get_sink(&id_b).unwrap().enqueue(solid_nv12_frame(8, 8)),
            EnqueueOutcome::Rejected
        );
        assert_eq!(sink_a.accepted.load(Ordering::Relaxed), 1);

        unregister_sink(&id_a);
        unregister_sink(&id_b);
    }

    #[test]
    fn unregister_returns_the_active_arc_and_inflight_holders_keep_working() {
        let id = fresh_id("unregister-race");
        let sink = Arc::new(CountingSink {
            accepted: AtomicU32::new(0),
        });
        register_sink(id.clone(), sink.clone());

        let inflight = get_sink(&id).expect("sink available");
        let removed = unregister_sink(&id).expect("unregister returns sink");
        assert!(Arc::ptr_eq(&inflight, &removed));
        assert!(get_sink(&id).is_none());

        assert_eq!(
            inflight.enqueue(solid_nv12_frame(8, 8)),
            EnqueueOutcome::Accepted
        );
        assert_eq!(sink.accepted.load(Ordering::Relaxed), 1);
    }

    #[test]
    fn bgra_frame_exposes_timestamp_and_dimensions() {
        let frame = ScreenFrame::Bgra(BgraFrame {
            data: vec![0; 4 * 16 * 16],
            width: 16,
            height: 16,
            stride: 64,
            timestamp_us: 9_999,
        });
        assert_eq!(frame.dimensions(), (16, 16));
        assert_eq!(frame.timestamp_us(), 9_999);
    }

    struct StaticSharedBytes {
        bytes: Box<[u8]>,
    }

    impl SharedFrameBytes for StaticSharedBytes {
        fn bytes(&self) -> &[u8] {
            &self.bytes
        }
    }

    #[test]
    fn frame_data_owned_round_trips_without_sharing() {
        let data = FrameData::from(vec![1u8, 2, 3]);
        assert!(!data.is_shared());
        assert!(!data.is_empty());
        assert_eq!(data.len(), 3);
        assert_eq!(data.as_slice(), &[1, 2, 3]);
        assert_eq!(data.into_vec(), vec![1, 2, 3]);
    }

    #[test]
    fn frame_data_shared_round_trips_prefix_of_source_bytes() {
        let source = Arc::new(StaticSharedBytes {
            bytes: vec![7u8; 16].into_boxed_slice(),
        });
        let data = FrameData::from_shared(source.clone(), 12, None);
        assert!(data.is_shared());
        assert_eq!(data.len(), 12);
        assert_eq!(data.as_slice(), &[7u8; 12]);
        assert_eq!(Arc::strong_count(&source), 2);
        assert_eq!(data.into_vec(), vec![7u8; 12]);
        assert_eq!(Arc::strong_count(&source), 1);
    }

    #[test]
    #[should_panic]
    fn frame_data_shared_rejects_len_beyond_source_bytes() {
        let source = Arc::new(StaticSharedBytes {
            bytes: vec![0u8; 4].into_boxed_slice(),
        });
        let _ = FrameData::from_shared(source, 5, None);
    }

    #[test]
    fn frame_data_shared_capacity_token_returns_slot_on_drop_with_counters_intact() {
        let pool = frame_pool::CpuFrameBuilder::build_pool_with_capacity(2, 4).expect("pool init");
        let source = Arc::new(StaticSharedBytes {
            bytes: vec![9u8; 8].into_boxed_slice(),
        });
        let token = pool.try_acquire().expect("first slot");
        let data = FrameData::from_shared(source, 8, Some(token));
        assert_eq!(pool.currently_in_flight(), 1);

        let second = pool.try_acquire().expect("second slot");
        assert!(pool.try_acquire().is_none());
        assert_eq!(pool.skipped_total(), 1);
        assert_eq!(pool.acquired_total(), 2);

        drop(data);
        assert_eq!(pool.currently_in_flight(), 1);
        let third = pool.try_acquire().expect("slot returned by frame drop");
        assert_eq!(pool.acquired_total(), 3);
        assert_eq!(pool.skipped_total(), 1);

        drop(second);
        drop(third);
        assert_eq!(pool.currently_in_flight(), 0);
    }

    #[test]
    fn nv12_frame_with_shared_payload_exposes_timestamp_and_dimensions() {
        let source = Arc::new(StaticSharedBytes {
            bytes: vec![0u8; 64 * 96].into_boxed_slice(),
        });
        let frame = ScreenFrame::Nv12(Nv12Frame {
            data: FrameData::from_shared(source, 64 * 96, None),
            width: 64,
            height: 64,
            stride_y: 64,
            stride_uv: 64,
            timestamp_us: 77,
        });
        assert_eq!(frame.dimensions(), (64, 64));
        assert_eq!(frame.timestamp_us(), 77);
    }

    #[cfg(target_os = "macos")]
    #[test]
    fn mac_cv_pixel_buffer_with_null_pointer_drops_without_calling_release() {
        let frame =
            unsafe { MacCvPixelBufferFrame::from_retained(std::ptr::null_mut(), 8, 8, 0, 0) };
        assert_eq!(frame.pixel_buffer_ptr(), std::ptr::null_mut());
        drop(frame);
    }

    const STAGING_TEST_LEN: usize = 64;

    fn fresh_staging_pair() -> StagingSurfacePair<CpuStagingBackend> {
        let backends = [
            CpuStagingBackend::new(STAGING_TEST_LEN),
            CpuStagingBackend::new(STAGING_TEST_LEN),
        ];
        assert_eq!(backends[0].len(), STAGING_TEST_LEN);
        assert_eq!(backends[1].len(), STAGING_TEST_LEN);
        StagingSurfacePair::new(backends)
    }

    #[test]
    fn submit_then_map_returns_the_data_from_two_frames_ago() {
        let mut pair = fresh_staging_pair();
        assert!(pair.submit(0, |buf| buf.fill(0xA1)).is_ok());
        assert!(pair.submit(1, |buf| buf.fill(0xB2)).is_ok());

        let first = pair
            .try_map(0, |buf| buf[0])
            .expect("first map ready under CPU backend");
        assert_eq!(first, 0xA1);

        assert!(pair.submit(2, |buf| buf.fill(0xC3)).is_ok());
        let second = pair
            .try_map(1, |buf| buf[0])
            .expect("second map ready under CPU backend");
        assert_eq!(second, 0xB2);

        let third = pair
            .try_map(2, |buf| buf[0])
            .expect("third map ready under CPU backend");
        assert_eq!(third, 0xC3);
    }

    #[test]
    fn two_submits_in_a_row_preserve_distinct_slots() {
        let mut pair = fresh_staging_pair();
        assert!(pair.submit(0, |buf| buf.fill(0x11)).is_ok());
        assert!(pair.submit(1, |buf| buf.fill(0x22)).is_ok());
        assert_eq!(pair.next_submit_sequence(), 2);

        let first = pair
            .try_map(0, |buf| buf[0])
            .expect("slot zero still holds first submission");
        assert_eq!(first, 0x11);
        let second = pair
            .try_map(1, |buf| buf[0])
            .expect("slot one still holds second submission");
        assert_eq!(second, 0x22);
    }

    #[test]
    fn third_unmapped_submit_returns_would_overwrite_and_drops_newest() {
        let mut pair = fresh_staging_pair();
        assert!(pair.submit(0, |buf| buf.fill(0x33)).is_ok());
        assert!(pair.submit(1, |buf| buf.fill(0x44)).is_ok());
        assert_eq!(pair.overwrite_dropped_count(), 0);

        let outcome = pair.submit(2, |buf| buf.fill(0xFF));
        assert_eq!(outcome, Err(StagingError::WouldOverwrite));
        assert_eq!(pair.overwrite_dropped_count(), 1);

        let preserved = pair
            .try_map(0, |buf| buf[0])
            .expect("slot zero kept its original submission");
        assert_eq!(preserved, 0x33);
    }

    struct GatedBackend {
        buffer: Vec<u8>,
        ready: bool,
    }

    impl GatedBackend {
        fn new(len: usize) -> Self {
            assert!(len > 0, "gated backend len positive");
            assert!(len <= 1 << 16, "gated backend len within sanity cap");
            Self {
                buffer: vec![0u8; len],
                ready: true,
            }
        }
    }

    impl StagingBackend for GatedBackend {
        fn write<F: FnOnce(&mut [u8])>(&mut self, fill: F) {
            fill(&mut self.buffer);
        }

        fn read<R, F: FnOnce(&[u8]) -> R>(&self, read: F) -> R {
            read(&self.buffer)
        }

        fn is_ready(&self) -> bool {
            self.ready
        }

        fn is_idle(&self) -> bool {
            true
        }
    }

    #[test]
    fn try_map_returning_none_advances_and_bumps_skipped_counter() {
        let mut a = GatedBackend::new(STAGING_TEST_LEN);
        let mut b = GatedBackend::new(STAGING_TEST_LEN);
        a.ready = false;
        b.ready = true;
        let mut pair = StagingSurfacePair::new([a, b]);

        assert!(pair.submit(0, |buf| buf.fill(0x55)).is_ok());
        assert!(pair.submit(1, |buf| buf.fill(0x66)).is_ok());

        let skipped = pair.try_map(0, |buf| buf[0]);
        assert!(skipped.is_none());
        assert_eq!(pair.skipped_count(), 1);
        assert_eq!(pair.next_map_sequence(), 1);

        let mapped = pair.try_map(1, |buf| buf[0]).expect("slot one ready");
        assert_eq!(mapped, 0x66);
        assert_eq!(pair.next_map_sequence(), 2);

        assert!(pair.submit(2, |buf| buf.fill(0x77)).is_ok());
        assert!(pair.submit(3, |buf| buf.fill(0x88)).is_ok());
        let after_skip = pair.try_map(2, |buf| buf[0]);
        assert!(after_skip.is_none());
        assert_eq!(pair.skipped_count(), 2);
    }

    #[test]
    #[should_panic(expected = "submit must use next sequence")]
    fn submit_with_out_of_order_sequence_panics() {
        let mut pair = fresh_staging_pair();
        let _ = pair.submit(7, |buf| buf.fill(0));
    }

    #[test]
    #[should_panic(expected = "try_map must use next pending sequence")]
    fn try_map_with_wrong_sequence_panics() {
        let mut pair = fresh_staging_pair();
        assert!(pair.submit(0, |buf| buf.fill(0)).is_ok());
        let _ = pair.try_map(5, |buf| buf[0]);
    }

    #[test]
    #[should_panic(expected = "cannot map an unsubmitted sequence")]
    fn try_map_without_any_submit_panics() {
        let mut pair = fresh_staging_pair();
        let _ = pair.try_map(0, |buf| buf[0]);
    }

    #[test]
    #[should_panic(expected = "try_map must use next pending sequence")]
    fn try_map_twice_on_the_same_sequence_panics() {
        let mut pair = fresh_staging_pair();
        assert!(pair.submit(0, |buf| buf.fill(0xAB)).is_ok());
        assert!(pair.submit(1, |buf| buf.fill(0xCD)).is_ok());
        let first = pair.try_map(0, |buf| buf[0]).expect("first map");
        assert_eq!(first, 0xAB);
        let _ = pair.try_map(0, |buf| buf[0]);
    }

    static SCREEN_AUDIO_ENQUEUE_CALLS: AtomicU32 = AtomicU32::new(0);

    unsafe extern "C" fn screen_audio_test_retain(_context: *const c_void) {}
    unsafe extern "C" fn screen_audio_test_release(_context: *const c_void) {}
    unsafe extern "C" fn screen_audio_test_enqueue(
        _context: *const c_void,
        _samples: *const f32,
        _num_frames: u32,
        _channels: u32,
        _sample_rate_hz: u32,
        _timestamp_us: i64,
    ) -> u32 {
        SCREEN_AUDIO_ENQUEUE_CALLS.fetch_add(1, Ordering::Relaxed);
        NATIVE_SCREEN_FRAME_SINK_ACCEPTED
    }

    fn screen_audio_test_handle_ref() -> NativeScreenFrameSinkHandleRef {
        let handle = NativeScreenFrameSinkHandle {
            magic: NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC,
            version: NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION,
            context: (&SCREEN_AUDIO_ENQUEUE_CALLS as *const AtomicU32).cast::<c_void>(),
            retain: screen_audio_test_retain,
            release: screen_audio_test_release,
            enqueue_nv12: None,
            enqueue_bgra: None,
            enqueue_mac_cv_pixel_buffer: None,
            enqueue_dmabuf: None,
            enqueue_shared_texture: None,
            enqueue_screen_audio: Some(screen_audio_test_enqueue),
        };
        NativeScreenFrameSinkHandleRef { handle }
    }

    #[test]
    fn enqueue_screen_audio_rejects_malformed_inputs_without_panicking() {
        SCREEN_AUDIO_ENQUEUE_CALLS.store(0, Ordering::Relaxed);
        let sink = screen_audio_test_handle_ref();

        assert_eq!(
            sink.enqueue_screen_audio_f32(&[0.0, 0.0], 1, 0, 48_000, 0),
            EnqueueOutcome::Rejected
        );
        assert_eq!(
            sink.enqueue_screen_audio_f32(&[], 0, 2, 48_000, 0),
            EnqueueOutcome::Rejected
        );
        assert_eq!(
            sink.enqueue_screen_audio_f32(&[0.0, 0.0, 0.0], 1, 2, 48_000, 0),
            EnqueueOutcome::Rejected
        );
        assert_eq!(SCREEN_AUDIO_ENQUEUE_CALLS.load(Ordering::Relaxed), 0);
    }

    #[test]
    fn enqueue_screen_audio_accepts_whole_stereo_frames() {
        SCREEN_AUDIO_ENQUEUE_CALLS.store(0, Ordering::Relaxed);
        let sink = screen_audio_test_handle_ref();

        assert_eq!(
            sink.enqueue_screen_audio_f32(&[0.1, 0.2, 0.3, 0.4], 2, 2, 48_000, 7),
            EnqueueOutcome::Accepted
        );
        assert_eq!(SCREEN_AUDIO_ENQUEUE_CALLS.load(Ordering::Relaxed), 1);
    }
}
