//! Frame and Input Codec — encode/decode complete messages.
//!
//! A message = header (16 bytes) + payload (variable length).

use crate::protocol::*;

// ─── Stream Parser ───

/// Accumulates bytes from a transport and yields complete messages.
///
/// Handles partial reads: feed arbitrary chunks in, get complete
/// `(FrameHeader, Vec<u8>)` tuples out.
///
/// ```ignore
/// let mut parser = StreamParser::new();
/// parser.feed(&data_from_websocket);
/// while let Some((header, payload)) = parser.next_message() {
///     // process message
/// }
/// ```
pub struct StreamParser {
    buf: Vec<u8>,
}

impl StreamParser {
    pub fn new() -> Self {
        Self { buf: Vec::with_capacity(4096) }
    }

    /// Feed raw bytes into the parser.
    pub fn feed(&mut self, data: &[u8]) {
        self.buf.extend_from_slice(data);
    }

    /// Try to extract the next complete message.
    /// Returns `Some((header, payload))` if a complete message is available.
    pub fn next_message(&mut self) -> Option<(FrameHeader, Vec<u8>)> {
        if self.buf.len() < HEADER_SIZE {
            return None;
        }
        let header = FrameHeader::decode(&self.buf)?;
        let total = HEADER_SIZE + header.length as usize;
        if self.buf.len() < total {
            return None;
        }
        let payload = self.buf[HEADER_SIZE..total].to_vec();
        self.buf.drain(..total);
        Some((header, payload))
    }

    /// Number of bytes currently buffered.
    pub fn buffered(&self) -> usize {
        self.buf.len()
    }

    /// Clear the internal buffer.
    pub fn clear(&mut self) {
        self.buf.clear();
    }
}

impl Default for StreamParser {
    fn default() -> Self {
        Self::new()
    }
}

// ─── Frame Encoder ───

/// Encode a complete frame message: header + payload.
pub fn encode_frame(
    frame_type: FrameType,
    seq: u16,
    timestamp: u32,
    width: u16,
    height: u16,
    flags: u8,
    payload: &[u8],
) -> Vec<u8> {
    let header = FrameHeader {
        frame_type: frame_type as u8,
        flags,
        seq,
        timestamp,
        width,
        height,
        length: payload.len() as u32,
    };
    let mut buf = Vec::with_capacity(HEADER_SIZE + payload.len());
    buf.extend_from_slice(&header.encode());
    buf.extend_from_slice(payload);
    buf
}

/// Encode an input event message: header + payload, with FLAG_INPUT set.
pub fn encode_input(
    input_type: InputType,
    seq: u16,
    timestamp: u32,
    payload: &[u8],
) -> Vec<u8> {
    let header = FrameHeader {
        frame_type: input_type as u8,
        flags: FLAG_INPUT,
        seq,
        timestamp,
        width: 0,
        height: 0,
        length: payload.len() as u32,
    };
    let mut buf = Vec::with_capacity(HEADER_SIZE + payload.len());
    buf.extend_from_slice(&header.encode());
    buf.extend_from_slice(payload);
    buf
}

// ─── Frame Decoder ───

/// Decoded message: header + payload reference.
#[derive(Debug)]
pub struct DecodedMessage<'a> {
    pub header: FrameHeader,
    pub payload: &'a [u8],
}

/// Decode a complete message from bytes.
/// Returns None if the buffer is too short for the header or payload.
pub fn decode_message(buf: &[u8]) -> Option<DecodedMessage<'_>> {
    let header = FrameHeader::decode(buf)?;
    let payload_start = HEADER_SIZE;
    let payload_end = payload_start + header.length as usize;
    if buf.len() < payload_end {
        return None;
    }
    Some(DecodedMessage {
        header,
        payload: &buf[payload_start..payload_end],
    })
}

/// Total message size from a header.
pub fn message_size(header: &FrameHeader) -> usize {
    HEADER_SIZE + header.length as usize
}

// ─── Delta Compression ───

/// Compute XOR delta between two frames.
/// Returns a delta buffer where unchanged pixels are zero bytes.
pub fn compute_delta(current: &[u8], previous: &[u8]) -> Vec<u8> {
    assert_eq!(current.len(), previous.len(), "frames must be same size");
    current.iter().zip(previous.iter()).map(|(c, p)| c ^ p).collect()
}

/// Apply XOR delta to reconstruct current frame from previous + delta.
pub fn apply_delta(previous: &[u8], delta: &[u8]) -> Vec<u8> {
    assert_eq!(previous.len(), delta.len(), "frames must be same size");
    previous.iter().zip(delta.iter()).map(|(p, d)| p ^ d).collect()
}

/// Check if a delta frame is worth sending (vs sending a keyframe).
/// Returns true if the delta is significantly smaller due to zero runs.
pub fn delta_is_worthwhile(delta: &[u8]) -> bool {
    let zero_count = delta.iter().filter(|&&b| b == 0).count();
    zero_count > delta.len() * 3 / 10
}

// ─── RLE Compression for Delta Frames ───

/// RLE-encode a delta buffer for transmission.
///
/// Encoding scheme:
/// - Non-zero byte `b`: emit literally
/// - Run of N zero bytes: emit `0x00` followed by 2-byte LE count
///
/// Optimized for XOR delta frames where most bytes are 0x00 (unchanged pixels).
/// On typical UI frames with 70-95% unchanged content, achieves 5-20x compression.
pub fn rle_encode(data: &[u8]) -> Vec<u8> {
    let mut out = Vec::with_capacity(data.len() / 2);
    let mut i = 0;
    while i < data.len() {
        if data[i] == 0 {
            let start = i;
            while i < data.len() && data[i] == 0 {
                i += 1;
            }
            let count = i - start;
            let mut remaining = count;
            while remaining > 0 {
                let chunk = remaining.min(65535);
                out.push(0x00);
                out.push((chunk & 0xFF) as u8);
                out.push(((chunk >> 8) & 0xFF) as u8);
                remaining -= chunk;
            }
        } else {
            out.push(data[i]);
            i += 1;
        }
    }
    out
}

/// RLE-decode a compressed delta buffer.
///
/// Inverse of `rle_encode`: expands `0x00 count_lo count_hi` markers
/// into zero runs, passes non-zero bytes through literally.
pub fn rle_decode(data: &[u8]) -> Vec<u8> {
    let mut out = Vec::with_capacity(data.len() * 2);
    let mut i = 0;
    while i < data.len() {
        if data[i] == 0 {
            if i + 2 >= data.len() {
                break;
            }
            let count = data[i + 1] as usize | ((data[i + 2] as usize) << 8);
            out.resize(out.len() + count, 0);
            i += 3;
        } else {
            out.push(data[i]);
            i += 1;
        }
    }
    out
}

/// Encode a delta frame with RLE: compute XOR delta, then RLE-compress.
/// Returns compressed bytes and the compression ratio (compressed/original).
pub fn encode_delta_rle(current: &[u8], previous: &[u8]) -> (Vec<u8>, f32) {
    let delta = compute_delta(current, previous);
    let compressed = rle_encode(&delta);
    let ratio = compressed.len() as f32 / delta.len() as f32;
    (compressed, ratio)
}

/// Decode a delta frame with RLE: RLE-decompress, then apply XOR delta.
pub fn decode_delta_rle(compressed: &[u8], previous: &[u8]) -> Vec<u8> {
    let delta = rle_decode(compressed);
    apply_delta(previous, &delta)
}

// ─── Convenience Builders ───

/// Build a pixel frame message from raw RGBA data.
pub fn pixel_frame(seq: u16, timestamp: u32, width: u16, height: u16, rgba_data: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::Pixels, seq, timestamp, width, height, FLAG_KEYFRAME, rgba_data)
}

/// Build a delta frame message with RLE-compressed payload.
pub fn delta_frame(seq: u16, timestamp: u32, width: u16, height: u16, rle_data: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::DeltaPixels, seq, timestamp, width, height, FLAG_COMPRESSED, rle_data)
}

/// Build a signal sync (full state) frame from a JSON payload.
pub fn signal_sync_frame(seq: u16, timestamp: u32, json: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::SignalSync, seq, timestamp, 0, 0, FLAG_KEYFRAME, json)
}

/// Build a signal diff (changed signals only) frame from a JSON payload.
pub fn signal_diff_frame(seq: u16, timestamp: u32, json: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::SignalDiff, seq, timestamp, 0, 0, 0, json)
}

/// Build a schema announcement frame (source → relay → receivers).
pub fn schema_announce_frame(seq: u16, timestamp: u32, json: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::SchemaAnnounce, seq, timestamp, 0, 0, 0, json)
}

/// Build a subscribe filter frame (receiver → relay).
pub fn subscribe_filter_frame(seq: u16, timestamp: u32, json: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::SubscribeFilter, seq, timestamp, 0, 0, 0, json)
}

/// Build a pointer input message.
pub fn pointer_input(seq: u16, timestamp: u32, event: &PointerEvent, input_type: InputType) -> Vec<u8> {
    encode_input(input_type, seq, timestamp, &event.encode())
}

/// Build a touch input message.
pub fn touch_input(seq: u16, timestamp: u32, event: &TouchEvent, input_type: InputType) -> Vec<u8> {
    encode_input(input_type, seq, timestamp, &event.encode())
}

/// Build a gamepad axis input message.
pub fn gamepad_axis_input(seq: u16, timestamp: u32, event: &GamepadAxisEvent) -> Vec<u8> {
    encode_input(InputType::GamepadAxis, seq, timestamp, &event.encode())
}

/// Build a gamepad button input message.
pub fn gamepad_button_input(seq: u16, timestamp: u32, event: &GamepadButtonEvent) -> Vec<u8> {
    encode_input(InputType::GamepadButton, seq, timestamp, &event.encode())
}

/// Build a ping/heartbeat message.
pub fn ping(seq: u16, timestamp: u32) -> Vec<u8> {
    encode_frame(FrameType::Ping, seq, timestamp, 0, 0, 0, &[])
}

/// Build a stream-end message.
pub fn stream_end(seq: u16, timestamp: u32) -> Vec<u8> {
    encode_frame(FrameType::End, seq, timestamp, 0, 0, 0, &[])
}

// ─── Neural Frame Builders ───

/// Build a neural frame message (model output tensor as pixels).
pub fn neural_frame(seq: u16, timestamp: u32, width: u16, height: u16, tensor_data: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::NeuralFrame, seq, timestamp, width, height, 0, tensor_data)
}

/// Build a neural audio synthesis output message.
pub fn neural_audio(seq: u16, timestamp: u32, audio_data: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::NeuralAudio, seq, timestamp, 0, 0, 0, audio_data)
}

/// Build a neural actuator command message (learned motor control).
pub fn neural_actuator(seq: u16, timestamp: u32, command_data: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::NeuralActuator, seq, timestamp, 0, 0, 0, command_data)
}

/// Build a neural latent space representation message.
pub fn neural_latent(seq: u16, timestamp: u32, latent_data: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::NeuralLatent, seq, timestamp, 0, 0, 0, latent_data)
}

// ─── Audio Frame Builder ───

/// Build an Opus-compressed audio frame message.
pub fn audio_compressed(seq: u16, timestamp: u32, channels: u16, sample_rate_div100: u16, opus_data: &[u8]) -> Vec<u8> {
    encode_frame(FrameType::AudioCompressed, seq, timestamp, channels, sample_rate_div100, FLAG_COMPRESSED, opus_data)
}

// ─── Remaining Input Builders ───

/// Build a MIDI input message.
pub fn midi_input(seq: u16, timestamp: u32, event: &MidiEvent) -> Vec<u8> {
    encode_input(InputType::Midi, seq, timestamp, &event.encode())
}

/// Build a voice/audio input message (header + PCM payload).
pub fn voice_input(seq: u16, timestamp: u32, event: &VoiceInputEvent, pcm_data: &[u8]) -> Vec<u8> {
    let header_bytes = event.encode();
    let mut payload = Vec::with_capacity(header_bytes.len() + pcm_data.len());
    payload.extend_from_slice(&header_bytes);
    payload.extend_from_slice(pcm_data);
    encode_input(InputType::VoiceInput, seq, timestamp, &payload)
}

/// Build a camera frame input message (header + frame payload).
pub fn camera_input(seq: u16, timestamp: u32, event: &CameraInputEvent, frame_data: &[u8]) -> Vec<u8> {
    let header_bytes = event.encode();
    let mut payload = Vec::with_capacity(header_bytes.len() + frame_data.len());
    payload.extend_from_slice(&header_bytes);
    payload.extend_from_slice(frame_data);
    encode_input(InputType::CameraInput, seq, timestamp, &payload)
}

/// Build a sensor telemetry input message.
pub fn sensor_input(seq: u16, timestamp: u32, event: &SensorInputEvent) -> Vec<u8> {
    encode_input(InputType::SensorInput, seq, timestamp, &event.encode())
}

/// Build a BCI (brain-computer interface) input message (header + samples payload).
pub fn bci_input(seq: u16, timestamp: u32, event: &BciInputEvent, samples: &[u8]) -> Vec<u8> {
    let header_bytes = event.encode();
    let mut payload = Vec::with_capacity(header_bytes.len() + samples.len());
    payload.extend_from_slice(&header_bytes);
    payload.extend_from_slice(samples);
    encode_input(InputType::BciInput, seq, timestamp, &payload)
}

// ─── ACK Frame Builder ───

/// Build an ACK message (receiver → source for RTT measurement).
pub fn ack_frame(seq: u16, timestamp: u32, event: &AckEvent) -> Vec<u8> {
    encode_frame(FrameType::Ack, seq, timestamp, 0, 0, 0, &event.encode())
}

// ─── Compression Helpers ───

/// Compute compression ratio and savings percentage.
/// Returns `(ratio, savings_percent)` where ratio = compressed/original
/// and savings_percent = (1 - ratio) * 100.
pub fn compression_ratio(original_len: usize, compressed_len: usize) -> (f32, f32) {
    if original_len == 0 {
        return (1.0, 0.0);
    }
    let ratio = compressed_len as f32 / original_len as f32;
    let savings = (1.0 - ratio) * 100.0;
    (ratio, savings)
}

// ─── v0.3: Compressed Pixel Frame ───

/// Build a CompressedPixels frame with format byte prepended.
/// `format`: 0=PNG, 1=WebP, 2=JPEG (see `CompressedPixelFormat`).
pub fn compressed_pixel_frame(
    seq: u16, timestamp: u32, width: u16, height: u16,
    format: CompressedPixelFormat, image_data: &[u8],
) -> Vec<u8> {
    let mut payload = Vec::with_capacity(1 + image_data.len());
    payload.push(format as u8);
    payload.extend_from_slice(image_data);
    encode_frame(FrameType::CompressedPixels, seq, timestamp, width, height, FLAG_COMPRESSED, &payload)
}

// ─── v0.3: Recording File I/O ───

/// Magic bytes for `.dsrec` recording files: "DS\x01"
pub const RECORDING_MAGIC: [u8; 3] = [0x44, 0x53, 0x01]; // 'D', 'S', version=1

/// Writes ds-stream messages to a `.dsrec` file.
///
/// File format:
/// ```text
/// [magic: 3 bytes "DS\x01"]
/// [entry]*
///
/// Each entry:
/// [timestamp_us: u64 LE]  — microsecond timestamp
/// [msg_len: u32 LE]       — message length in bytes
/// [msg: msg_len bytes]    — raw ds-stream message (header + payload)
/// ```
pub struct RecordingWriter<W: std::io::Write> {
    writer: W,
    entry_count: u64,
}

impl<W: std::io::Write> RecordingWriter<W> {
    /// Create a new recording writer. Writes the magic header immediately.
    pub fn new(mut writer: W) -> std::io::Result<Self> {
        writer.write_all(&RECORDING_MAGIC)?;
        Ok(Self { writer, entry_count: 0 })
    }

    /// Write a single message entry with timestamp.
    pub fn write_entry(&mut self, timestamp_us: u64, message: &[u8]) -> std::io::Result<()> {
        self.writer.write_all(&timestamp_us.to_le_bytes())?;
        self.writer.write_all(&(message.len() as u32).to_le_bytes())?;
        self.writer.write_all(message)?;
        self.entry_count += 1;
        Ok(())
    }

    /// Flush the underlying writer.
    pub fn flush(&mut self) -> std::io::Result<()> {
        self.writer.flush()
    }

    /// Number of entries written.
    pub fn entry_count(&self) -> u64 {
        self.entry_count
    }
}

/// Reads ds-stream messages from a `.dsrec` file.
pub struct RecordingReader<R: std::io::Read> {
    reader: R,
    entries_read: u64,
}

impl<R: std::io::Read> RecordingReader<R> {
    /// Create a new recording reader. Validates the magic header.
    pub fn new(mut reader: R) -> std::io::Result<Self> {
        let mut magic = [0u8; 3];
        reader.read_exact(&mut magic)?;
        if magic != RECORDING_MAGIC {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                "Invalid .dsrec magic bytes",
            ));
        }
        Ok(Self { reader, entries_read: 0 })
    }

    /// Read the next entry. Returns `None` at end of file.
    /// Returns `Some((timestamp_us, message_bytes))`.
    pub fn next_entry(&mut self) -> std::io::Result<Option<(u64, Vec<u8>)>> {
        let mut ts_buf = [0u8; 8];
        match self.reader.read_exact(&mut ts_buf) {
            Ok(_) => {}
            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
            Err(e) => return Err(e),
        }
        let timestamp_us = u64::from_le_bytes(ts_buf);

        let mut len_buf = [0u8; 4];
        self.reader.read_exact(&mut len_buf)?;
        let msg_len = u32::from_le_bytes(len_buf) as usize;

        let mut message = vec![0u8; msg_len];
        self.reader.read_exact(&mut message)?;

        self.entries_read += 1;
        Ok(Some((timestamp_us, message)))
    }

    /// Number of entries read so far.
    pub fn entries_read(&self) -> u64 {
        self.entries_read
    }

    /// Read all remaining entries into a Vec.
    pub fn read_all(&mut self) -> std::io::Result<Vec<(u64, Vec<u8>)>> {
        let mut entries = Vec::new();
        while let Some(entry) = self.next_entry()? {
            entries.push(entry);
        }
        Ok(entries)
    }
}


// ─── v0.4: Adaptive Quality ───

/// Quality tier based on measured RTT.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QualityTier {
    /// RTT < 50ms: full quality, all frames
    Full = 0,
    /// RTT 50-150ms: reduced quality, skip some frames
    Reduced = 1,
    /// RTT > 150ms: minimal — signal diffs only, no pixels
    Minimal = 2,
}

/// Adaptive quality controller that tracks RTT and recommends quality settings.
pub struct AdaptiveQuality {
    rtt_history: Vec<u16>,
    max_history: usize,
    current_tier: QualityTier,
}

impl AdaptiveQuality {
    pub fn new() -> Self {
        Self {
            rtt_history: Vec::new(),
            max_history: 30,
            current_tier: QualityTier::Full,
        }
    }

    /// Record an ACK RTT measurement and update the quality tier.
    pub fn record_ack(&mut self, rtt_ms: u16) {
        self.rtt_history.push(rtt_ms);
        if self.rtt_history.len() > self.max_history {
            self.rtt_history.remove(0);
        }
        self.current_tier = self.compute_tier();
    }

    /// Current quality tier.
    pub fn current_tier(&self) -> QualityTier {
        self.current_tier
    }

    /// Suggested JPEG/WebP quality (0-100).
    pub fn suggested_quality(&self) -> u8 {
        match self.current_tier {
            QualityTier::Full => 80,
            QualityTier::Reduced => 50,
            QualityTier::Minimal => 0,
        }
    }

    /// Suggested number of frames to skip between sends.
    pub fn suggested_skip_frames(&self) -> u32 {
        match self.current_tier {
            QualityTier::Full => 0,
            QualityTier::Reduced => 2,
            QualityTier::Minimal => u32::MAX,
        }
    }

    /// Average RTT over the history window (or 0 if empty).
    pub fn avg_rtt(&self) -> u16 {
        if self.rtt_history.is_empty() { return 0; }
        let sum: u32 = self.rtt_history.iter().map(|&r| r as u32).sum();
        (sum / self.rtt_history.len() as u32) as u16
    }

    fn compute_tier(&self) -> QualityTier {
        let avg = self.avg_rtt();
        if avg < 50 { QualityTier::Full }
        else if avg < 150 { QualityTier::Reduced }
        else { QualityTier::Minimal }
    }
}

/// Sliding window bandwidth estimator.
pub struct BandwidthEstimator {
    /// (timestamp_ms, bytes) entries
    samples: Vec<(u64, usize)>,
    window_ms: u64,
}

impl BandwidthEstimator {
    pub fn new(window_seconds: u32) -> Self {
        Self {
            samples: Vec::new(),
            window_ms: window_seconds as u64 * 1000,
        }
    }

    /// Record bytes sent/received at a given timestamp.
    pub fn record(&mut self, timestamp_ms: u64, bytes: usize) {
        self.samples.push((timestamp_ms, bytes));
        self.prune(timestamp_ms);
    }

    /// Estimated throughput in bytes per second.
    pub fn estimate_bps(&self) -> f64 {
        if self.samples.len() < 2 { return 0.0; }
        let first = self.samples.first().unwrap().0;
        let last = self.samples.last().unwrap().0;
        let elapsed = (last - first) as f64 / 1000.0;
        if elapsed <= 0.0 { return 0.0; }
        let total: usize = self.samples.iter().map(|s| s.1).sum();
        total as f64 / elapsed
    }

    /// Check if bandwidth is below a threshold.
    pub fn is_congested(&self, threshold_bps: f64) -> bool {
        self.estimate_bps() > 0.0 && self.estimate_bps() < threshold_bps
    }

    fn prune(&mut self, now: u64) {
        let cutoff = now.saturating_sub(self.window_ms);
        self.samples.retain(|s| s.0 >= cutoff);
    }
}

// ─── v0.5: Recording Metadata ───

/// Metadata header for .dsrec files. Written after magic bytes.
/// Total size: 24 bytes.
#[derive(Debug, Clone, PartialEq)]
pub struct RecordingMetadata {
    pub version: u16,
    pub created_at: u64,   // unix timestamp seconds
    pub duration_ms: u32,
    pub frame_count: u32,
    pub width: u16,
    pub height: u16,
}

impl RecordingMetadata {
    pub const SIZE: usize = 24;

    pub fn encode(&self) -> [u8; Self::SIZE] {
        let mut buf = [0u8; Self::SIZE];
        buf[0..2].copy_from_slice(&self.version.to_le_bytes());
        buf[2..10].copy_from_slice(&self.created_at.to_le_bytes());
        buf[10..14].copy_from_slice(&self.duration_ms.to_le_bytes());
        buf[14..18].copy_from_slice(&self.frame_count.to_le_bytes());
        buf[18..20].copy_from_slice(&self.width.to_le_bytes());
        buf[20..22].copy_from_slice(&self.height.to_le_bytes());
        // bytes 22-23 reserved
        buf
    }

    pub fn decode(buf: &[u8]) -> Option<Self> {
        if buf.len() < Self::SIZE { return None; }
        Some(Self {
            version: u16::from_le_bytes([buf[0], buf[1]]),
            created_at: u64::from_le_bytes([buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]]),
            duration_ms: u32::from_le_bytes([buf[10], buf[11], buf[12], buf[13]]),
            frame_count: u32::from_le_bytes([buf[14], buf[15], buf[16], buf[17]]),
            width: u16::from_le_bytes([buf[18], buf[19]]),
            height: u16::from_le_bytes([buf[20], buf[21]]),
        })
    }
}

// ─── v0.5: Auth Frame Builders ───

/// Build an auth challenge frame (server → client).
pub fn auth_challenge_frame(seq: u16, nonce: &[u8; 8]) -> Vec<u8> {
    let payload = AuthPayload {
        phase: 0,
        nonce: *nonce,
        token: Vec::new(),
    };
    let encoded = payload.encode();
    encode_frame(FrameType::Auth, seq, 0, 0, 0, 0, &encoded)
}

/// Build an auth response frame (client → server).
pub fn auth_response_frame(seq: u16, nonce: &[u8; 8], token: &[u8]) -> Vec<u8> {
    let payload = AuthPayload {
        phase: 1,
        nonce: *nonce,
        token: token.to_vec(),
    };
    let encoded = payload.encode();
    encode_frame(FrameType::Auth, seq, 0, 0, 0, 0, &encoded)
}

// ─── v0.10: Stream Multiplexing ───

/// Multiplex a frame onto a logical stream by embedding stream_id in the flags byte.
pub fn mux_frame(stream_id: u8, frame: &[u8]) -> Vec<u8> {
    if frame.len() < HEADER_SIZE { return frame.to_vec(); }
    let mut out = frame.to_vec();
    out[1] = embed_stream_id(out[1], stream_id);
    out
}

/// Demultiplex: extract stream_id and strip it from the flags byte.
/// Returns (stream_id, cleaned_frame).
pub fn demux_frame(frame: &[u8]) -> (u8, Vec<u8>) {
    if frame.len() < HEADER_SIZE { return (0, frame.to_vec()); }
    let stream_id = extract_stream_id(frame[1]);
    let mut out = frame.to_vec();
    out[1] &= FLAGS_MASK;
    (stream_id, out)
}

// ─── v0.10: Frame Priority ───

/// Priority levels for frame types.
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum FramePriority {
    Critical = 0,
    High = 1,
    Normal = 2,
    Low = 3,
}

/// Determine the priority of a frame type.
pub fn prioritize_frame(frame_type: u8) -> FramePriority {
    match FrameType::from_u8(frame_type) {
        Some(FrameType::Keyframe) | Some(FrameType::Auth) | Some(FrameType::End) => FramePriority::Critical,
        Some(FrameType::Pixels) | Some(FrameType::CompressedPixels) | Some(FrameType::DeltaPixels) |
        Some(FrameType::NeuralFrame) => FramePriority::High,
        Some(FrameType::SignalSync) | Some(FrameType::SignalDiff) |
        Some(FrameType::AudioPcm) | Some(FrameType::AudioCompressed) => FramePriority::Normal,
        Some(FrameType::Ping) | Some(FrameType::Ack) => FramePriority::Low,
        _ => FramePriority::Normal,
    }
}

// ─── v0.10: Bandwidth Allocator ───

/// Per-stream bandwidth allocation.
#[derive(Debug, Clone)]
pub struct StreamAllocation {
    pub stream_id: u8,
    pub priority: FramePriority,
    pub byte_limit: u32,
}

/// Allocate bytes across streams based on priority.
/// Higher-priority streams get proportionally more bandwidth.
pub fn allocate_bandwidth(total_bps: u32, streams: &[(u8, FramePriority)]) -> Vec<StreamAllocation> {
    if streams.is_empty() { return Vec::new(); }
    // Priority weights: Critical=8, High=4, Normal=2, Low=1
    let weight = |p: &FramePriority| -> u32 {
        match p {
            FramePriority::Critical => 8,
            FramePriority::High => 4,
            FramePriority::Normal => 2,
            FramePriority::Low => 1,
        }
    };
    let total_weight: u32 = streams.iter().map(|(_, p)| weight(p)).sum();
    if total_weight == 0 { return Vec::new(); }
    streams.iter().map(|(id, p)| {
        StreamAllocation {
            stream_id: *id,
            priority: *p,
            byte_limit: (total_bps as u64 * weight(p) as u64 / total_weight as u64) as u32,
        }
    }).collect()
}

// ─── v0.10: Frame Drop Policy ───

/// Congestion level (0-3). 0 = no congestion, 3 = severe.
pub type CongestionLevel = u8;

/// Determine if a frame should be dropped based on priority and congestion.
/// Returns true if the frame should be dropped.
pub fn should_drop(priority: FramePriority, congestion: CongestionLevel) -> bool {
    match congestion {
        0 => false,                     // No drops
        1 => priority == FramePriority::Low,  // Drop low-priority only
        2 => priority >= FramePriority::Normal, // Drop normal and low
        _ => priority >= FramePriority::High,   // Drop everything except critical
    }
}

// ─── Tests ───

// ─── v0.11: Frame Stats ───

/// Accumulates per-stream frame statistics.
#[derive(Debug, Clone)]
pub struct FrameStats {
    pub frame_count: u64,
    pub total_bytes: u64,
    pub dropped_count: u64,
    pub min_size: u32,
    pub max_size: u32,
}

impl FrameStats {
    pub fn new() -> Self {
        FrameStats {
            frame_count: 0,
            total_bytes: 0,
            dropped_count: 0,
            min_size: u32::MAX,
            max_size: 0,
        }
    }

    /// Record a frame (len in bytes, dropped = true if skipped).
    pub fn record(&mut self, len: u32, dropped: bool) {
        if dropped {
            self.dropped_count += 1;
        } else {
            self.frame_count += 1;
            self.total_bytes += len as u64;
            self.min_size = self.min_size.min(len);
            self.max_size = self.max_size.max(len);
        }
    }

    /// Average frame size in bytes.
    pub fn avg_size(&self) -> f64 {
        if self.frame_count == 0 { 0.0 } else { self.total_bytes as f64 / self.frame_count as f64 }
    }

    /// Reset all counters.
    pub fn reset(&mut self) {
        *self = FrameStats::new();
    }
}

impl Default for FrameStats {
    fn default() -> Self { Self::new() }
}

// ─── v0.11: Jitter Buffer ───

/// Reorders frames by sequence number with configurable depth.
pub struct JitterBuffer {
    buffer: Vec<(u16, Vec<u8>)>,
    depth: usize,
    next_seq: u16,
}

impl JitterBuffer {
    pub fn new(depth: usize) -> Self {
        JitterBuffer {
            buffer: Vec::with_capacity(depth),
            depth: depth.max(1),
            next_seq: 0,
        }
    }

    /// Push a frame with its sequence number.
    pub fn push(&mut self, seq: u16, frame: Vec<u8>) {
        if self.buffer.len() >= self.depth * 2 {
            // Prevent unbounded growth: flush oldest
            self.buffer.sort_by_key(|(s, _)| *s);
            self.buffer.drain(..self.depth);
        }
        self.buffer.push((seq, frame));
    }

    /// Pop the next in-order frame, if available.
    pub fn pop(&mut self) -> Option<Vec<u8>> {
        if let Some(pos) = self.buffer.iter().position(|(s, _)| *s == self.next_seq) {
            let (_, frame) = self.buffer.remove(pos);
            self.next_seq = self.next_seq.wrapping_add(1);
            Some(frame)
        } else if self.buffer.len() >= self.depth {
            // Buffer full, force pop oldest
            self.buffer.sort_by_key(|(s, _)| *s);
            let (seq, frame) = self.buffer.remove(0);
            self.next_seq = seq.wrapping_add(1);
            Some(frame)
        } else {
            None
        }
    }

    /// Number of buffered frames.
    pub fn len(&self) -> usize { self.buffer.len() }
    pub fn is_empty(&self) -> bool { self.buffer.is_empty() }
}

// ─── v0.11: Latency Tracker ───

/// Tracks round-trip time samples for latency analysis.
#[derive(Debug, Clone)]
pub struct LatencyTracker {
    samples: Vec<f64>,
    max_samples: usize,
}

impl LatencyTracker {
    pub fn new(max_samples: usize) -> Self {
        LatencyTracker {
            samples: Vec::with_capacity(max_samples),
            max_samples: max_samples.max(1),
        }
    }

    /// Record an RTT sample in milliseconds.
    pub fn record_rtt(&mut self, ms: f64) {
        if self.samples.len() >= self.max_samples {
            self.samples.remove(0);
        }
        self.samples.push(ms);
    }

    /// Average RTT in ms.
    pub fn avg_rtt(&self) -> f64 {
        if self.samples.is_empty() { return 0.0; }
        self.samples.iter().sum::<f64>() / self.samples.len() as f64
    }

    /// P95 RTT in ms.
    pub fn p95_rtt(&self) -> f64 {
        if self.samples.is_empty() { return 0.0; }
        let mut sorted = self.samples.clone();
        sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
        let idx = ((sorted.len() as f64 * 0.95) as usize).min(sorted.len() - 1);
        sorted[idx]
    }

    /// Jitter: standard deviation of RTT samples.
    pub fn jitter(&self) -> f64 {
        if self.samples.len() < 2 { return 0.0; }
        let avg = self.avg_rtt();
        let variance = self.samples.iter().map(|s| (s - avg).powi(2)).sum::<f64>() / self.samples.len() as f64;
        variance.sqrt()
    }

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

// ─── v0.12: State Snapshot ───

/// Encode application state as a frame for initial sync on reconnect.
/// Format: [version:u32][state_bytes...]
pub fn encode_state(version: u32, state: &[u8]) -> Vec<u8> {
    let mut out = Vec::with_capacity(4 + state.len());
    out.extend_from_slice(&version.to_le_bytes());
    out.extend_from_slice(state);
    out
}

/// Decode state snapshot. Returns (version, state_bytes).
pub fn decode_state(data: &[u8]) -> Option<(u32, Vec<u8>)> {
    if data.len() < 4 { return None; }
    let version = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
    Some((version, data[4..].to_vec()))
}

// ─── v0.12: Gap Detector ───

/// Result of feeding a sequence number to the gap detector.
#[derive(Debug, Clone, PartialEq)]
pub enum GapResult {
    Ok,
    Gap { expected: u16, got: u16 },
    Duplicate,
}

/// Detects gaps and duplicates in sequence numbers.
pub struct GapDetector {
    next_expected: u16,
    seen: Vec<u16>,
    max_seen: usize,
}

impl GapDetector {
    pub fn new() -> Self {
        GapDetector { next_expected: 0, seen: Vec::new(), max_seen: 256 }
    }

    /// Feed a sequence number. Returns gap/dup/ok status.
    pub fn feed(&mut self, seq: u16) -> GapResult {
        // Check duplicate
        if self.seen.contains(&seq) {
            return GapResult::Duplicate;
        }
        if self.seen.len() >= self.max_seen {
            self.seen.drain(..self.max_seen / 2);
        }
        self.seen.push(seq);

        if seq == self.next_expected {
            self.next_expected = self.next_expected.wrapping_add(1);
            GapResult::Ok
        } else {
            let expected = self.next_expected;
            self.next_expected = seq.wrapping_add(1);
            GapResult::Gap { expected, got: seq }
        }
    }

    pub fn expected(&self) -> u16 { self.next_expected }
}

// ─── v0.12: Connection Handshake ───

/// Build a handshake hello message.
/// Format: [0x48][version:u16][cap_count:u8][capabilities...]
pub fn handshake_hello(version: u16, capabilities: &[u8]) -> Vec<u8> {
    let mut out = Vec::with_capacity(4 + capabilities.len());
    out.push(0x48); // 'H'
    out.extend_from_slice(&version.to_le_bytes());
    out.push(capabilities.len() as u8);
    out.extend_from_slice(capabilities);
    out
}

/// Build a handshake accept response.
/// Format: [0x41][version:u16]
pub fn handshake_accept(version: u16) -> Vec<u8> {
    let mut out = Vec::with_capacity(3);
    out.push(0x41); // 'A'
    out.extend_from_slice(&version.to_le_bytes());
    out
}

/// Parse a handshake hello. Returns (version, capabilities).
pub fn parse_handshake_hello(data: &[u8]) -> Option<(u16, Vec<u8>)> {
    if data.len() < 4 || data[0] != 0x48 { return None; }
    let version = u16::from_le_bytes([data[1], data[2]]);
    let cap_count = data[3] as usize;
    if data.len() < 4 + cap_count { return None; }
    Some((version, data[4..4 + cap_count].to_vec()))
}

// ─── v0.13: Replay Buffer ───

/// Circular replay buffer for DVR-style rewind.
pub struct ReplayBuffer {
    frames: Vec<Vec<u8>>,
    capacity: usize,
    write_pos: usize,
    count: usize,
}

impl ReplayBuffer {
    pub fn new(capacity: usize) -> Self {
        ReplayBuffer {
            frames: Vec::with_capacity(capacity),
            capacity: capacity.max(1),
            write_pos: 0,
            count: 0,
        }
    }

    /// Push a frame into the buffer (overwrites oldest if full).
    pub fn push(&mut self, frame: Vec<u8>) {
        if self.frames.len() < self.capacity {
            self.frames.push(frame);
        } else {
            self.frames[self.write_pos] = frame;
        }
        self.write_pos = (self.write_pos + 1) % self.capacity;
        self.count += 1;
    }

    /// Get frames in the range [start, end) from oldest to newest.
    pub fn get_range(&self, start: usize, end: usize) -> Vec<Vec<u8>> {
        let len = self.frames.len();
        if len == 0 || start >= len { return vec![]; }
        let end = end.min(len);
        if self.count <= self.capacity {
            self.frames[start..end].to_vec()
        } else {
            let mut result = Vec::new();
            for i in start..end {
                let idx = (self.write_pos + i) % self.capacity;
                result.push(self.frames[idx].clone());
            }
            result
        }
    }

    /// Snapshot all buffered frames.
    pub fn snapshot(&self) -> Vec<Vec<u8>> {
        self.get_range(0, self.frames.len())
    }

    pub fn len(&self) -> usize { self.frames.len() }
    pub fn is_empty(&self) -> bool { self.frames.is_empty() }
    pub fn total_pushed(&self) -> usize { self.count }
}

// ─── v0.13: Header Serialization ───

/// Serialize a frame header to 16 bytes.
pub fn serialize_header(frame_type: u8, flags: u8, seq: u16, timestamp: u32, width: u16, height: u16, payload_len: u32) -> Vec<u8> {
    let mut out = Vec::with_capacity(HEADER_SIZE);
    out.push(frame_type);
    out.push(flags);
    out.extend_from_slice(&seq.to_le_bytes());
    out.extend_from_slice(&timestamp.to_le_bytes());
    out.extend_from_slice(&width.to_le_bytes());
    out.extend_from_slice(&height.to_le_bytes());
    out.extend_from_slice(&payload_len.to_le_bytes());
    out
}

/// Deserialize a 16-byte header. Returns (type, flags, seq, timestamp, width, height, len).
pub fn deserialize_header(data: &[u8]) -> Option<(u8, u8, u16, u32, u16, u16, u32)> {
    if data.len() < HEADER_SIZE { return None; }
    let frame_type = data[0];
    let flags = data[1];
    let seq = u16::from_le_bytes([data[2], data[3]]);
    let timestamp = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
    let width = u16::from_le_bytes([data[8], data[9]]);
    let height = u16::from_le_bytes([data[10], data[11]]);
    let payload_len = u32::from_le_bytes([data[12], data[13], data[14], data[15]]);
    Some((frame_type, flags, seq, timestamp, width, height, payload_len))
}

// ─── v0.13: Codec Registry ───

/// Named codec entry.
#[derive(Debug, Clone)]
pub struct CodecEntry {
    pub name: String,
    pub encode_id: u8,
    pub decode_id: u8,
}

/// Registry for dynamically loaded codecs.
pub struct CodecRegistry {
    codecs: Vec<CodecEntry>,
}

impl CodecRegistry {
    pub fn new() -> Self { CodecRegistry { codecs: Vec::new() } }

    pub fn register(&mut self, name: &str, encode_id: u8, decode_id: u8) {
        self.codecs.push(CodecEntry {
            name: name.to_string(),
            encode_id,
            decode_id,
        });
    }

    pub fn list(&self) -> Vec<String> {
        self.codecs.iter().map(|c| c.name.clone()).collect()
    }

    pub fn get(&self, name: &str) -> Option<&CodecEntry> {
        self.codecs.iter().find(|c| c.name == name)
    }

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

impl Default for CodecRegistry {
    fn default() -> Self { Self::new() }
}

// ─── v0.14: Frame Compressor (RLE) ───

/// Simple run-length encoding compressor.
pub struct FrameCompressor {
    total_in: usize,
    total_out: usize,
}

impl FrameCompressor {
    pub fn new() -> Self { FrameCompressor { total_in: 0, total_out: 0 } }

    /// RLE compress: [byte, count, byte, count, ...]
    pub fn compress(&mut self, data: &[u8]) -> Vec<u8> {
        self.total_in += data.len();
        let mut out = Vec::new();
        if data.is_empty() { return out; }
        let mut i = 0;
        while i < data.len() {
            let val = data[i];
            let mut count: u8 = 1;
            while i + (count as usize) < data.len() && data[i + count as usize] == val && count < 255 {
                count += 1;
            }
            out.push(val);
            out.push(count);
            i += count as usize;
        }
        self.total_out += out.len();
        out
    }

    /// RLE decompress.
    pub fn decompress(&self, data: &[u8]) -> Vec<u8> {
        let mut out = Vec::new();
        let mut i = 0;
        while i + 1 < data.len() {
            let val = data[i];
            let count = data[i + 1] as usize;
            for _ in 0..count { out.push(val); }
            i += 2;
        }
        out
    }

    /// Compression ratio (lower = better).
    pub fn compression_ratio(&self) -> f32 {
        if self.total_in == 0 { return 1.0; }
        self.total_out as f32 / self.total_in as f32
    }
}

impl Default for FrameCompressor {
    fn default() -> Self { Self::new() }
}

// ─── v0.14: Multi-Client Sync ───

/// Per-client state.
#[derive(Debug, Clone)]
pub struct ClientState {
    pub id: String,
    pub last_seq: u16,
    pub last_ack: u16,
    pub drift: i32, // seq - ack difference
}

/// Tracks per-client sync state.
pub struct MultiClientSync {
    clients: Vec<ClientState>,
}

impl MultiClientSync {
    pub fn new() -> Self { MultiClientSync { clients: Vec::new() } }

    pub fn register_client(&mut self, id: &str) {
        self.clients.push(ClientState {
            id: id.to_string(), last_seq: 0, last_ack: 0, drift: 0,
        });
    }

    pub fn update_seq(&mut self, id: &str, seq: u16) {
        if let Some(c) = self.clients.iter_mut().find(|c| c.id == id) {
            c.last_seq = seq;
            c.drift = seq as i32 - c.last_ack as i32;
        }
    }

    pub fn update_ack(&mut self, id: &str, ack: u16) {
        if let Some(c) = self.clients.iter_mut().find(|c| c.id == id) {
            c.last_ack = ack;
            c.drift = c.last_seq as i32 - ack as i32;
        }
    }

    /// Get the slowest consumer (highest drift). Returns client id or empty.
    pub fn slowest_client(&self) -> String {
        self.clients.iter().max_by_key(|c| c.drift)
            .map(|c| c.id.clone()).unwrap_or_default()
    }

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

    pub fn get_drift(&self, id: &str) -> i32 {
        self.clients.iter().find(|c| c.id == id).map(|c| c.drift).unwrap_or(0)
    }
}

impl Default for MultiClientSync {
    fn default() -> Self { Self::new() }
}

// ─── v0.14: Bandwidth Throttle ───

/// Rate limiter for bandwidth.
pub struct BandwidthThrottle {
    limit_bps: usize,
    consumed: usize,
    window_start: u64, // timestamp ms
}

impl BandwidthThrottle {
    pub fn new(limit_bps: usize) -> Self {
        BandwidthThrottle { limit_bps, consumed: 0, window_start: 0 }
    }

    /// Set bandwidth limit (bytes per second).
    pub fn set_limit(&mut self, bps: usize) { self.limit_bps = bps; }

    /// Check if a frame of given size can be sent. Advances consumed counter if yes.
    pub fn should_send(&mut self, frame_size: usize, now_ms: u64) -> bool {
        // Reset window every second
        if now_ms - self.window_start >= 1000 {
            self.consumed = 0;
            self.window_start = now_ms;
        }
        if self.consumed + frame_size <= self.limit_bps {
            self.consumed += frame_size;
            true
        } else {
            false
        }
    }

    pub fn consumed(&self) -> usize { self.consumed }
    pub fn limit(&self) -> usize { self.limit_bps }
}

// ─── v0.15: Adaptive Bitrate ───

/// Adaptive bitrate controller based on RTT and loss.
pub struct AdaptiveBitrate {
    rtt_samples: Vec<f32>,
    loss_samples: Vec<f32>,
    max_samples: usize,
}

impl AdaptiveBitrate {
    pub fn new() -> Self {
        AdaptiveBitrate { rtt_samples: Vec::new(), loss_samples: Vec::new(), max_samples: 30 }
    }

    pub fn feed_rtt(&mut self, ms: f32) {
        if self.rtt_samples.len() >= self.max_samples { self.rtt_samples.remove(0); }
        self.rtt_samples.push(ms);
    }

    pub fn feed_loss(&mut self, pct: f32) {
        if self.loss_samples.len() >= self.max_samples { self.loss_samples.remove(0); }
        self.loss_samples.push(pct);
    }

    fn avg_rtt(&self) -> f32 {
        if self.rtt_samples.is_empty() { return 0.0; }
        self.rtt_samples.iter().sum::<f32>() / self.rtt_samples.len() as f32
    }

    fn avg_loss(&self) -> f32 {
        if self.loss_samples.is_empty() { return 0.0; }
        self.loss_samples.iter().sum::<f32>() / self.loss_samples.len() as f32
    }

    /// Recommended quality tier 0-4 (0=lowest, 4=highest).
    pub fn recommended_tier(&self) -> u8 {
        let rtt = self.avg_rtt();
        let loss = self.avg_loss();
        if rtt > 200.0 || loss > 10.0 { 0 }
        else if rtt > 100.0 || loss > 5.0 { 1 }
        else if rtt > 50.0 || loss > 2.0 { 2 }
        else if rtt > 20.0 || loss > 0.5 { 3 }
        else { 4 }
    }
}

impl Default for AdaptiveBitrate {
    fn default() -> Self { Self::new() }
}

// ─── v0.15: Metrics Snapshot ───

/// Frozen snapshot of stream metrics.
#[derive(Debug, Clone)]
pub struct MetricsSnapshot {
    pub fps: f32,
    pub bitrate_bps: usize,
    pub clients: usize,
    pub avg_drift: f32,
    pub health: f32,
    pub timestamp_ms: u64,
}

impl MetricsSnapshot {
    pub fn capture(fps: f32, bitrate_bps: usize, clients: usize, avg_drift: f32, health: f32) -> Self {
        MetricsSnapshot {
            fps, bitrate_bps, clients, avg_drift, health,
            timestamp_ms: 0, // caller sets
        }
    }

    pub fn to_json(&self) -> String {
        format!(
            r#"{{"fps":{:.1},"bitrate":{},"clients":{},"drift":{:.1},"health":{:.2}}}"#,
            self.fps, self.bitrate_bps, self.clients, self.avg_drift, self.health,
        )
    }
}

// ─── v0.15: Frame Pipeline ───

/// Named processing stage.
#[derive(Debug, Clone)]
pub struct PipelineStage {
    pub name: String,
}

/// Chain of named processing stages.
pub struct FramePipeline {
    stages: Vec<PipelineStage>,
}

impl FramePipeline {
    pub fn new() -> Self { FramePipeline { stages: Vec::new() } }

    pub fn add_stage(&mut self, name: &str) {
        self.stages.push(PipelineStage { name: name.to_string() });
    }

    /// Process data through all stages (identity for now — stages are named hooks).
    pub fn process(&self, data: Vec<u8>) -> Vec<u8> {
        // Each stage is a named hook; actual transforms would be registered callbacks.
        // For now, pipeline is a named pass-through.
        data
    }

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

    pub fn stage_names(&self) -> Vec<String> {
        self.stages.iter().map(|s| s.name.clone()).collect()
    }
}

impl Default for FramePipeline {
    fn default() -> Self { Self::new() }
}

// ─── v0.16: Frame Encryptor ───

/// XOR cipher with rotating key.
pub struct FrameEncryptor;

impl FrameEncryptor {
    /// XOR encrypt data with key (symmetric).
    pub fn encrypt(data: &[u8], key: &[u8]) -> Vec<u8> {
        if key.is_empty() { return data.to_vec(); }
        data.iter().enumerate().map(|(i, &b)| b ^ key[i % key.len()]).collect()
    }

    /// XOR decrypt (same as encrypt for XOR).
    pub fn decrypt(data: &[u8], key: &[u8]) -> Vec<u8> {
        Self::encrypt(data, key)
    }
}

// ─── v0.16: Stream Migration ───

/// State transfer for stream migration between servers.
pub struct StreamMigration {
    state: Vec<u8>,
}

impl StreamMigration {
    pub fn new() -> Self { StreamMigration { state: Vec::new() } }

    /// Serialize current state for handoff.
    pub fn prepare_handoff(&mut self, seq: u16, clients: usize, fps: f32) -> Vec<u8> {
        let mut buf = Vec::with_capacity(8);
        buf.extend_from_slice(&seq.to_le_bytes());
        buf.extend_from_slice(&(clients as u16).to_le_bytes());
        buf.extend_from_slice(&fps.to_le_bytes());
        self.state = buf.clone();
        buf
    }

    /// Accept state from another server.
    pub fn accept_handoff(&mut self, state: &[u8]) -> Option<(u16, u16, f32)> {
        if state.len() < 8 { return None; }
        let seq = u16::from_le_bytes([state[0], state[1]]);
        let clients = u16::from_le_bytes([state[2], state[3]]);
        let fps = f32::from_le_bytes([state[4], state[5], state[6], state[7]]);
        self.state = state.to_vec();
        Some((seq, clients, fps))
    }
}

impl Default for StreamMigration {
    fn default() -> Self { Self::new() }
}

// ─── v0.16: Frame Dedup ───

/// Hash-based frame deduplication.
pub struct FrameDedup {
    seen_hashes: Vec<u64>,
    max_entries: usize,
    bytes_saved: usize,
}

impl FrameDedup {
    pub fn new(max_entries: usize) -> Self {
        FrameDedup { seen_hashes: Vec::new(), max_entries, bytes_saved: 0 }
    }

    fn hash(data: &[u8]) -> u64 {
        let mut h: u64 = 0xcbf29ce484222325;
        for &b in data {
            h ^= b as u64;
            h = h.wrapping_mul(0x100000001b3);
        }
        h
    }

    /// Check if frame is duplicate. Returns true if already seen.
    pub fn is_duplicate(&mut self, data: &[u8]) -> bool {
        let h = Self::hash(data);
        if self.seen_hashes.contains(&h) {
            self.bytes_saved += data.len();
            return true;
        }
        if self.seen_hashes.len() >= self.max_entries { self.seen_hashes.remove(0); }
        self.seen_hashes.push(h);
        false
    }

    pub fn dedup_savings(&self) -> usize { self.bytes_saved }
}

// ─── v0.16: Priority Queue ───

/// Priority-sorted frame queue.
pub struct PriorityQueue<T> {
    items: Vec<(T, u8)>, // (item, priority)
}

impl<T> PriorityQueue<T> {
    pub fn new() -> Self { PriorityQueue { items: Vec::new() } }

    pub fn enqueue(&mut self, item: T, priority: u8) {
        self.items.push((item, priority));
        self.items.sort_by(|a, b| b.1.cmp(&a.1));
    }

    pub fn dequeue(&mut self) -> Option<T> {
        if self.items.is_empty() { None }
        else { Some(self.items.remove(0).0) }
    }

    pub fn len(&self) -> usize { self.items.len() }
    pub fn is_empty(&self) -> bool { self.items.is_empty() }
}

impl<T> Default for PriorityQueue<T> {
    fn default() -> Self { Self::new() }
}

// ─── v0.17: Video Codec Adapter ───

/// Abstract codec adapter for video formats.
pub struct VideoCodecAdapter {
    name: String,
    mime_types: Vec<String>,
    ratio: f32,
}

impl VideoCodecAdapter {
    pub fn new(name: &str, mime_types: &[&str], ratio: f32) -> Self {
        VideoCodecAdapter {
            name: name.to_string(),
            mime_types: mime_types.iter().map(|s| s.to_string()).collect(),
            ratio,
        }
    }

    pub fn codec_name(&self) -> &str { &self.name }
    pub fn can_encode(&self, mime: &str) -> bool { self.mime_types.iter().any(|m| m == mime) }
    pub fn estimated_ratio(&self) -> f32 { self.ratio }
}

// ─── v0.17: Stream Router ───

/// Route frames to named destinations.
pub struct StreamRouter {
    routes: std::collections::HashMap<String, Vec<Vec<u8>>>,
}

impl StreamRouter {
    pub fn new() -> Self { StreamRouter { routes: std::collections::HashMap::new() } }

    pub fn add_route(&mut self, name: &str) {
        self.routes.entry(name.to_string()).or_default();
    }

    pub fn route_frame(&mut self, name: &str, data: Vec<u8>) -> bool {
        if let Some(buf) = self.routes.get_mut(name) {
            buf.push(data);
            true
        } else { false }
    }

    pub fn get_routed(&mut self, name: &str) -> Vec<Vec<u8>> {
        self.routes.get_mut(name).map(|b| std::mem::take(b)).unwrap_or_default()
    }

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

impl Default for StreamRouter {
    fn default() -> Self { Self::new() }
}

// ─── v0.17: Frame Interleaver ───

/// Merge multiple streams into one interleaved stream.
pub struct FrameInterleaver {
    buffer: Vec<(u8, Vec<u8>)>, // (stream_id, data)
}

impl FrameInterleaver {
    pub fn new() -> Self { FrameInterleaver { buffer: Vec::new() } }

    pub fn interleave(&mut self, stream_id: u8, data: Vec<u8>) {
        self.buffer.push((stream_id, data));
    }

    pub fn deinterleave(&mut self) -> Vec<(u8, Vec<u8>)> {
        std::mem::take(&mut self.buffer)
    }

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

impl Default for FrameInterleaver {
    fn default() -> Self { Self::new() }
}

// ─── v0.17: Backpressure ───

/// Flow control with bounded buffer.
pub struct Backpressure {
    buffer: Vec<Vec<u8>>,
    capacity: usize,
}

impl Backpressure {
    pub fn new(capacity: usize) -> Self {
        Backpressure { buffer: Vec::new(), capacity }
    }

    pub fn set_capacity(&mut self, cap: usize) { self.capacity = cap; }

    pub fn try_push(&mut self, data: Vec<u8>) -> bool {
        if self.buffer.len() >= self.capacity { return false; }
        self.buffer.push(data);
        true
    }

    pub fn drain(&mut self) -> Vec<Vec<u8>> {
        std::mem::take(&mut self.buffer)
    }

    pub fn len(&self) -> usize { self.buffer.len() }
    pub fn is_full(&self) -> bool { self.buffer.len() >= self.capacity }
}

// ─── v0.18: Frame Scheduler ───

/// Frame emission timing controller.
pub struct FrameScheduler {
    target_fps: f64,
    last_emit_ms: f64,
    frames_emitted: u64,
}

impl FrameScheduler {
    pub fn new(target_fps: f64) -> Self {
        FrameScheduler { target_fps, last_emit_ms: -1000.0, frames_emitted: 0 }
    }

    pub fn set_fps(&mut self, fps: f64) { self.target_fps = fps; }
    pub fn target_fps(&self) -> f64 { self.target_fps }

    /// Check if enough time has passed to emit a frame.
    pub fn should_emit(&mut self, current_ms: f64) -> bool {
        let interval = 1000.0 / self.target_fps;
        if current_ms - self.last_emit_ms >= interval {
            self.last_emit_ms = current_ms;
            self.frames_emitted += 1;
            true
        } else { false }
    }

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

impl Default for FrameScheduler {
    fn default() -> Self { Self::new(30.0) }
}

// ─── v0.18: Live Preview ───

/// Generate downscaled preview frames.
pub struct LivePreview {
    scale: f32,
    last_preview: Vec<u8>,
}

impl LivePreview {
    pub fn new(scale: f32) -> Self {
        LivePreview { scale: scale.clamp(0.1, 1.0), last_preview: Vec::new() }
    }

    /// Generate preview by sampling every Nth byte (simple downscale).
    pub fn generate(&mut self, frame: &[u8]) -> Vec<u8> {
        let step = (1.0 / self.scale).ceil() as usize;
        let preview: Vec<u8> = frame.iter().step_by(step.max(1)).copied().collect();
        self.last_preview = preview.clone();
        preview
    }

    pub fn preview_scale(&self) -> f32 { self.scale }
    pub fn last_preview_size(&self) -> usize { self.last_preview.len() }
}

// ─── v0.18: Stream Stats ───

/// Comprehensive stream statistics.
pub struct StreamStats {
    frames_in: u64,
    frames_out: u64,
    bytes_in: u64,
    bytes_out: u64,
    drops: u64,
}

impl StreamStats {
    pub fn new() -> Self {
        StreamStats { frames_in: 0, frames_out: 0, bytes_in: 0, bytes_out: 0, drops: 0 }
    }

    pub fn record_in(&mut self, bytes: usize) { self.frames_in += 1; self.bytes_in += bytes as u64; }
    pub fn record_out(&mut self, bytes: usize) { self.frames_out += 1; self.bytes_out += bytes as u64; }
    pub fn record_drop(&mut self) { self.drops += 1; }

    pub fn compression_ratio(&self) -> f64 {
        if self.bytes_in == 0 { return 1.0; }
        self.bytes_out as f64 / self.bytes_in as f64
    }

    pub fn to_json(&self) -> String {
        format!("{{\"frames_in\":{},\"frames_out\":{},\"bytes_in\":{},\"bytes_out\":{},\"drops\":{},\"ratio\":{:.3}}}",
            self.frames_in, self.frames_out, self.bytes_in, self.bytes_out, self.drops, self.compression_ratio())
    }
}

impl Default for StreamStats {
    fn default() -> Self { Self::new() }
}

// ─── v0.19: Audio Mixer ───

/// Mix multiple audio streams.
pub struct AudioMixer {
    sources: Vec<String>,
}

impl AudioMixer {
    pub fn new() -> Self { AudioMixer { sources: Vec::new() } }

    pub fn add_source(&mut self, id: &str) { self.sources.push(id.to_string()); }
    pub fn source_count(&self) -> usize { self.sources.len() }

    /// Mix two sample buffers by averaging.
    pub fn mix_sources(&self, a: &[i16], b: &[i16]) -> Vec<i16> {
        let len = a.len().max(b.len());
        let mut out = Vec::with_capacity(len);
        for i in 0..len {
            let sa = if i < a.len() { a[i] as i32 } else { 0 };
            let sb = if i < b.len() { b[i] as i32 } else { 0 };
            out.push(((sa + sb) / 2) as i16);
        }
        out
    }
}

impl Default for AudioMixer {
    fn default() -> Self { Self::new() }
}

// ─── v0.19: Session Store ───

/// Persist and restore stream state.
pub struct SessionStore {
    data: std::collections::HashMap<String, Vec<u8>>,
}

impl SessionStore {
    pub fn new() -> Self { SessionStore { data: std::collections::HashMap::new() } }

    pub fn save_state(&mut self, key: &str, data: &[u8]) {
        self.data.insert(key.to_string(), data.to_vec());
    }

    pub fn load_state(&self, key: &str) -> Option<Vec<u8>> {
        self.data.get(key).cloned()
    }

    pub fn list_keys(&self) -> Vec<String> {
        self.data.keys().cloned().collect()
    }

    pub fn clear(&mut self) { self.data.clear(); }
}

impl Default for SessionStore {
    fn default() -> Self { Self::new() }
}

// ─── v0.19: Frame Annotation ───

/// Attach key-value metadata to frames.
pub struct FrameAnnotation;

impl FrameAnnotation {
    /// Annotate frame: prepend [key_len:2][key][val_len:2][val] then original data.
    pub fn annotate(frame: &[u8], key: &str, value: &str) -> Vec<u8> {
        let kb = key.as_bytes();
        let vb = value.as_bytes();
        let mut out = Vec::with_capacity(4 + kb.len() + vb.len() + frame.len());
        out.extend_from_slice(&(kb.len() as u16).to_le_bytes());
        out.extend_from_slice(kb);
        out.extend_from_slice(&(vb.len() as u16).to_le_bytes());
        out.extend_from_slice(vb);
        out.extend_from_slice(frame);
        out
    }

    /// Read annotation from annotated frame.
    pub fn read_annotation(annotated: &[u8]) -> Option<(String, String, usize)> {
        if annotated.len() < 4 { return None; }
        let kl = u16::from_le_bytes([annotated[0], annotated[1]]) as usize;
        if annotated.len() < 2 + kl + 2 { return None; }
        let key = String::from_utf8_lossy(&annotated[2..2+kl]).to_string();
        let vl = u16::from_le_bytes([annotated[2+kl], annotated[3+kl]]) as usize;
        let offset = 4 + kl + vl;
        if annotated.len() < offset { return None; }
        let val = String::from_utf8_lossy(&annotated[4+kl..offset]).to_string();
        Some((key, val, offset))
    }
}

// ─── v0.20: Stream Recorder ───

/// Record and playback frame sequences.
pub struct StreamRecorder {
    recording: bool,
    frames: Vec<Vec<u8>>,
}

impl StreamRecorder {
    pub fn new() -> Self { StreamRecorder { recording: false, frames: Vec::new() } }
    pub fn start(&mut self) { self.recording = true; self.frames.clear(); }
    pub fn stop(&mut self) { self.recording = false; }
    pub fn is_recording(&self) -> bool { self.recording }

    pub fn push(&mut self, frame: &[u8]) {
        if self.recording { self.frames.push(frame.to_vec()); }
    }

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

    pub fn get_frame(&self, index: usize) -> Option<&[u8]> {
        self.frames.get(index).map(|f| f.as_slice())
    }
}

impl Default for StreamRecorder {
    fn default() -> Self { Self::new() }
}

// ─── v0.20: Hot Config ───

/// Runtime-mutable key-value configuration.
pub struct HotConfig {
    values: std::collections::HashMap<String, String>,
}

impl HotConfig {
    pub fn new() -> Self { HotConfig { values: std::collections::HashMap::new() } }
    pub fn set(&mut self, key: &str, val: &str) { self.values.insert(key.to_string(), val.to_string()); }
    pub fn get(&self, key: &str) -> Option<&str> { self.values.get(key).map(|s| s.as_str()) }
    pub fn keys(&self) -> Vec<String> { self.values.keys().cloned().collect() }
}

impl Default for HotConfig {
    fn default() -> Self { Self::new() }
}

// ─── v0.20: Frame Pool ───

/// Object pool for frame buffer reuse.
pub struct FramePool {
    pool: Vec<Vec<u8>>,
    buf_size: usize,
}

impl FramePool {
    pub fn new(capacity: usize, buf_size: usize) -> Self {
        let pool = (0..capacity).map(|_| vec![0u8; buf_size]).collect();
        FramePool { pool, buf_size }
    }

    pub fn alloc(&mut self) -> Option<Vec<u8>> { self.pool.pop() }

    pub fn release(&mut self, mut buf: Vec<u8>) {
        buf.resize(self.buf_size, 0);
        self.pool.push(buf);
    }

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

// ─── v0.21: Stream Mux ───

/// Multiplex named channels.
pub struct StreamMux {
    channels: std::collections::HashMap<String, Vec<Vec<u8>>>,
}

impl StreamMux {
    pub fn new() -> Self { StreamMux { channels: std::collections::HashMap::new() } }

    pub fn create_channel(&mut self, name: &str) {
        self.channels.entry(name.to_string()).or_insert_with(Vec::new);
    }

    pub fn push(&mut self, channel: &str, frame: &[u8]) {
        if let Some(ch) = self.channels.get_mut(channel) {
            ch.push(frame.to_vec());
        }
    }

    pub fn pull(&mut self, channel: &str) -> Option<Vec<u8>> {
        self.channels.get_mut(channel).and_then(|ch| if ch.is_empty() { None } else { Some(ch.remove(0)) })
    }

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

impl Default for StreamMux {
    fn default() -> Self { Self::new() }
}

// ─── v0.21: Remote Control ───

/// Queue commands for remote execution.
pub struct RemoteControl {
    commands: Vec<String>,
}

impl RemoteControl {
    pub fn new() -> Self { RemoteControl { commands: Vec::new() } }
    pub fn push_command(&mut self, cmd: &str) { self.commands.push(cmd.to_string()); }
    pub fn pop_command(&mut self) -> Option<String> { if self.commands.is_empty() { None } else { Some(self.commands.remove(0)) } }
    pub fn pending_count(&self) -> usize { self.commands.len() }
}

impl Default for RemoteControl {
    fn default() -> Self { Self::new() }
}

// ─── v0.21: Frame Hash ───

/// Simple frame integrity hash.
pub struct FrameHash;

impl FrameHash {
    /// Compute a fast hash (FNV-1a style) of frame bytes.
    pub fn hash(data: &[u8]) -> u32 {
        let mut h: u32 = 0x811c9dc5;
        for &b in data {
            h ^= b as u32;
            h = h.wrapping_mul(0x01000193);
        }
        h
    }

    /// Verify frame against expected hash.
    pub fn verify(data: &[u8], expected: u32) -> bool {
        Self::hash(data) == expected
    }
}

// ─── v0.22: Stream Compressor ───

/// Simple RLE-style byte compressor.
pub struct StreamCompressor;

impl StreamCompressor {
    /// Compress via simple RLE: [byte, count] pairs.
    pub fn compress(data: &[u8]) -> Vec<u8> {
        if data.is_empty() { return Vec::new(); }
        let mut out = Vec::new();
        let mut i = 0;
        while i < data.len() {
            let b = data[i];
            let mut count: u8 = 1;
            while i + (count as usize) < data.len() && data[i + count as usize] == b && count < 255 {
                count += 1;
            }
            out.push(b);
            out.push(count);
            i += count as usize;
        }
        out
    }

    /// Decompress RLE pairs back to bytes.
    pub fn decompress(data: &[u8]) -> Vec<u8> {
        let mut out = Vec::new();
        let mut i = 0;
        while i + 1 < data.len() {
            let b = data[i];
            let count = data[i + 1];
            for _ in 0..count { out.push(b); }
            i += 2;
        }
        out
    }
}

// ─── v0.22: Telemetry ───

/// Track stream telemetry metrics.
pub struct Telemetry {
    latencies: Vec<f64>,
    errors: u64,
    bytes_total: u64,
}

impl Telemetry {
    pub fn new() -> Self { Telemetry { latencies: Vec::new(), errors: 0, bytes_total: 0 } }
    pub fn record_latency(&mut self, ms: f64) { self.latencies.push(ms); }
    pub fn record_error(&mut self) { self.errors += 1; }
    pub fn record_bytes(&mut self, n: u64) { self.bytes_total += n; }

    pub fn avg_latency(&self) -> f64 {
        if self.latencies.is_empty() { 0.0 }
        else { self.latencies.iter().sum::<f64>() / self.latencies.len() as f64 }
    }

    pub fn error_count(&self) -> u64 { self.errors }
    pub fn total_bytes(&self) -> u64 { self.bytes_total }
}

impl Default for Telemetry {
    fn default() -> Self { Self::new() }
}

// ─── v0.22: Frame Ring ───

/// Fixed-size ring buffer for frames.
pub struct FrameRing {
    buf: Vec<Option<Vec<u8>>>,
    head: usize,
    len: usize,
}

impl FrameRing {
    pub fn new(capacity: usize) -> Self {
        FrameRing { buf: (0..capacity).map(|_| None).collect(), head: 0, len: 0 }
    }

    pub fn push(&mut self, frame: Vec<u8>) {
        let cap = self.buf.len();
        let idx = (self.head + self.len) % cap;
        self.buf[idx] = Some(frame);
        if self.len < cap { self.len += 1; } else { self.head = (self.head + 1) % cap; }
    }

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

// ─── v0.23: Frame Batcher ───

/// Batch N frames into one message.
pub struct FrameBatcher {
    batch_size: usize,
    buffer: Vec<Vec<u8>>,
}

impl FrameBatcher {
    pub fn new(batch_size: usize) -> Self { FrameBatcher { batch_size, buffer: Vec::new() } }

    /// Push a frame. Returns Some(batch) when batch_size reached.
    pub fn push(&mut self, frame: Vec<u8>) -> Option<Vec<Vec<u8>>> {
        self.buffer.push(frame);
        if self.buffer.len() >= self.batch_size {
            Some(std::mem::take(&mut self.buffer))
        } else { None }
    }

    /// Flush remaining frames.
    pub fn flush(&mut self) -> Vec<Vec<u8>> { std::mem::take(&mut self.buffer) }
    pub fn pending(&self) -> usize { self.buffer.len() }
}

// ─── v0.23: Diagnostic Log ───

/// Structured event log.
pub struct DiagnosticLog {
    events: Vec<(u64, String)>, // (timestamp_ms, message)
    counter: u64,
}

impl DiagnosticLog {
    pub fn new() -> Self { DiagnosticLog { events: Vec::new(), counter: 0 } }
    pub fn log(&mut self, msg: &str) { self.events.push((self.counter, msg.to_string())); self.counter += 1; }
    pub fn count(&self) -> usize { self.events.len() }
    pub fn get(&self, idx: usize) -> Option<&str> { self.events.get(idx).map(|(_, m)| m.as_str()) }
    pub fn clear(&mut self) { self.events.clear(); }
}

impl Default for DiagnosticLog {
    fn default() -> Self { Self::new() }
}

// ─── v0.23: Stream Gate ───

/// Enable/disable frame forwarding.
pub struct StreamGate {
    open: bool,
}

impl StreamGate {
    pub fn new(open: bool) -> Self { StreamGate { open } }
    pub fn is_open(&self) -> bool { self.open }
    pub fn open(&mut self) { self.open = true; }
    pub fn close(&mut self) { self.open = false; }
    /// Pass frame through if gate is open, else drop.
    pub fn filter(&self, frame: &[u8]) -> Option<Vec<u8>> {
        if self.open { Some(frame.to_vec()) } else { None }
    }
}

// ─── v0.24: Priority Queue ───

/// Priority-ordered frame queue (lower = higher priority).
pub struct FramePriorityQueue {
    items: Vec<(u8, Vec<u8>)>,
}

impl FramePriorityQueue {
    pub fn new() -> Self { FramePriorityQueue { items: Vec::new() } }

    pub fn enqueue(&mut self, priority: u8, data: Vec<u8>) {
        let pos = self.items.iter().position(|(p, _)| *p > priority).unwrap_or(self.items.len());
        self.items.insert(pos, (priority, data));
    }

    pub fn dequeue(&mut self) -> Option<Vec<u8>> {
        if self.items.is_empty() { None } else { Some(self.items.remove(0).1) }
    }

    pub fn len(&self) -> usize { self.items.len() }
    pub fn is_empty(&self) -> bool { self.items.is_empty() }
}

impl Default for FramePriorityQueue {
    fn default() -> Self { Self::new() }
}

// ─── v0.24: Frame Watermark ───

/// Stamp frames with metadata.
pub struct FrameWatermark {
    tag: String,
    seq: u64,
}

impl FrameWatermark {
    pub fn new(tag: &str) -> Self { FrameWatermark { tag: tag.to_string(), seq: 0 } }

    /// Prepend 8-byte header: [tag_len(1), tag(N), seq_hi(1), seq_lo(1)] to frame.
    pub fn stamp(&mut self, frame: &[u8]) -> Vec<u8> {
        let mut out = Vec::new();
        let tag_bytes = self.tag.as_bytes();
        out.push(tag_bytes.len() as u8);
        out.extend_from_slice(tag_bytes);
        out.push((self.seq >> 8) as u8);
        out.push(self.seq as u8);
        out.extend_from_slice(frame);
        self.seq += 1;
        out
    }

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

// ─── v0.24: Stream Meter ───

/// Measure frame rate over a window.
pub struct StreamMeter {
    window: Vec<f64>,
    max_window: usize,
}

impl StreamMeter {
    pub fn new(window_size: usize) -> Self { StreamMeter { window: Vec::new(), max_window: window_size } }
    pub fn tick(&mut self, timestamp: f64) {
        self.window.push(timestamp);
        if self.window.len() > self.max_window { self.window.remove(0); }
    }
    pub fn fps(&self) -> f64 {
        if self.window.len() < 2 { return 0.0; }
        let dt = self.window.last().unwrap() - self.window.first().unwrap();
        if dt <= 0.0 { 0.0 } else { (self.window.len() - 1) as f64 / dt }
    }
    pub fn sample_count(&self) -> usize { self.window.len() }
}

// ─── v0.25: Replay Buffer ───

/// Record and replay frames.
pub struct FrameReplayBuffer {
    frames: Vec<Vec<u8>>,
    max_frames: usize,
    cursor: usize,
}

impl FrameReplayBuffer {
    pub fn new(max: usize) -> Self { FrameReplayBuffer { frames: Vec::new(), max_frames: max, cursor: 0 } }
    pub fn record(&mut self, frame: Vec<u8>) {
        if self.frames.len() >= self.max_frames { self.frames.remove(0); }
        self.frames.push(frame);
    }
    pub fn replay_next(&mut self) -> Option<&[u8]> {
        if self.frames.is_empty() { return None; }
        let f = &self.frames[self.cursor % self.frames.len()];
        self.cursor = (self.cursor + 1) % self.frames.len();
        Some(f)
    }
    pub fn frame_count(&self) -> usize { self.frames.len() }
    pub fn reset_cursor(&mut self) { self.cursor = 0; }
}

// ─── v0.25: Frame Diff ───

/// Compute XOR diff between consecutive frames.
pub struct FrameDiff;

impl FrameDiff {
    /// XOR two frames. Output length = min(a.len(), b.len()).
    pub fn diff(a: &[u8], b: &[u8]) -> Vec<u8> {
        a.iter().zip(b.iter()).map(|(x, y)| x ^ y).collect()
    }

    /// Apply XOR diff to a base frame.
    pub fn apply(base: &[u8], diff: &[u8]) -> Vec<u8> {
        Self::diff(base, diff) // XOR is its own inverse
    }
}

// ─── v0.25: Stream Throttle ───

/// Rate-limit frames by dropping excess.
pub struct StreamThrottle {
    interval_ms: f64,
    last_pass: f64,
}

impl StreamThrottle {
    pub fn new(max_fps: f64) -> Self {
        StreamThrottle { interval_ms: 1000.0 / max_fps, last_pass: f64::NEG_INFINITY }
    }
    /// Returns true if frame should pass, false if dropped.
    pub fn should_pass(&mut self, now_ms: f64) -> bool {
        if now_ms - self.last_pass >= self.interval_ms {
            self.last_pass = now_ms;
            true
        } else { false }
    }
}

// ─── v0.26: Chunked Transfer ───

/// Split large frames into fixed-size chunks.
pub struct ChunkedTransfer {
    chunk_size: usize,
}

impl ChunkedTransfer {
    pub fn new(chunk_size: usize) -> Self { ChunkedTransfer { chunk_size } }

    pub fn split(&self, frame: &[u8]) -> Vec<Vec<u8>> {
        frame.chunks(self.chunk_size).map(|c| c.to_vec()).collect()
    }

    pub fn merge(chunks: &[Vec<u8>]) -> Vec<u8> {
        chunks.iter().flat_map(|c| c.iter().copied()).collect()
    }
}

// ─── v0.26: Frame Annotation ───

/// Attach metadata to frames.
pub struct MetadataAnnotation {
    annotations: Vec<(String, String)>,
}

impl MetadataAnnotation {
    pub fn new() -> Self { MetadataAnnotation { annotations: Vec::new() } }
    pub fn set(&mut self, key: &str, value: &str) {
        if let Some(a) = self.annotations.iter_mut().find(|(k, _)| k == key) {
            a.1 = value.to_string();
        } else {
            self.annotations.push((key.to_string(), value.to_string()));
        }
    }
    pub fn get(&self, key: &str) -> Option<&str> {
        self.annotations.iter().find(|(k, _)| k == key).map(|(_, v)| v.as_str())
    }
    pub fn count(&self) -> usize { self.annotations.len() }
}

impl Default for MetadataAnnotation {
    fn default() -> Self { Self::new() }
}

// ─── v0.26: Stream Stats ───

/// Accumulate stream statistics.
pub struct StreamMetrics {
    pub frames_sent: u64,
    pub bytes_sent: u64,
    pub frames_dropped: u64,
}

impl StreamMetrics {
    pub fn new() -> Self { StreamMetrics { frames_sent: 0, bytes_sent: 0, frames_dropped: 0 } }
    pub fn record_sent(&mut self, bytes: usize) { self.frames_sent += 1; self.bytes_sent += bytes as u64; }
    pub fn record_drop(&mut self) { self.frames_dropped += 1; }
    pub fn drop_rate(&self) -> f64 {
        let total = self.frames_sent + self.frames_dropped;
        if total == 0 { 0.0 } else { self.frames_dropped as f64 / total as f64 }
    }
}

impl Default for StreamMetrics {
    fn default() -> Self { Self::new() }
}

// ─── v0.27: Double Buffer ───

/// Front/back buffer swap for lock-free frame delivery.
pub struct DoubleBuffer {
    front: Vec<u8>,
    back: Vec<u8>,
}

impl DoubleBuffer {
    pub fn new() -> Self { DoubleBuffer { front: Vec::new(), back: Vec::new() } }
    pub fn write(&mut self, data: Vec<u8>) { self.back = data; }
    pub fn swap(&mut self) { std::mem::swap(&mut self.front, &mut self.back); }
    pub fn read(&self) -> &[u8] { &self.front }
    pub fn back_len(&self) -> usize { self.back.len() }
}

impl Default for DoubleBuffer {
    fn default() -> Self { Self::new() }
}

// ─── v0.27: Frame Sequencer ───

/// Assign monotonic sequence numbers to frames.
pub struct FrameSequencer {
    next_seq: u64,
}

impl FrameSequencer {
    pub fn new() -> Self { FrameSequencer { next_seq: 0 } }
    pub fn next(&mut self) -> u64 { let s = self.next_seq; self.next_seq += 1; s }
    pub fn current(&self) -> u64 { self.next_seq }
    pub fn reset(&mut self) { self.next_seq = 0; }
}

impl Default for FrameSequencer {
    fn default() -> Self { Self::new() }
}

// ─── v0.27: Bandwidth Estimator ───

/// Estimate bandwidth from recent frame sizes and timestamps.
pub struct ThroughputEstimator {
    samples: Vec<(f64, usize)>, // (timestamp_ms, bytes)
    max_samples: usize,
}

impl ThroughputEstimator {
    pub fn new(window: usize) -> Self { ThroughputEstimator { samples: Vec::new(), max_samples: window } }
    pub fn record(&mut self, timestamp_ms: f64, bytes: usize) {
        self.samples.push((timestamp_ms, bytes));
        if self.samples.len() > self.max_samples { self.samples.remove(0); }
    }
    /// Bytes per second estimate.
    pub fn estimate_bps(&self) -> f64 {
        if self.samples.len() < 2 { return 0.0; }
        let dt = self.samples.last().unwrap().0 - self.samples.first().unwrap().0;
        if dt <= 0.0 { return 0.0; }
        let total_bytes: usize = self.samples.iter().map(|(_, b)| b).sum();
        total_bytes as f64 / (dt / 1000.0)
    }
}

// ─── v0.28: Frame Pool ───

/// Pre-allocated frame pool to reduce allocations.
pub struct BufferPool {
    pool: Vec<Vec<u8>>,
    frame_capacity: usize,
}

impl BufferPool {
    pub fn new(count: usize, capacity: usize) -> Self {
        let pool = (0..count).map(|_| Vec::with_capacity(capacity)).collect();
        BufferPool { pool, frame_capacity: capacity }
    }
    pub fn acquire(&mut self) -> Vec<u8> {
        self.pool.pop().unwrap_or_else(|| Vec::with_capacity(self.frame_capacity))
    }
    pub fn release(&mut self, mut frame: Vec<u8>) {
        frame.clear();
        self.pool.push(frame);
    }
    pub fn available(&self) -> usize { self.pool.len() }
}

// ─── v0.28: Jitter Buffer ───

/// Buffer frames to smooth out timing jitter.
pub struct PacketJitterBuffer {
    buffer: Vec<(u64, Vec<u8>)>,
    next_expected: u64,
}

impl PacketJitterBuffer {
    pub fn new() -> Self { PacketJitterBuffer { buffer: Vec::new(), next_expected: 0 } }
    pub fn insert(&mut self, seq: u64, data: Vec<u8>) {
        let pos = self.buffer.iter().position(|(s, _)| *s > seq).unwrap_or(self.buffer.len());
        self.buffer.insert(pos, (seq, data));
    }
    pub fn try_pop(&mut self) -> Option<Vec<u8>> {
        if let Some((seq, _)) = self.buffer.first() {
            if *seq == self.next_expected {
                self.next_expected += 1;
                return Some(self.buffer.remove(0).1);
            }
        }
        None
    }
    pub fn pending(&self) -> usize { self.buffer.len() }
}

impl Default for PacketJitterBuffer {
    fn default() -> Self { Self::new() }
}

// ─── v0.28: Latency Tracker ───

/// Track round-trip latency samples.
pub struct RttTracker {
    samples: Vec<f64>,
    max_samples: usize,
}

impl RttTracker {
    pub fn new(window: usize) -> Self { RttTracker { samples: Vec::new(), max_samples: window } }
    pub fn record(&mut self, latency_ms: f64) {
        self.samples.push(latency_ms);
        if self.samples.len() > self.max_samples { self.samples.remove(0); }
    }
    pub fn average(&self) -> f64 {
        if self.samples.is_empty() { 0.0 } else { self.samples.iter().sum::<f64>() / self.samples.len() as f64 }
    }
    pub fn max_latency(&self) -> f64 {
        self.samples.iter().copied().fold(0.0f64, f64::max)
    }
}

// ─── v0.30: Frame Ring Buffer ───

/// Fixed-size ring buffer for frames.
pub struct FrameRingBuffer {
    buffer: Vec<Option<Vec<u8>>>,
    head: usize,
    len: usize,
}

impl FrameRingBuffer {
    pub fn new(capacity: usize) -> Self {
        FrameRingBuffer { buffer: vec![None; capacity], head: 0, len: 0 }
    }
    pub fn push(&mut self, frame: Vec<u8>) {
        self.buffer[self.head] = Some(frame);
        self.head = (self.head + 1) % self.buffer.len();
        if self.len < self.buffer.len() { self.len += 1; }
    }
    pub fn latest(&self) -> Option<&[u8]> {
        if self.len == 0 { return None; }
        let idx = if self.head == 0 { self.buffer.len() - 1 } else { self.head - 1 };
        self.buffer[idx].as_deref()
    }
    pub fn occupancy(&self) -> usize { self.len }
}

// ─── v0.30: Packet Loss Detector ───

/// Detect packet loss from sequence gaps.
pub struct PacketLossDetector {
    expected: u64,
    lost: u64,
    received: u64,
}

impl PacketLossDetector {
    pub fn new() -> Self { PacketLossDetector { expected: 0, lost: 0, received: 0 } }
    pub fn observe(&mut self, seq: u64) {
        if seq > self.expected {
            self.lost += seq - self.expected;
        }
        self.received += 1;
        self.expected = seq + 1;
    }
    pub fn loss_ratio(&self) -> f64 {
        let total = self.received + self.lost;
        if total == 0 { 0.0 } else { self.lost as f64 / total as f64 }
    }
    pub fn total_lost(&self) -> u64 { self.lost }
}

impl Default for PacketLossDetector {
    fn default() -> Self { Self::new() }
}

// ─── v0.30: Connection Quality ───

/// Aggregate connection quality from latency and loss.
pub struct ConnectionQuality {
    pub latency_avg: f64,
    pub loss_ratio: f64,
}

impl ConnectionQuality {
    pub fn new(latency: f64, loss: f64) -> Self {
        ConnectionQuality { latency_avg: latency, loss_ratio: loss }
    }
    /// Quality score 0.0 (worst) to 1.0 (best).
    pub fn score(&self) -> f64 {
        let lat_score = (1.0 - (self.latency_avg / 500.0).min(1.0)).max(0.0);
        let loss_score = (1.0 - self.loss_ratio * 10.0).max(0.0);
        (lat_score + loss_score) / 2.0
    }
}

// ─── v0.40: Adaptive Bitrate ───

/// Auto-adjust quality level based on connection quality score.
pub struct QualityAdapter {
    levels: Vec<u32>,
    current: usize,
}

impl QualityAdapter {
    pub fn new(levels: Vec<u32>) -> Self { QualityAdapter { current: levels.len() - 1, levels } }
    /// Given a quality score (0.0-1.0), pick the right bitrate level.
    pub fn adjust(&mut self, quality: f64) -> u32 {
        self.current = ((quality * (self.levels.len() - 1) as f64).round() as usize).min(self.levels.len() - 1);
        self.levels[self.current]
    }
    pub fn current_level(&self) -> u32 { self.levels[self.current] }
}

// ─── v0.40: Source Mixer ───

/// Merge frames from multiple named sources.
pub struct SourceMixer {
    sources: Vec<(String, Vec<u8>)>,
}

impl SourceMixer {
    pub fn new() -> Self { SourceMixer { sources: Vec::new() } }
    pub fn add(&mut self, id: &str, data: Vec<u8>) {
        if let Some(s) = self.sources.iter_mut().find(|(k, _)| k == id) {
            s.1 = data;
        } else {
            self.sources.push((id.to_string(), data));
        }
    }
    pub fn output(&self) -> Vec<u8> {
        let mut out = Vec::new();
        for (_, data) in &self.sources { out.extend_from_slice(data); }
        out
    }
    pub fn source_count(&self) -> usize { self.sources.len() }
}

impl Default for SourceMixer {
    fn default() -> Self { Self::new() }
}

// ─── v0.40: Frame Deduplicator ───

/// Skip identical consecutive frames using hash comparison.
pub struct FrameDeduplicator {
    last_hash: u64,
    skipped: u64,
}

impl FrameDeduplicator {
    pub fn new() -> Self { FrameDeduplicator { last_hash: 0, skipped: 0 } }
    fn hash(data: &[u8]) -> u64 {
        let mut h: u64 = 5381;
        for &b in data { h = h.wrapping_mul(33).wrapping_add(b as u64); }
        h
    }
    /// Returns true if frame is unique (should send), false if duplicate.
    pub fn check(&mut self, data: &[u8]) -> bool {
        let h = Self::hash(data);
        if h == self.last_hash { self.skipped += 1; false } else { self.last_hash = h; true }
    }
    pub fn skipped_count(&self) -> u64 { self.skipped }
}

impl Default for FrameDeduplicator {
    fn default() -> Self { Self::new() }
}

// ─── v0.40: Backpressure Controller ───

/// Signal backpressure when queue exceeds threshold.
pub struct BackpressureController {
    threshold: usize,
}

impl BackpressureController {
    pub fn new(threshold: usize) -> Self { BackpressureController { threshold } }
    /// Returns true if sender should pause (queue too deep).
    pub fn should_pause(&self, queue_depth: usize) -> bool { queue_depth >= self.threshold }
    pub fn threshold(&self) -> usize { self.threshold }
}

// ─── v0.40: Heartbeat Monitor ───

/// Monitor connection liveness.
pub struct HeartbeatMonitor {
    interval_ms: f64,
    last_ping: f64,
    alive: bool,
}

impl HeartbeatMonitor {
    pub fn new(interval_ms: f64) -> Self {
        HeartbeatMonitor { interval_ms, last_ping: 0.0, alive: true }
    }
    /// Tick with current time. Returns true if connection is still alive.
    pub fn tick(&mut self, now_ms: f64) -> bool {
        if now_ms - self.last_ping > self.interval_ms * 3.0 { self.alive = false; }
        self.alive
    }
    pub fn ping(&mut self, now_ms: f64) { self.last_ping = now_ms; self.alive = true; }
    pub fn is_alive(&self) -> bool { self.alive }
}

// ─── v0.40: Compression Tracker ───

/// Track compression ratios.
pub struct CompressionTracker {
    total_raw: u64,
    total_compressed: u64,
}

impl CompressionTracker {
    pub fn new() -> Self { CompressionTracker { total_raw: 0, total_compressed: 0 } }
    pub fn record(&mut self, raw: usize, compressed: usize) {
        self.total_raw += raw as u64;
        self.total_compressed += compressed as u64;
    }
    pub fn ratio(&self) -> f64 {
        if self.total_raw == 0 { 1.0 } else { self.total_compressed as f64 / self.total_raw as f64 }
    }
}

impl Default for CompressionTracker {
    fn default() -> Self { Self::new() }
}

// ─── v0.40: FEC Encoder ───

/// Simple XOR-based forward error correction.
pub struct FecEncoder {
    group_size: usize,
}

impl FecEncoder {
    pub fn new(group_size: usize) -> Self { FecEncoder { group_size: group_size.max(2) } }
    /// Generate parity byte from a group of packets (XOR all).
    pub fn parity(packets: &[&[u8]]) -> Vec<u8> {
        if packets.is_empty() { return Vec::new(); }
        let max_len = packets.iter().map(|p| p.len()).max().unwrap_or(0);
        let mut parity = vec![0u8; max_len];
        for packet in packets {
            for (i, &b) in packet.iter().enumerate() { parity[i] ^= b; }
        }
        parity
    }
    pub fn group_size(&self) -> usize { self.group_size }
}

// ─── v0.40: Stream Snapshot ───

/// Capture stream state for debugging.
pub struct StreamSnapshot {
    pub frames_sent: u64,
    pub bytes_sent: u64,
    pub loss_ratio: f64,
    pub quality_score: f64,
}

impl StreamSnapshot {
    pub fn new(sent: u64, bytes: u64, loss: f64, quality: f64) -> Self {
        StreamSnapshot { frames_sent: sent, bytes_sent: bytes, loss_ratio: loss, quality_score: quality }
    }
    pub fn to_json(&self) -> String {
        format!("{{\"frames\":{},\"bytes\":{},\"loss\":{:.4},\"quality\":{:.4}}}",
            self.frames_sent, self.bytes_sent, self.loss_ratio, self.quality_score)
    }
}

// ─── v0.40: Adaptive Priority Queue ───

/// Priority queue with deadline-based eviction.
pub struct AdaptivePriorityQueue {
    items: Vec<(u8, f64, Vec<u8>)>, // (priority, deadline_ms, data)
}

impl AdaptivePriorityQueue {
    pub fn new() -> Self { AdaptivePriorityQueue { items: Vec::new() } }
    pub fn push(&mut self, priority: u8, deadline_ms: f64, data: Vec<u8>) {
        self.items.push((priority, deadline_ms, data));
        self.items.sort_by(|a, b| b.0.cmp(&a.0)); // higher priority first
    }
    /// Pop highest-priority non-expired item.
    pub fn pop(&mut self, now_ms: f64) -> Option<Vec<u8>> {
        // Remove expired
        self.items.retain(|(_, d, _)| *d > now_ms);
        if self.items.is_empty() { None } else { Some(self.items.remove(0).2) }
    }
    pub fn len(&self) -> usize { self.items.len() }
    pub fn is_empty(&self) -> bool { self.items.is_empty() }
}

impl Default for AdaptivePriorityQueue {
    fn default() -> Self { Self::new() }
}

// ─── v0.50: Stream Cipher ───

/// XOR cipher with rotating key.
pub struct StreamCipher { key: Vec<u8> }

impl StreamCipher {
    pub fn new(key: Vec<u8>) -> Self { StreamCipher { key: if key.is_empty() { vec![0x42] } else { key } } }
    pub fn encrypt(&self, data: &[u8]) -> Vec<u8> {
        data.iter().enumerate().map(|(i, b)| b ^ self.key[i % self.key.len()]).collect()
    }
    pub fn decrypt(&self, data: &[u8]) -> Vec<u8> { self.encrypt(data) } // XOR is symmetric
}

// ─── v0.50: Channel Mux ───

/// Multiplex N logical channels.
pub struct ChannelMux { channels: Vec<(u8, Vec<u8>)> }

impl ChannelMux {
    pub fn new() -> Self { ChannelMux { channels: Vec::new() } }
    pub fn send(&mut self, channel: u8, data: Vec<u8>) { self.channels.push((channel, data)); }
    /// Pack all pending into [ch, len_hi, len_lo, ...data] frames.
    pub fn pack(&mut self) -> Vec<u8> {
        let mut out = Vec::new();
        for (ch, data) in self.channels.drain(..) {
            out.push(ch);
            out.push((data.len() >> 8) as u8);
            out.push(data.len() as u8);
            out.extend_from_slice(&data);
        }
        out
    }
}

impl Default for ChannelMux { fn default() -> Self { Self::new() } }

// ─── v0.50: Channel Demux ───

/// Demultiplex packed stream.
pub struct ChannelDemux;

impl ChannelDemux {
    pub fn unpack(data: &[u8]) -> Vec<(u8, Vec<u8>)> {
        let mut result = Vec::new();
        let mut i = 0;
        while i + 3 <= data.len() {
            let ch = data[i];
            let len = ((data[i + 1] as usize) << 8) | data[i + 2] as usize;
            i += 3;
            if i + len <= data.len() {
                result.push((ch, data[i..i + len].to_vec()));
                i += len;
            } else { break; }
        }
        result
    }
}

// ─── v0.50: Frame Pacer ───

/// Pace frames at target interval.
pub struct FramePacer { interval_ms: f64, last_emit: f64 }

impl FramePacer {
    pub fn new(interval_ms: f64) -> Self { FramePacer { interval_ms, last_emit: f64::NEG_INFINITY } }
    pub fn should_emit(&mut self, now_ms: f64) -> bool {
        if now_ms - self.last_emit >= self.interval_ms { self.last_emit = now_ms; true } else { false }
    }
}

// ─── v0.50: Congestion Window ───

/// AIMD congestion control.
pub struct CongestionWindow { cwnd: f64, min: f64, max: f64 }

impl CongestionWindow {
    pub fn new(initial: f64, min: f64, max: f64) -> Self { CongestionWindow { cwnd: initial, min, max } }
    pub fn on_ack(&mut self) { self.cwnd = (self.cwnd + 1.0).min(self.max); }
    pub fn on_loss(&mut self) { self.cwnd = (self.cwnd / 2.0).max(self.min); }
    pub fn window(&self) -> f64 { self.cwnd }
}

// ─── v0.50: Flow Controller ───

/// Token-bucket flow control.
pub struct FlowController { tokens: f64, max_tokens: f64, refill_rate: f64, last_refill: f64 }

impl FlowController {
    pub fn new(max: f64, rate: f64) -> Self { FlowController { tokens: max, max_tokens: max, refill_rate: rate, last_refill: 0.0 } }
    pub fn consume(&mut self, bytes: f64, now_ms: f64) -> bool {
        let elapsed = (now_ms - self.last_refill) / 1000.0;
        self.tokens = (self.tokens + elapsed * self.refill_rate).min(self.max_tokens);
        self.last_refill = now_ms;
        if self.tokens >= bytes { self.tokens -= bytes; true } else { false }
    }
    pub fn available(&self) -> f64 { self.tokens }
}

// ─── v0.50: Protocol Negotiator ───

/// Capability exchange.
pub struct ProtocolNegotiator { capabilities: Vec<String> }

impl ProtocolNegotiator {
    pub fn new(caps: Vec<String>) -> Self { ProtocolNegotiator { capabilities: caps } }
    pub fn negotiate(&self, remote: &[String]) -> Vec<String> {
        self.capabilities.iter().filter(|c| remote.contains(c)).cloned().collect()
    }
}

// ─── v0.50: Replay Recorder ───

/// Record frames for later replay.
pub struct ReplayRecorder { frames: Vec<(f64, Vec<u8>)> }

impl ReplayRecorder {
    pub fn new() -> Self { ReplayRecorder { frames: Vec::new() } }
    pub fn record(&mut self, timestamp: f64, data: Vec<u8>) { self.frames.push((timestamp, data)); }
    pub fn frame_count(&self) -> usize { self.frames.len() }
    pub fn get_frame(&self, idx: usize) -> Option<&[u8]> { self.frames.get(idx).map(|(_, d)| d.as_slice()) }
}

impl Default for ReplayRecorder { fn default() -> Self { Self::new() } }

// ─── v0.50: Bandwidth Shaper ───

/// Enforce max bytes/sec.
pub struct BandwidthShaper { max_bps: f64, sent_this_window: f64, window_start: f64 }

impl BandwidthShaper {
    pub fn new(max_bps: f64) -> Self { BandwidthShaper { max_bps, sent_this_window: 0.0, window_start: 0.0 } }
    pub fn allow(&mut self, bytes: f64, now_ms: f64) -> bool {
        if now_ms - self.window_start >= 1000.0 { self.window_start = now_ms; self.sent_this_window = 0.0; }
        if self.sent_this_window + bytes <= self.max_bps { self.sent_this_window += bytes; true } else { false }
    }
}

// ─── v0.60: CRC32 Checker ───

pub struct Crc32Checker;

impl Crc32Checker {
    pub fn checksum(data: &[u8]) -> u32 {
        let mut crc: u32 = 0xFFFFFFFF;
        for &b in data {
            crc ^= b as u32;
            for _ in 0..8 { crc = if crc & 1 != 0 { (crc >> 1) ^ 0xEDB88320 } else { crc >> 1 }; }
        }
        !crc
    }
    pub fn verify(data: &[u8], expected: u32) -> bool { Self::checksum(data) == expected }
}

// ─── v0.60: Connection State ───

#[derive(Debug, Clone, PartialEq)]
pub enum ConnState { Connecting, Connected, Disconnected, Reconnecting }

pub struct ConnectionStateMachine { state: ConnState, retries: u32 }

impl ConnectionStateMachine {
    pub fn new() -> Self { ConnectionStateMachine { state: ConnState::Connecting, retries: 0 } }
    pub fn connect(&mut self) { self.state = ConnState::Connected; self.retries = 0; }
    pub fn disconnect(&mut self) { self.state = ConnState::Disconnected; }
    pub fn reconnect(&mut self) { self.state = ConnState::Reconnecting; self.retries += 1; }
    pub fn state(&self) -> &ConnState { &self.state }
    pub fn retries(&self) -> u32 { self.retries }
}

impl Default for ConnectionStateMachine { fn default() -> Self { Self::new() } }

// ─── v0.60: Packet Sequencer ───

pub struct PacketSequencer { next: u64, gaps: Vec<u64> }

impl PacketSequencer {
    pub fn new() -> Self { PacketSequencer { next: 0, gaps: Vec::new() } }
    pub fn next_seq(&mut self) -> u64 { let s = self.next; self.next += 1; s }
    pub fn receive(&mut self, seq: u64) {
        if seq > self.next { for g in self.next..seq { self.gaps.push(g); } self.next = seq + 1; }
    }
    pub fn gap_count(&self) -> usize { self.gaps.len() }
}

impl Default for PacketSequencer { fn default() -> Self { Self::new() } }

// ─── v0.60: Stream Resumer ───

pub struct StreamResumer { last_good: u64 }

impl StreamResumer {
    pub fn new() -> Self { StreamResumer { last_good: 0 } }
    pub fn mark(&mut self, pos: u64) { self.last_good = pos; }
    pub fn resume_point(&self) -> u64 { self.last_good }
}

// ─── v0.60: Frame Interpolator ───

pub struct FrameInterpolator;

impl FrameInterpolator {
    pub fn lerp(a: f64, b: f64, t: f64) -> f64 { a + (b - a) * t.clamp(0.0, 1.0) }
    pub fn lerp_bytes(a: &[u8], b: &[u8], t: f64) -> Vec<u8> {
        let len = a.len().min(b.len());
        (0..len).map(|i| Self::lerp(a[i] as f64, b[i] as f64, t) as u8).collect()
    }
}

// ─── v0.60: Error Recovery ───

pub struct ErrorRecovery { max_retries: u32 }

impl ErrorRecovery {
    pub fn new(max: u32) -> Self { ErrorRecovery { max_retries: max } }
    /// Exponential backoff delay in ms.
    pub fn delay_ms(&self, attempt: u32) -> f64 {
        if attempt >= self.max_retries { return -1.0; }
        (100.0 * 2.0_f64.powi(attempt as i32)).min(30000.0)
    }
}

// ─── v0.60: Peer Router ───

pub struct PeerRouter { peers: Vec<(String, Vec<Vec<u8>>)> }

impl PeerRouter {
    pub fn new() -> Self { PeerRouter { peers: Vec::new() } }
    pub fn send(&mut self, peer: &str, data: Vec<u8>) {
        if let Some(p) = self.peers.iter_mut().find(|(n, _)| n == peer) { p.1.push(data); }
        else { self.peers.push((peer.to_string(), vec![data])); }
    }
    pub fn recv(&mut self, peer: &str) -> Option<Vec<u8>> {
        self.peers.iter_mut().find(|(n, _)| n == peer).and_then(|(_, q)| if q.is_empty() { None } else { Some(q.remove(0)) })
    }
    pub fn peer_count(&self) -> usize { self.peers.len() }
}

impl Default for PeerRouter { fn default() -> Self { Self::new() } }

// ─── v0.60: Stats Aggregator ───

pub struct StatsAggregator { values: Vec<f64>, window: usize }

impl StatsAggregator {
    pub fn new(window: usize) -> Self { StatsAggregator { values: Vec::new(), window } }
    pub fn record(&mut self, val: f64) {
        self.values.push(val);
        if self.values.len() > self.window { self.values.remove(0); }
    }
    pub fn avg(&self) -> f64 { if self.values.is_empty() { 0.0 } else { self.values.iter().sum::<f64>() / self.values.len() as f64 } }
    pub fn min(&self) -> f64 { self.values.iter().cloned().fold(f64::INFINITY, f64::min) }
    pub fn max(&self) -> f64 { self.values.iter().cloned().fold(f64::NEG_INFINITY, f64::max) }
}

// ─── v0.60: Packet Compressor (RLE) ───

pub struct PacketCompressor;

impl PacketCompressor {
    pub fn compress(data: &[u8]) -> Vec<u8> {
        if data.is_empty() { return Vec::new(); }
        let mut out = Vec::new();
        let mut i = 0;
        while i < data.len() {
            let val = data[i];
            let mut count = 1u8;
            while i + (count as usize) < data.len() && data[i + (count as usize)] == val && count < 255 { count += 1; }
            out.push(count);
            out.push(val);
            i += count as usize;
        }
        out
    }
    pub fn decompress(data: &[u8]) -> Vec<u8> {
        let mut out = Vec::new();
        let mut i = 0;
        while i + 1 < data.len() { for _ in 0..data[i] { out.push(data[i + 1]); } i += 2; }
        out
    }
}

// ─── v0.70: Protocol Version ───

pub struct ProtocolVersion { pub major: u16, pub minor: u16, pub patch: u16 }

impl ProtocolVersion {
    pub fn new(major: u16, minor: u16, patch: u16) -> Self { ProtocolVersion { major, minor, patch } }
    pub fn compatible(&self, other: &ProtocolVersion) -> bool { self.major == other.major }
    pub fn to_string_v(&self) -> String { format!("{}.{}.{}", self.major, self.minor, self.patch) }
}

// ─── v0.70: QoS Level ───

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum QosLevel { Background = 0, Low = 1, Normal = 2, High = 3, Realtime = 4 }

impl QosLevel {
    pub fn from_u8(v: u8) -> Self { match v { 0 => Self::Background, 1 => Self::Low, 3 => Self::High, 4 => Self::Realtime, _ => Self::Normal } }
    pub fn priority(&self) -> u8 { *self as u8 }
}

// ─── v0.70: Frame Signer ───

pub struct FrameSigner { key: Vec<u8> }

impl FrameSigner {
    pub fn new(key: Vec<u8>) -> Self { FrameSigner { key } }
    pub fn sign(&self, data: &[u8]) -> Vec<u8> {
        let mut hash: u32 = 0x811c9dc5;
        for &b in self.key.iter().chain(data.iter()) { hash ^= b as u32; hash = hash.wrapping_mul(0x01000193); }
        hash.to_le_bytes().to_vec()
    }
    pub fn verify(&self, data: &[u8], sig: &[u8]) -> bool { self.sign(data) == sig }
}

// ─── v0.70: Rate Limiter ───

pub struct RateLimiter { max_per_sec: f64, count: f64, window_start: f64 }

impl RateLimiter {
    pub fn new(max: f64) -> Self { RateLimiter { max_per_sec: max, count: 0.0, window_start: 0.0 } }
    pub fn check(&mut self, now_ms: f64) -> bool {
        if now_ms - self.window_start >= 1000.0 { self.window_start = now_ms; self.count = 0.0; }
        if self.count < self.max_per_sec { self.count += 1.0; true } else { false }
    }
}

// ─── v0.70: Stream Splitter ───

pub struct StreamSplitter;

impl StreamSplitter {
    pub fn split(data: &[u8], n: usize) -> Vec<Vec<u8>> {
        if n == 0 { return vec![data.to_vec()]; }
        let chunk_size = (data.len() + n - 1) / n;
        data.chunks(chunk_size.max(1)).map(|c| c.to_vec()).collect()
    }
}

// ─── v0.70: Watermark Tracker ───

pub struct WatermarkTracker { low: f64, high: f64, current: f64 }

impl WatermarkTracker {
    pub fn new(low: f64, high: f64) -> Self { WatermarkTracker { low, high, current: 0.0 } }
    pub fn update(&mut self, val: f64) { self.current = val; }
    pub fn status(&self) -> &str {
        if self.current >= self.high { "high" } else if self.current <= self.low { "low" } else { "normal" }
    }
}

// ─── v0.70: Packet Reorderer ───

pub struct PacketReorderer { buffer: Vec<(u64, Vec<u8>)>, next_expected: u64 }

impl PacketReorderer {
    pub fn new() -> Self { PacketReorderer { buffer: Vec::new(), next_expected: 0 } }
    pub fn push(&mut self, seq: u64, data: Vec<u8>) {
        self.buffer.push((seq, data));
        self.buffer.sort_by_key(|(s, _)| *s);
    }
    pub fn pop(&mut self) -> Option<Vec<u8>> {
        if !self.buffer.is_empty() && self.buffer[0].0 == self.next_expected {
            self.next_expected += 1;
            Some(self.buffer.remove(0).1)
        } else { None }
    }
    pub fn pending(&self) -> usize { self.buffer.len() }
}

impl Default for PacketReorderer { fn default() -> Self { Self::new() } }

// ─── v0.70: Timeout Monitor ───

pub struct TimeoutMonitor { pending: Vec<(u64, f64)>, timeout_ms: f64 }

impl TimeoutMonitor {
    pub fn new(timeout_ms: f64) -> Self { TimeoutMonitor { pending: Vec::new(), timeout_ms } }
    pub fn start(&mut self, id: u64, now_ms: f64) { self.pending.push((id, now_ms)); }
    pub fn check_expired(&mut self, now_ms: f64) -> Vec<u64> {
        let (expired, live): (Vec<_>, Vec<_>) = self.pending.drain(..).partition(|(_, t)| now_ms - t >= self.timeout_ms);
        self.pending = live;
        expired.into_iter().map(|(id, _)| id).collect()
    }
}

// ─── v0.70: Delta Encoder ───

pub struct DeltaEncoder;

impl DeltaEncoder {
    pub fn encode(prev: &[u8], cur: &[u8]) -> Vec<u8> {
        let len = prev.len().min(cur.len());
        let mut out: Vec<u8> = (0..len).map(|i| cur[i] ^ prev[i]).collect();
        if cur.len() > len { out.extend_from_slice(&cur[len..]); }
        out
    }
    pub fn decode(prev: &[u8], delta: &[u8]) -> Vec<u8> {
        let len = prev.len().min(delta.len());
        let mut out: Vec<u8> = (0..len).map(|i| delta[i] ^ prev[i]).collect();
        if delta.len() > len { out.extend_from_slice(&delta[len..]); }
        out
    }
}

// ─── v0.75: Stream Journal ───

pub struct StreamJournal { entries: Vec<String> }

impl StreamJournal {
    pub fn new() -> Self { StreamJournal { entries: Vec::new() } }
    pub fn append(&mut self, entry: &str) { self.entries.push(entry.to_string()); }
    pub fn len(&self) -> usize { self.entries.len() }
    pub fn is_empty(&self) -> bool { self.entries.is_empty() }
    pub fn get(&self, idx: usize) -> Option<&str> { self.entries.get(idx).map(|s| s.as_str()) }
}

impl Default for StreamJournal { fn default() -> Self { Self::new() } }

// ─── v0.75: Hot Config ───

pub struct HotConfigV2 { values: Vec<(String, String)>, generation: u64 }

impl HotConfigV2 {
    pub fn new() -> Self { HotConfigV2 { values: Vec::new(), generation: 0 } }
    pub fn set(&mut self, key: &str, val: &str) {
        if let Some(p) = self.values.iter_mut().find(|(k, _)| k == key) { p.1 = val.to_string(); }
        else { self.values.push((key.to_string(), val.to_string())); }
        self.generation += 1;
    }
    pub fn get(&self, key: &str) -> Option<&str> { self.values.iter().find(|(k, _)| k == key).map(|(_, v)| v.as_str()) }
    pub fn generation(&self) -> u64 { self.generation }
}

impl Default for HotConfigV2 { fn default() -> Self { Self::new() } }

// ─── v0.75: Frame Dedup V2 ───

pub struct FrameDedupV2 { seen: Vec<u64>, capacity: usize }

impl FrameDedupV2 {
    pub fn new(capacity: usize) -> Self { FrameDedupV2 { seen: Vec::new(), capacity } }
    fn hash(data: &[u8]) -> u64 {
        let mut h: u64 = 0xcbf29ce484222325;
        for &b in data { h ^= b as u64; h = h.wrapping_mul(0x100000001b3); }
        h
    }
    pub fn is_dup(&mut self, data: &[u8]) -> bool {
        let h = Self::hash(data);
        if self.seen.contains(&h) { return true; }
        if self.seen.len() >= self.capacity { self.seen.remove(0); }
        self.seen.push(h);
        false
    }
}

// ─── v0.75: Metric Histogram ───

pub struct MetricHistogram { buckets: Vec<(f64, u64)> }

impl MetricHistogram {
    pub fn new(boundaries: &[f64]) -> Self {
        let mut buckets: Vec<(f64, u64)> = boundaries.iter().map(|&b| (b, 0)).collect();
        buckets.push((f64::INFINITY, 0));
        MetricHistogram { buckets }
    }
    pub fn record(&mut self, val: f64) {
        for b in self.buckets.iter_mut() { if val <= b.0 { b.1 += 1; return; } }
    }
    pub fn percentile(&self, p: f64) -> f64 {
        let total: u64 = self.buckets.iter().map(|(_, c)| c).sum();
        let target = (total as f64 * p / 100.0) as u64;
        let mut acc = 0u64;
        for (bound, count) in &self.buckets { acc += count; if acc >= target { return *bound; } }
        self.buckets.last().map(|(b, _)| *b).unwrap_or(0.0)
    }
}

// ─── v0.75: Circuit Breaker ───

#[derive(Debug, Clone, PartialEq)]
pub enum BreakerState { Closed, Open, HalfOpen }

pub struct CircuitBreaker { state: BreakerState, failures: u32, threshold: u32 }

impl CircuitBreaker {
    pub fn new(threshold: u32) -> Self { CircuitBreaker { state: BreakerState::Closed, failures: 0, threshold } }
    pub fn record_success(&mut self) { self.failures = 0; self.state = BreakerState::Closed; }
    pub fn record_failure(&mut self) {
        self.failures += 1;
        if self.failures >= self.threshold { self.state = BreakerState::Open; }
    }
    pub fn try_half_open(&mut self) { if self.state == BreakerState::Open { self.state = BreakerState::HalfOpen; } }
    pub fn state(&self) -> &BreakerState { &self.state }
    pub fn is_allowed(&self) -> bool { self.state != BreakerState::Open }
}

// ─── v0.75: Token Bucket V2 ───

pub struct TokenBucketV2 { tokens: f64, max: f64, refill_rate: f64, last_refill: f64 }

impl TokenBucketV2 {
    pub fn new(max: f64, refill_rate: f64) -> Self {
        TokenBucketV2 { tokens: max, max, refill_rate, last_refill: 0.0 }
    }
    pub fn take(&mut self, n: f64, now_ms: f64) -> bool {
        let elapsed = (now_ms - self.last_refill) / 1000.0;
        self.tokens = (self.tokens + elapsed * self.refill_rate).min(self.max);
        self.last_refill = now_ms;
        if self.tokens >= n { self.tokens -= n; true } else { false }
    }
    pub fn available(&self) -> f64 { self.tokens }
}

// ─── v0.75: Batch Accumulator ───

pub struct BatchAccumulator { buffer: Vec<Vec<u8>>, max_count: usize, max_bytes: usize, current_bytes: usize }

impl BatchAccumulator {
    pub fn new(max_count: usize, max_bytes: usize) -> Self {
        BatchAccumulator { buffer: Vec::new(), max_count, max_bytes, current_bytes: 0 }
    }
    pub fn add(&mut self, data: Vec<u8>) -> bool {
        self.current_bytes += data.len();
        self.buffer.push(data);
        self.buffer.len() >= self.max_count || self.current_bytes >= self.max_bytes
    }
    pub fn flush(&mut self) -> Vec<Vec<u8>> {
        self.current_bytes = 0;
        std::mem::take(&mut self.buffer)
    }
    pub fn pending(&self) -> usize { self.buffer.len() }
}

// ─── v0.75: Checksum Chain ───

pub struct ChecksumChain { prev_crc: u32 }

impl ChecksumChain {
    pub fn new() -> Self { ChecksumChain { prev_crc: 0 } }
    pub fn feed(&mut self, data: &[u8]) -> u32 {
        let mut crc: u32 = self.prev_crc ^ 0xFFFFFFFF;
        for &b in data { crc ^= b as u32; for _ in 0..8 { crc = if crc & 1 != 0 { (crc >> 1) ^ 0xEDB88320 } else { crc >> 1 }; } }
        self.prev_crc = !crc;
        self.prev_crc
    }
}

impl Default for ChecksumChain { fn default() -> Self { Self::new() } }

// ─── v0.75: Ping Monitor ───

pub struct PingMonitor { samples: Vec<f64>, smoothed: f64, alpha: f64 }

impl PingMonitor {
    pub fn new(alpha: f64) -> Self { PingMonitor { samples: Vec::new(), smoothed: 0.0, alpha } }
    pub fn record(&mut self, rtt_ms: f64) {
        self.smoothed = self.alpha * rtt_ms + (1.0 - self.alpha) * self.smoothed;
        self.samples.push(rtt_ms);
    }
    pub fn smoothed_rtt(&self) -> f64 { self.smoothed }
    pub fn sample_count(&self) -> usize { self.samples.len() }
}

// ─── v0.80: ABR Controller ───

pub struct AbrController { tiers: Vec<u32>, current: usize }

impl AbrController {
    pub fn new(tiers: Vec<u32>) -> Self { AbrController { current: tiers.len().saturating_sub(1), tiers } }
    pub fn adjust(&mut self, loss_pct: f64, rtt_ms: f64) -> u32 {
        if loss_pct > 5.0 || rtt_ms > 200.0 { if self.current > 0 { self.current -= 1; } }
        else if loss_pct < 1.0 && rtt_ms < 50.0 { if self.current + 1 < self.tiers.len() { self.current += 1; } }
        self.tiers[self.current]
    }
    pub fn current_bitrate(&self) -> u32 { self.tiers[self.current] }
}

// ─── v0.80: Jitter Buffer ───

pub struct JitterBufferV2 { buffer: Vec<Vec<u8>>, delay: usize }

impl JitterBufferV2 {
    pub fn new(delay: usize) -> Self { JitterBufferV2 { buffer: Vec::new(), delay } }
    pub fn push(&mut self, data: Vec<u8>) { self.buffer.push(data); }
    pub fn pop(&mut self) -> Option<Vec<u8>> {
        if self.buffer.len() > self.delay { Some(self.buffer.remove(0)) } else { None }
    }
    pub fn buffered(&self) -> usize { self.buffer.len() }
}

// ─── v0.80: Frame Counter ───

pub struct FrameCounter { timestamps: Vec<f64>, bytes: Vec<usize>, window_ms: f64 }

impl FrameCounter {
    pub fn new(window_ms: f64) -> Self { FrameCounter { timestamps: Vec::new(), bytes: Vec::new(), window_ms } }
    pub fn tick(&mut self, now_ms: f64, size: usize) {
        self.timestamps.push(now_ms);
        self.bytes.push(size);
        let cutoff = now_ms - self.window_ms;
        while !self.timestamps.is_empty() && self.timestamps[0] < cutoff { self.timestamps.remove(0); self.bytes.remove(0); }
    }
    pub fn fps(&self) -> f64 { self.timestamps.len() as f64 / (self.window_ms / 1000.0) }
    pub fn bps(&self) -> f64 { self.bytes.iter().sum::<usize>() as f64 * 8.0 / (self.window_ms / 1000.0) }
}

// ─── v0.80: Event Bus ───

pub struct EventBus { events: Vec<String> }

impl EventBus {
    pub fn new() -> Self { EventBus { events: Vec::new() } }
    pub fn emit(&mut self, name: &str) { self.events.push(name.to_string()); }
    pub fn count(&self) -> usize { self.events.len() }
    pub fn drain(&mut self) -> Vec<String> { std::mem::take(&mut self.events) }
}

impl Default for EventBus { fn default() -> Self { Self::new() } }

// ─── v0.80: Stream Tee ───

pub struct StreamTee { sink_count: usize }

impl StreamTee {
    pub fn new(n: usize) -> Self { StreamTee { sink_count: n } }
    pub fn tee(&self, data: &[u8]) -> Vec<Vec<u8>> { (0..self.sink_count).map(|_| data.to_vec()).collect() }
    pub fn count(&self) -> usize { self.sink_count }
}

// ─── v0.80: Gain Controller ───

pub struct GainController { current_gain: f64, smoothing: f64 }

impl GainController {
    pub fn new(smoothing: f64) -> Self { GainController { current_gain: 1.0, smoothing } }
    pub fn apply(&mut self, val: f64, target: f64) -> f64 {
        let ratio = if val.abs() > 0.001 { target / val } else { 1.0 };
        self.current_gain = self.smoothing * ratio + (1.0 - self.smoothing) * self.current_gain;
        val * self.current_gain
    }
    pub fn gain(&self) -> f64 { self.current_gain }
}

// ─── v0.80: Lossy Queue ───

pub struct LossyQueue { buffer: Vec<Vec<u8>>, capacity: usize }

impl LossyQueue {
    pub fn new(capacity: usize) -> Self { LossyQueue { buffer: Vec::new(), capacity } }
    pub fn push(&mut self, data: Vec<u8>) {
        if self.buffer.len() >= self.capacity { self.buffer.remove(0); }
        self.buffer.push(data);
    }
    pub fn pop(&mut self) -> Option<Vec<u8>> { if self.buffer.is_empty() { None } else { Some(self.buffer.remove(0)) } }
    pub fn len(&self) -> usize { self.buffer.len() }
    pub fn is_empty(&self) -> bool { self.buffer.is_empty() }
}

// ─── v0.80: Header Builder ───

pub struct HeaderBuilder;

impl HeaderBuilder {
    pub fn build(seq: u32, channel: u8, flags: u8) -> Vec<u8> {
        let mut hdr = Vec::with_capacity(6);
        hdr.extend_from_slice(&seq.to_le_bytes());
        hdr.push(channel);
        hdr.push(flags);
        hdr
    }
    pub fn parse(hdr: &[u8]) -> Option<(u32, u8, u8)> {
        if hdr.len() < 6 { return None; }
        let seq = u32::from_le_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
        Some((seq, hdr[4], hdr[5]))
    }
}

// ─── v0.80: Session Tracker ───

pub struct SessionTracker { id: u64, start_ms: f64, last_ms: f64 }

impl SessionTracker {
    pub fn new(id: u64, now_ms: f64) -> Self { SessionTracker { id, start_ms: now_ms, last_ms: now_ms } }
    pub fn touch(&mut self, now_ms: f64) { self.last_ms = now_ms; }
    pub fn duration_ms(&self) -> f64 { self.last_ms - self.start_ms }
    pub fn id(&self) -> u64 { self.id }
}

// ─── v0.90: XOR Cipher V2 ───

pub struct XorCipherV2 { key: Vec<u8> }

impl XorCipherV2 {
    pub fn new(key: Vec<u8>) -> Self { XorCipherV2 { key } }
    pub fn apply(&self, data: &[u8]) -> Vec<u8> {
        if self.key.is_empty() { return data.to_vec(); }
        data.iter().enumerate().map(|(i, &b)| b ^ self.key[i % self.key.len()]).collect()
    }
}

// ─── v0.90: Channel Router ───

pub struct ChannelRouter { channels: Vec<(u8, Vec<Vec<u8>>)> }

impl ChannelRouter {
    pub fn new() -> Self { ChannelRouter { channels: Vec::new() } }
    pub fn route(&mut self, ch: u8, data: Vec<u8>) {
        if let Some(entry) = self.channels.iter_mut().find(|(c, _)| *c == ch) { entry.1.push(data); }
        else { self.channels.push((ch, vec![data])); }
    }
    pub fn drain(&mut self, ch: u8) -> Vec<Vec<u8>> {
        if let Some(entry) = self.channels.iter_mut().find(|(c, _)| *c == ch) { std::mem::take(&mut entry.1) }
        else { Vec::new() }
    }
    pub fn channel_count(&self) -> usize { self.channels.len() }
}

impl Default for ChannelRouter { fn default() -> Self { Self::new() } }

// ─── v0.90: Ack Tracker ───

pub struct AckTracker { sent: Vec<u64>, acked: Vec<u64> }

impl AckTracker {
    pub fn new() -> Self { AckTracker { sent: Vec::new(), acked: Vec::new() } }
    pub fn send(&mut self, seq: u64) { self.sent.push(seq); }
    pub fn ack(&mut self, seq: u64) { if !self.acked.contains(&seq) { self.acked.push(seq); } }
    pub fn is_acked(&self, seq: u64) -> bool { self.acked.contains(&seq) }
    pub fn pending(&self) -> usize { self.sent.len() - self.acked.len() }
}

impl Default for AckTracker { fn default() -> Self { Self::new() } }

// ─── v0.90: Frame Pool ───

pub struct FramePoolV2 { pool: Vec<Vec<u8>>, buf_size: usize }

impl FramePoolV2 {
    pub fn new(buf_size: usize, count: usize) -> Self {
        let pool = (0..count).map(|_| vec![0u8; buf_size]).collect();
        FramePoolV2 { pool, buf_size }
    }
    pub fn alloc(&mut self) -> Vec<u8> { self.pool.pop().unwrap_or_else(|| vec![0u8; self.buf_size]) }
    pub fn free(&mut self, buf: Vec<u8>) { self.pool.push(buf); }
    pub fn available(&self) -> usize { self.pool.len() }
}

// ─── v0.90: Bandwidth Estimator ───

pub struct BandwidthEstimatorV2 { estimate_bps: f64, alpha: f64 }

impl BandwidthEstimatorV2 {
    pub fn new(alpha: f64) -> Self { BandwidthEstimatorV2 { estimate_bps: 0.0, alpha } }
    pub fn sample(&mut self, bytes: usize, duration_ms: f64) {
        if duration_ms <= 0.0 { return; }
        let bps = bytes as f64 * 8.0 * 1000.0 / duration_ms;
        self.estimate_bps = self.alpha * bps + (1.0 - self.alpha) * self.estimate_bps;
    }
    pub fn estimate(&self) -> f64 { self.estimate_bps }
}

// ─── v0.90: Priority Mux ───

pub struct PriorityMux { queues: Vec<(u8, Vec<Vec<u8>>)> }

impl PriorityMux {
    pub fn new() -> Self { PriorityMux { queues: Vec::new() } }
    pub fn push(&mut self, priority: u8, data: Vec<u8>) {
        if let Some(q) = self.queues.iter_mut().find(|(p, _)| *p == priority) { q.1.push(data); }
        else { self.queues.push((priority, vec![data])); self.queues.sort_by(|a, b| b.0.cmp(&a.0)); }
    }
    pub fn pop(&mut self) -> Option<Vec<u8>> {
        for q in self.queues.iter_mut() { if !q.1.is_empty() { return Some(q.1.remove(0)); } }
        None
    }
}

impl Default for PriorityMux { fn default() -> Self { Self::new() } }

// ─── v0.90: Nonce Generator ───

pub struct NonceGenerator { counter: u64 }

impl NonceGenerator {
    pub fn new() -> Self { NonceGenerator { counter: 0 } }
    pub fn next(&mut self) -> u64 { self.counter += 1; self.counter }
    pub fn current(&self) -> u64 { self.counter }
}

impl Default for NonceGenerator { fn default() -> Self { Self::new() } }

// ─── v0.90: Stream Validator ───

pub struct StreamValidator { expected_seq: u64, errors: u64 }

impl StreamValidator {
    pub fn new() -> Self { StreamValidator { expected_seq: 0, errors: 0 } }
    pub fn validate(&mut self, seq: u64) -> bool {
        if seq == self.expected_seq { self.expected_seq += 1; true }
        else { self.errors += 1; self.expected_seq = seq + 1; false }
    }
    pub fn error_count(&self) -> u64 { self.errors }
}

impl Default for StreamValidator { fn default() -> Self { Self::new() } }

// ─── v0.90: Retry Queue ───

pub struct RetryQueue { items: Vec<(Vec<u8>, u32)>, max_retries: u32 }

impl RetryQueue {
    pub fn new(max_retries: u32) -> Self { RetryQueue { items: Vec::new(), max_retries } }
    pub fn push(&mut self, data: Vec<u8>) { self.items.push((data, 0)); }
    pub fn pop_for_retry(&mut self) -> Option<Vec<u8>> {
        if let Some(pos) = self.items.iter().position(|(_, r)| *r < self.max_retries) {
            self.items[pos].1 += 1;
            Some(self.items[pos].0.clone())
        } else { None }
    }
    pub fn remove_acked(&mut self, data: &[u8]) { self.items.retain(|(d, _)| d != data); }
    pub fn pending(&self) -> usize { self.items.len() }
}

// ─── v0.95: Lz4 Lite ───

pub struct Lz4Lite;

impl Lz4Lite {
    pub fn compress(data: &[u8]) -> Vec<u8> {
        let mut out = Vec::new();
        let mut i = 0;
        while i < data.len() {
            let mut run = 1;
            while i + run < data.len() && data[i + run] == data[i] && run < 255 { run += 1; }
            out.push(run as u8);
            out.push(data[i]);
            i += run;
        }
        out
    }
    pub fn decompress(data: &[u8]) -> Vec<u8> {
        let mut out = Vec::new();
        let mut i = 0;
        while i + 1 < data.len() {
            let run = data[i] as usize;
            let byte = data[i + 1];
            for _ in 0..run { out.push(byte); }
            i += 2;
        }
        out
    }
}

// ─── v0.95: Telemetry Sink ───

pub struct TelemetrySink { events: Vec<(String, f64)> }

impl TelemetrySink {
    pub fn new() -> Self { TelemetrySink { events: Vec::new() } }
    pub fn emit(&mut self, name: &str, val: f64) { self.events.push((name.to_string(), val)); }
    pub fn count(&self) -> usize { self.events.len() }
    pub fn drain(&mut self) -> Vec<(String, f64)> { std::mem::take(&mut self.events) }
}

impl Default for TelemetrySink { fn default() -> Self { Self::new() } }

// ─── v0.95: Frame Differ ───

pub struct FrameDiffer;

impl FrameDiffer {
    pub fn diff(a: &[u8], b: &[u8]) -> Vec<u8> {
        let len = a.len().max(b.len());
        let mut out = Vec::with_capacity(len);
        for i in 0..len {
            let va = if i < a.len() { a[i] } else { 0 };
            let vb = if i < b.len() { b[i] } else { 0 };
            out.push(va ^ vb);
        }
        out
    }
    pub fn apply(base: &[u8], diff: &[u8]) -> Vec<u8> {
        let len = base.len().max(diff.len());
        let mut out = Vec::with_capacity(len);
        for i in 0..len {
            let va = if i < base.len() { base[i] } else { 0 };
            let vd = if i < diff.len() { diff[i] } else { 0 };
            out.push(va ^ vd);
        }
        out
    }
}

// ─── v0.95: Backoff Timer ───

pub struct BackoffTimer { attempt: u32, base_ms: f64, max_ms: f64 }

impl BackoffTimer {
    pub fn new(base_ms: f64, max_ms: f64) -> Self { BackoffTimer { attempt: 0, base_ms, max_ms } }
    pub fn next(&mut self) -> f64 {
        let delay = (self.base_ms * 2.0f64.powi(self.attempt as i32)).min(self.max_ms);
        self.attempt += 1;
        delay
    }
    pub fn reset(&mut self) { self.attempt = 0; }
    pub fn attempts(&self) -> u32 { self.attempt }
}

// ─── v0.95: Stream Mirror ───

pub struct StreamMirror { primary: Vec<Vec<u8>>, mirror: Vec<Vec<u8>> }

impl StreamMirror {
    pub fn new() -> Self { StreamMirror { primary: Vec::new(), mirror: Vec::new() } }
    pub fn push(&mut self, data: Vec<u8>) { self.mirror.push(data.clone()); self.primary.push(data); }
    pub fn pop_primary(&mut self) -> Option<Vec<u8>> { if self.primary.is_empty() { None } else { Some(self.primary.remove(0)) } }
    pub fn pop_mirror(&mut self) -> Option<Vec<u8>> { if self.mirror.is_empty() { None } else { Some(self.mirror.remove(0)) } }
}

impl Default for StreamMirror { fn default() -> Self { Self::new() } }

// ─── v0.95: Quota Manager ───

pub struct QuotaManager { limit: usize, used: usize }

impl QuotaManager {
    pub fn new(limit: usize) -> Self { QuotaManager { limit, used: 0 } }
    pub fn consume(&mut self, n: usize) -> bool { if self.used + n > self.limit { false } else { self.used += n; true } }
    pub fn remaining(&self) -> usize { self.limit.saturating_sub(self.used) }
    pub fn reset(&mut self) { self.used = 0; }
}

// ─── v0.95: Heartbeat V2 ───

pub struct HeartbeatV2 { last_ping: f64, last_pong: f64 }

impl HeartbeatV2 {
    pub fn new() -> Self { HeartbeatV2 { last_ping: 0.0, last_pong: 0.0 } }
    pub fn ping(&mut self, now: f64) { self.last_ping = now; }
    pub fn pong(&mut self, now: f64) { self.last_pong = now; }
    pub fn rtt(&self) -> f64 { (self.last_pong - self.last_ping).abs() }
    pub fn is_alive(&self, now: f64, timeout: f64) -> bool { (now - self.last_pong) < timeout }
}

impl Default for HeartbeatV2 { fn default() -> Self { Self::new() } }

// ─── v0.95: Tag Filter ───

pub struct TagFilter { allowed: Vec<String> }

impl TagFilter {
    pub fn new(tags: Vec<String>) -> Self { TagFilter { allowed: tags } }
    pub fn accept(&self, tag: &str) -> bool { self.allowed.iter().any(|t| t == tag) }
    pub fn add(&mut self, tag: &str) { if !self.allowed.contains(&tag.to_string()) { self.allowed.push(tag.to_string()); } }
}

// ─── v0.95: Moving Average ───

pub struct MovingAverage { window: Vec<f64>, max_size: usize }

impl MovingAverage {
    pub fn new(max_size: usize) -> Self { MovingAverage { window: Vec::new(), max_size } }
    pub fn push(&mut self, val: f64) { self.window.push(val); if self.window.len() > self.max_size { self.window.remove(0); } }
    pub fn get(&self) -> f64 { if self.window.is_empty() { 0.0 } else { self.window.iter().sum::<f64>() / self.window.len() as f64 } }
    pub fn count(&self) -> usize { self.window.len() }
}

// ─── v1.0: Stream Pipeline ───

pub struct StreamPipeline { stages: Vec<String> }

impl StreamPipeline {
    pub fn new() -> Self { StreamPipeline { stages: Vec::new() } }
    pub fn add(&mut self, name: &str) { self.stages.push(name.to_string()); }
    pub fn count(&self) -> usize { self.stages.len() }
    pub fn stages(&self) -> &[String] { &self.stages }
}

impl Default for StreamPipeline { fn default() -> Self { Self::new() } }

// ─── v1.0: Protocol Header ───

pub struct ProtocolHeader;

impl ProtocolHeader {
    pub fn encode(version: u8, flags: u8, seq: u32) -> Vec<u8> {
        let mut hdr = vec![0xD5u8.wrapping_add(version), version, flags];
        hdr.extend_from_slice(&seq.to_le_bytes());
        hdr
    }
    pub fn decode(data: &[u8]) -> Option<(u8, u8, u32)> {
        if data.len() < 7 { return None; }
        let version = data[1];
        let flags = data[2];
        let seq = u32::from_le_bytes([data[3], data[4], data[5], data[6]]);
        Some((version, flags, seq))
    }
}

// ─── v1.0: Frame Splitter V2 ───

pub struct FrameSplitterV2 { mtu: usize }

impl FrameSplitterV2 {
    pub fn new(mtu: usize) -> Self { FrameSplitterV2 { mtu } }
    pub fn split(&self, data: &[u8]) -> Vec<Vec<u8>> {
        data.chunks(self.mtu).map(|c| c.to_vec()).collect()
    }
    pub fn reassemble(chunks: &[Vec<u8>]) -> Vec<u8> {
        chunks.iter().flat_map(|c| c.iter().copied()).collect()
    }
}

// ─── v1.0: Congestion Window ───

pub struct CongestionWindowV2 { cwnd: f64, ssthresh: f64 }

impl CongestionWindowV2 {
    pub fn new() -> Self { CongestionWindowV2 { cwnd: 1.0, ssthresh: 64.0 } }
    pub fn on_ack(&mut self) -> f64 {
        if self.cwnd < self.ssthresh { self.cwnd *= 2.0; } else { self.cwnd += 1.0; }
        self.cwnd
    }
    pub fn on_loss(&mut self) -> f64 { self.ssthresh = (self.cwnd / 2.0).max(1.0); self.cwnd = 1.0; self.cwnd }
    pub fn window(&self) -> f64 { self.cwnd }
}

impl Default for CongestionWindowV2 { fn default() -> Self { Self::new() } }

// ─── v1.0: Stream Stats V2 ───

pub struct StreamStatsV2 { frames: u64, bytes: u64, errors: u64, start_ms: f64 }

impl StreamStatsV2 {
    pub fn new(start_ms: f64) -> Self { StreamStatsV2 { frames: 0, bytes: 0, errors: 0, start_ms } }
    pub fn record(&mut self, size: usize) { self.frames += 1; self.bytes += size as u64; }
    pub fn error(&mut self) { self.errors += 1; }
    pub fn summary(&self, now_ms: f64) -> String {
        let elapsed = (now_ms - self.start_ms).max(1.0);
        format!("frames={} bytes={} errors={} elapsed_ms={:.0}", self.frames, self.bytes, self.errors, elapsed)
    }
}

// ─── v1.0: ACK Window ───

pub struct AckWindow { window: Vec<bool>, base: u64 }

impl AckWindow {
    pub fn new(size: usize) -> Self { AckWindow { window: vec![false; size], base: 0 } }
    pub fn ack(&mut self, seq: u64) -> bool {
        if seq < self.base { return false; }
        let idx = (seq - self.base) as usize;
        if idx >= self.window.len() { return false; }
        self.window[idx] = true;
        true
    }
    pub fn advance(&mut self) -> u64 {
        while !self.window.is_empty() && self.window[0] { self.window.remove(0); self.window.push(false); self.base += 1; }
        self.base
    }
}

// ─── v1.0: Codec Registry ───

pub struct CodecRegistryV2 { codecs: Vec<String> }

impl CodecRegistryV2 {
    pub fn new() -> Self { CodecRegistryV2 { codecs: Vec::new() } }
    pub fn register(&mut self, name: &str) { if !self.codecs.contains(&name.to_string()) { self.codecs.push(name.to_string()); } }
    pub fn has(&self, name: &str) -> bool { self.codecs.contains(&name.to_string()) }
    pub fn list(&self) -> &[String] { &self.codecs }
}

impl Default for CodecRegistryV2 { fn default() -> Self { Self::new() } }

// ─── v1.0: Flow Controller ───

pub struct FlowControllerV2 { credits: i64, max_credits: i64 }

impl FlowControllerV2 {
    pub fn new(max_credits: i64) -> Self { FlowControllerV2 { credits: max_credits, max_credits } }
    pub fn consume(&mut self, n: i64) -> bool { if self.credits >= n { self.credits -= n; true } else { false } }
    pub fn replenish(&mut self, n: i64) { self.credits = (self.credits + n).min(self.max_credits); }
    pub fn available(&self) -> i64 { self.credits }
}

// ─── v1.0: Version Negotiator ───

pub struct VersionNegotiator { supported: Vec<String> }

impl VersionNegotiator {
    pub fn new(supported: Vec<String>) -> Self { VersionNegotiator { supported } }
    pub fn negotiate(&self, requested: &str) -> String {
        if self.supported.contains(&requested.to_string()) { requested.to_string() }
        else { self.supported.last().cloned().unwrap_or_else(|| "1.0".to_string()) }
    }
}

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

    #[test]
    fn frame_encode_decode_roundtrip() {
        let payload = vec![0xFF; 100];
        let msg = encode_frame(FrameType::Pixels, 1, 5000, 320, 240, FLAG_KEYFRAME, &payload);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::Pixels as u8);
        assert_eq!(decoded.header.seq, 1);
        assert_eq!(decoded.header.timestamp, 5000);
        assert_eq!(decoded.header.width, 320);
        assert_eq!(decoded.header.height, 240);
        assert_eq!(decoded.header.length, 100);
        assert!(decoded.header.is_keyframe());
        assert_eq!(decoded.payload, &payload[..]);
    }

    #[test]
    fn input_encode_decode_roundtrip() {
        let ptr = PointerEvent { x: 100, y: 200, buttons: 1 };
        let msg = pointer_input(5, 1234, &ptr, InputType::Pointer);
        let decoded = decode_message(&msg).unwrap();
        assert!(decoded.header.is_input());
        assert_eq!(decoded.header.frame_type, InputType::Pointer as u8);
        let ptr2 = PointerEvent::decode(decoded.payload).unwrap();
        assert_eq!(ptr2.x, 100);
        assert_eq!(ptr2.y, 200);
    }

    #[test]
    fn delta_compression() {
        let frame1 = vec![10, 20, 30, 40, 50, 60, 70, 80];
        let frame2 = vec![10, 20, 30, 40, 55, 65, 70, 80];
        let delta = compute_delta(&frame2, &frame1);
        assert_eq!(delta[0], 0);
        assert_eq!(delta[1], 0);
        assert_eq!(delta[6], 0);
        assert_eq!(delta[7], 0);
        let reconstructed = apply_delta(&frame1, &delta);
        assert_eq!(reconstructed, frame2);
    }

    #[test]
    fn delta_worthwhile_check() {
        assert!(delta_is_worthwhile(&vec![0u8; 100]));
        assert!(!delta_is_worthwhile(&vec![0xFF; 100]));
    }

    #[test]
    fn ping_message() {
        let msg = ping(99, 5000);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::Ping as u8);
        assert_eq!(decoded.header.seq, 99);
        assert_eq!(decoded.payload.len(), 0);
    }

    #[test]
    fn partial_buffer_returns_none() {
        let msg = encode_frame(FrameType::Pixels, 0, 0, 10, 10, 0, &[1, 2, 3]);
        assert!(decode_message(&msg[..HEADER_SIZE + 2]).is_none());
    }

    #[test]
    fn message_size_calculation() {
        let header = FrameHeader {
            frame_type: 0, flags: 0, seq: 0, timestamp: 0,
            width: 0, height: 0, length: 1024,
        };
        assert_eq!(message_size(&header), HEADER_SIZE + 1024);
    }

    // ─── RLE Tests ───

    #[test]
    fn rle_encode_all_zeros() {
        let data = vec![0u8; 1000];
        let encoded = rle_encode(&data);
        assert_eq!(encoded.len(), 3);
        let decoded = rle_decode(&encoded);
        assert_eq!(decoded, data);
    }

    #[test]
    fn rle_encode_no_zeros() {
        let data: Vec<u8> = (1..=255).collect();
        let encoded = rle_encode(&data);
        assert_eq!(encoded.len(), data.len());
        let decoded = rle_decode(&encoded);
        assert_eq!(decoded, data);
    }

    #[test]
    fn rle_encode_mixed() {
        let data = vec![0, 0, 0, 0xAA, 0xBB, 0, 0, 0, 0, 0, 0xCC];
        let encoded = rle_encode(&data);
        let decoded = rle_decode(&encoded);
        assert_eq!(decoded, data);
        assert!(encoded.len() < data.len());
    }

    #[test]
    fn rle_delta_roundtrip() {
        let frame1: Vec<u8> = (0..100).map(|i| (i * 7 % 256) as u8).collect();
        let mut frame2 = frame1.clone();
        for i in (0..10).map(|x| x * 10) {
            frame2[i] = frame2[i].wrapping_add(1);
        }
        let (compressed, ratio) = encode_delta_rle(&frame2, &frame1);
        assert!(ratio < 0.5, "RLE should compress well: ratio={}", ratio);
        let reconstructed = decode_delta_rle(&compressed, &frame1);
        assert_eq!(reconstructed, frame2);
    }

    #[test]
    fn rle_large_zero_run() {
        let data = vec![0u8; 70000];
        let encoded = rle_encode(&data);
        let decoded = rle_decode(&encoded);
        assert_eq!(decoded, data);
        assert_eq!(encoded.len(), 6); // 2 markers: 65535 + 4465
    }

    // ─── Signal Frame Tests ───

    #[test]
    fn signal_sync_frame_roundtrip() {
        let json = br#"{"count":42,"name":"hello"}"#;
        let msg = signal_sync_frame(1, 1000, json);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::SignalSync as u8);
        assert!(decoded.header.is_keyframe());
        assert_eq!(decoded.payload, json);
    }

    #[test]
    fn signal_diff_frame_roundtrip() {
        let json = br#"{"count":43}"#;
        let msg = signal_diff_frame(2, 2000, json);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::SignalDiff as u8);
        assert!(!decoded.header.is_keyframe());
        assert_eq!(decoded.payload, json);
    }

    #[test]
    fn delta_frame_builder() {
        let rle_data = rle_encode(&[0, 0, 0, 0, 0xFF, 0xAA, 0, 0]);
        let msg = delta_frame(5, 3000, 320, 240, &rle_data);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::DeltaPixels as u8);
        assert_eq!(decoded.header.flags & FLAG_COMPRESSED, FLAG_COMPRESSED);
    }

    #[test]
    fn stream_end_message() {
        let msg = stream_end(100, 99999);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::End as u8);
    }

    #[test]
    fn touch_input_builder() {
        let evt = TouchEvent { id: 1, x: 300, y: 400, phase: 0 };
        let msg = touch_input(1, 100, &evt, InputType::Touch);
        let decoded = decode_message(&msg).unwrap();
        assert!(decoded.header.is_input());
        let evt2 = TouchEvent::decode(decoded.payload).unwrap();
        assert_eq!(evt2.id, 1);
        assert_eq!(evt2.x, 300);
    }

    #[test]
    fn gamepad_input_builders() {
        let axis = GamepadAxisEvent { axis: 0, value: 16000 };
        let msg = gamepad_axis_input(1, 100, &axis);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, InputType::GamepadAxis as u8);

        let btn = GamepadButtonEvent { button: 3, pressed: 1, analog: 255 };
        let msg = gamepad_button_input(2, 200, &btn);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, InputType::GamepadButton as u8);
    }

    #[test]
    fn neural_frame_builders() {
        let tensor = vec![0.5f32, 0.3, 0.8, 0.1].iter()
            .flat_map(|f| f.to_le_bytes()).collect::<Vec<u8>>();
        let msg = neural_frame(1, 100, 2, 2, &tensor);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::NeuralFrame as u8);
        assert_eq!(decoded.header.width, 2);
        assert_eq!(decoded.header.height, 2);
        assert_eq!(decoded.payload, &tensor[..]);
    }

    #[test]
    fn neural_audio_builder() {
        let audio = vec![0u8; 256];
        let msg = neural_audio(1, 200, &audio);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::NeuralAudio as u8);
        assert_eq!(decoded.payload.len(), 256);
    }

    #[test]
    fn neural_actuator_builder() {
        let cmd = vec![1, 128, 0, 64]; // actuator command
        let msg = neural_actuator(1, 300, &cmd);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::NeuralActuator as u8);
    }

    #[test]
    fn neural_latent_builder() {
        let latent = vec![0u8; 512]; // latent vector
        let msg = neural_latent(1, 400, &latent);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::NeuralLatent as u8);
        assert_eq!(decoded.payload.len(), 512);
    }

    #[test]
    fn audio_compressed_builder() {
        let opus = vec![0xAA; 128]; // fake opus data
        let msg = audio_compressed(1, 500, 2, 480, &opus);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::AudioCompressed as u8);
        assert_eq!(decoded.header.width, 2); // channels
        assert_eq!(decoded.header.height, 480); // sample_rate/100
        assert_eq!(decoded.header.flags & FLAG_COMPRESSED, FLAG_COMPRESSED);
    }

    #[test]
    fn midi_input_builder() {
        let evt = MidiEvent { status: 0x90, data1: 60, data2: 100 };
        let msg = midi_input(1, 100, &evt);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, InputType::Midi as u8);
        assert!(decoded.header.is_input());
        let evt2 = MidiEvent::decode(decoded.payload).unwrap();
        assert_eq!(evt2.status, 0x90);
    }

    #[test]
    fn sensor_input_builder() {
        let evt = SensorInputEvent { sensor_type: 0, x: 100, y: -200, z: 9800 };
        let msg = sensor_input(1, 100, &evt);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, InputType::SensorInput as u8);
        let evt2 = SensorInputEvent::decode(decoded.payload).unwrap();
        assert_eq!(evt2.z, 9800);
    }

    #[test]
    fn voice_input_builder() {
        let header = VoiceInputEvent { sample_rate: 48000, channels: 1, format: 0 };
        let pcm = vec![0u8; 960]; // 10ms of mono f32 at 48kHz
        let msg = voice_input(1, 100, &header, &pcm);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, InputType::VoiceInput as u8);
        assert_eq!(decoded.payload.len(), VoiceInputEvent::SIZE + 960);
    }

    #[test]
    fn camera_input_builder() {
        let header = CameraInputEvent { width: 640, height: 480, format: 1 };
        let jpeg = vec![0xFFu8; 1024];
        let msg = camera_input(1, 100, &header, &jpeg);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, InputType::CameraInput as u8);
        assert_eq!(decoded.payload.len(), CameraInputEvent::SIZE + 1024);
    }

    #[test]
    fn bci_input_builder() {
        let header = BciInputEvent { channel_count: 8, sample_rate: 256 };
        let samples = vec![0u8; 32]; // 8 channels × 1 f32 sample
        let msg = bci_input(1, 100, &header, &samples);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, InputType::BciInput as u8);
        assert_eq!(decoded.payload.len(), BciInputEvent::SIZE + 32);
    }

    // ─── v0.2 Tests ───

    #[test]
    fn stream_parser_single_message() {
        let mut parser = StreamParser::new();
        let msg = signal_diff_frame(1, 100, b"{\"x\":1}");
        parser.feed(&msg);
        let (header, payload) = parser.next_message().unwrap();
        assert_eq!(header.frame_type, FrameType::SignalDiff as u8);
        assert_eq!(payload, b"{\"x\":1}");
        assert!(parser.next_message().is_none());
    }

    #[test]
    fn stream_parser_multi_message() {
        let mut parser = StreamParser::new();
        let msg1 = ping(1, 100);
        let msg2 = signal_sync_frame(2, 200, b"{\"a\":1}");
        let msg3 = stream_end(3, 300);

        let mut combined = Vec::new();
        combined.extend_from_slice(&msg1);
        combined.extend_from_slice(&msg2);
        combined.extend_from_slice(&msg3);

        parser.feed(&combined);

        let (h1, _) = parser.next_message().unwrap();
        assert_eq!(h1.frame_type, FrameType::Ping as u8);

        let (h2, p2) = parser.next_message().unwrap();
        assert_eq!(h2.frame_type, FrameType::SignalSync as u8);
        assert_eq!(p2, b"{\"a\":1}");

        let (h3, _) = parser.next_message().unwrap();
        assert_eq!(h3.frame_type, FrameType::End as u8);

        assert!(parser.next_message().is_none());
    }

    #[test]
    fn stream_parser_partial_feed() {
        let mut parser = StreamParser::new();
        let msg = signal_diff_frame(1, 100, b"{\"x\":42}");

        // Feed in two halves
        let mid = msg.len() / 2;
        parser.feed(&msg[..mid]);
        assert!(parser.next_message().is_none());

        parser.feed(&msg[mid..]);
        let (header, payload) = parser.next_message().unwrap();
        assert_eq!(header.seq, 1);
        assert_eq!(payload, b"{\"x\":42}");
    }

    #[test]
    fn ack_frame_roundtrip() {
        let evt = AckEvent { ack_seq: 99, rtt_ms: 12 };
        let msg = ack_frame(5, 5000, &evt);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::Ack as u8);
        let ack = AckEvent::decode(decoded.payload).unwrap();
        assert_eq!(ack.ack_seq, 99);
        assert_eq!(ack.rtt_ms, 12);
    }

    #[test]
    fn compression_ratio_calc() {
        let (ratio, savings) = compression_ratio(1000, 300);
        assert!((ratio - 0.3).abs() < 0.001);
        assert!((savings - 70.0).abs() < 0.1);

        let (ratio_zero, _) = compression_ratio(0, 0);
        assert!((ratio_zero - 1.0).abs() < 0.001);
    }

    // ─── v0.3 Tests ───

    #[test]
    fn compressed_pixel_frame_roundtrip() {
        let fake_jpeg = vec![0xFF, 0xD8, 0xFF, 0xE0]; // JPEG header
        let msg = compressed_pixel_frame(1, 100, 320, 240, CompressedPixelFormat::Jpeg, &fake_jpeg);
        let decoded = decode_message(&msg).unwrap();
        assert_eq!(decoded.header.frame_type, FrameType::CompressedPixels as u8);
        assert_eq!(decoded.header.width, 320);
        assert_eq!(decoded.header.height, 240);
        assert_eq!(decoded.header.flags & FLAG_COMPRESSED, FLAG_COMPRESSED);
        // First payload byte is format
        assert_eq!(decoded.payload[0], CompressedPixelFormat::Jpeg as u8);
        assert_eq!(&decoded.payload[1..], &fake_jpeg);
    }

    #[test]
    fn recording_writer_reader_roundtrip() {
        let mut buf = Vec::new();

        // Write
        {
            let mut writer = RecordingWriter::new(&mut buf).unwrap();
            let msg1 = signal_diff_frame(1, 100, b"{\"x\":1}");
            let msg2 = ping(2, 200);
            writer.write_entry(1000, &msg1).unwrap();
            writer.write_entry(2000, &msg2).unwrap();
            assert_eq!(writer.entry_count(), 2);
            writer.flush().unwrap();
        }

        // Read
        {
            let cursor = std::io::Cursor::new(&buf);
            let mut reader = RecordingReader::new(cursor).unwrap();
            let entries = reader.read_all().unwrap();
            assert_eq!(entries.len(), 2);
            assert_eq!(entries[0].0, 1000); // timestamp
            assert_eq!(entries[1].0, 2000);

            // Verify the messages are intact
            let decoded = decode_message(&entries[0].1).unwrap();
            assert_eq!(decoded.header.frame_type, FrameType::SignalDiff as u8);
            assert_eq!(reader.entries_read(), 2);
        }
    }

    #[test]
    fn recording_reader_invalid_magic() {
        let buf = vec![0x00, 0x00, 0x00]; // wrong magic
        let result = RecordingReader::new(std::io::Cursor::new(&buf));
        assert!(result.is_err());
    }

    #[test]
    fn compressed_pixel_format_roundtrip() {
        assert_eq!(CompressedPixelFormat::from_u8(0), Some(CompressedPixelFormat::Png));
        assert_eq!(CompressedPixelFormat::from_u8(1), Some(CompressedPixelFormat::WebP));
        assert_eq!(CompressedPixelFormat::from_u8(2), Some(CompressedPixelFormat::Jpeg));
        assert_eq!(CompressedPixelFormat::from_u8(3), None);
        assert_eq!(CompressedPixelFormat::Jpeg.name(), "JPEG");
        assert_eq!(CompressedPixelFormat::WebP.mime_type(), "image/webp");
    }

    // ─── v0.4 Tests ───

    #[test]
    fn adaptive_quality_tiers() {
        let mut aq = AdaptiveQuality::new();
        assert_eq!(aq.current_tier(), QualityTier::Full);
        assert_eq!(aq.suggested_quality(), 80);
        assert_eq!(aq.suggested_skip_frames(), 0);

        // Push RTT into reduced range
        for _ in 0..10 { aq.record_ack(80); }
        assert_eq!(aq.current_tier(), QualityTier::Reduced);
        assert_eq!(aq.suggested_quality(), 50);
        assert_eq!(aq.suggested_skip_frames(), 2);

        // Push into minimal
        for _ in 0..30 { aq.record_ack(200); }
        assert_eq!(aq.current_tier(), QualityTier::Minimal);
        assert_eq!(aq.suggested_quality(), 0);

        // Recover
        for _ in 0..30 { aq.record_ack(10); }
        assert_eq!(aq.current_tier(), QualityTier::Full);
    }

    #[test]
    fn bandwidth_estimator_basic() {
        let mut bw = BandwidthEstimator::new(5);
        bw.record(0, 1000);
        bw.record(1000, 1000);
        bw.record(2000, 1000);
        // 3000 bytes over 2 seconds = 1500 bps
        let bps = bw.estimate_bps();
        assert!((bps - 1500.0).abs() < 1.0, "BPS should be ~1500: {}", bps);
        assert!(!bw.is_congested(1000.0));
        assert!(bw.is_congested(2000.0));
    }

    #[test]
    fn audio_header_roundtrip() {
        let header = AudioHeader {
            format: AudioFormat::Opus,
            channels: 2,
            sample_rate: 48000,
            frame_size: 960,
        };
        let encoded = header.encode();
        let decoded = AudioHeader::decode(&encoded).unwrap();
        assert_eq!(decoded.format, AudioFormat::Opus);
        assert_eq!(decoded.channels, 2);
        assert_eq!(decoded.sample_rate, 48000);
        assert_eq!(decoded.frame_size, 960);
    }

    #[test]
    fn audio_format_enum() {
        assert_eq!(AudioFormat::from_u8(0), Some(AudioFormat::Pcm));
        assert_eq!(AudioFormat::from_u8(1), Some(AudioFormat::Opus));
        assert_eq!(AudioFormat::from_u8(2), Some(AudioFormat::Aac));
        assert_eq!(AudioFormat::from_u8(3), None);
        assert_eq!(AudioFormat::Opus.name(), "Opus");
    }

    // ─── v0.5 Tests ───

    #[test]
    fn auth_payload_roundtrip() {
        let ap = AuthPayload {
            phase: 1,
            nonce: [1, 2, 3, 4, 5, 6, 7, 8],
            token: b"secret-token".to_vec(),
        };
        let encoded = ap.encode();
        let decoded = AuthPayload::decode(&encoded).unwrap();
        assert_eq!(decoded, ap);
    }

    #[test]
    fn xor_scramble_roundtrip() {
        let data = b"hello world";
        let key = b"mykey";
        let scrambled = scramble_payload(data, key);
        assert_ne!(&scrambled, data);
        let restored = descramble_payload(&scrambled, key);
        assert_eq!(&restored, data);
    }

    #[test]
    fn recording_metadata_roundtrip() {
        let meta = RecordingMetadata {
            version: 1,
            created_at: 1710000000,
            duration_ms: 5000,
            frame_count: 150,
            width: 1920,
            height: 1080,
        };
        let encoded = meta.encode();
        let decoded = RecordingMetadata::decode(&encoded).unwrap();
        assert_eq!(decoded, meta);
    }

    #[test]
    fn auth_frame_builders() {
        let nonce = [1u8; 8];
        let challenge = auth_challenge_frame(1, &nonce);
        assert_eq!(challenge[0], FrameType::Auth as u8);
        let payload = AuthPayload::decode(&challenge[HEADER_SIZE..]).unwrap();
        assert_eq!(payload.phase, 0);
        assert_eq!(payload.nonce, nonce);

        let response = auth_response_frame(2, &nonce, b"token123");
        let payload = AuthPayload::decode(&response[HEADER_SIZE..]).unwrap();
        assert_eq!(payload.phase, 1);
        assert_eq!(payload.token, b"token123");
    }

    // ─── v0.10 Tests ───

    #[test]
    fn mux_demux_roundtrip() {
        let frame = encode_frame(FrameType::Pixels, 1, 0, 320, 240, 0, &[0xAA; 10]);
        let muxed = mux_frame(5, &frame);
        assert_eq!(extract_stream_id(muxed[1]), 5);
        let (stream_id, demuxed) = demux_frame(&muxed);
        assert_eq!(stream_id, 5);
        assert_eq!(demuxed[1] & STREAM_ID_MASK, 0); // stream cleared
    }

    #[test]
    fn frame_priority_levels() {
        assert_eq!(prioritize_frame(FrameType::Keyframe as u8), FramePriority::Critical);
        assert_eq!(prioritize_frame(FrameType::Auth as u8), FramePriority::Critical);
        assert_eq!(prioritize_frame(FrameType::Pixels as u8), FramePriority::High);
        assert_eq!(prioritize_frame(FrameType::SignalDiff as u8), FramePriority::Normal);
        assert_eq!(prioritize_frame(FrameType::Ping as u8), FramePriority::Low);
    }

    #[test]
    fn bandwidth_allocation() {
        let streams = vec![
            (0, FramePriority::High),
            (1, FramePriority::Low),
        ];
        let allocs = allocate_bandwidth(5000, &streams);
        assert_eq!(allocs.len(), 2);
        // High (weight 4) should get 4x more than Low (weight 1)
        assert!(allocs[0].byte_limit > allocs[1].byte_limit);
        assert_eq!(allocs[0].byte_limit + allocs[1].byte_limit, 5000);
    }

    #[test]
    fn frame_drop_policy() {
        assert!(!should_drop(FramePriority::Low, 0));      // No congestion: no drops
        assert!(should_drop(FramePriority::Low, 1));       // Mild: drop low
        assert!(!should_drop(FramePriority::High, 1));     // Mild: keep high
        assert!(should_drop(FramePriority::Normal, 2));    // Moderate: drop normal
        assert!(!should_drop(FramePriority::Critical, 3)); // Severe: keep critical
        assert!(should_drop(FramePriority::High, 3));      // Severe: drop high
    }

    #[test]
    fn stream_id_embed_extract() {
        let flags = FLAG_KEYFRAME; // 0x01
        let combined = embed_stream_id(flags, 12);
        assert_eq!(extract_stream_id(combined), 12);
        assert_eq!(combined & FLAGS_MASK, FLAG_KEYFRAME);
    }

    // ─── v0.11 Tests ───

    #[test]
    fn frame_stats_accumulator() {
        let mut stats = FrameStats::new();
        stats.record(1000, false);
        stats.record(2000, false);
        stats.record(500, true); // dropped
        assert_eq!(stats.frame_count, 2);
        assert_eq!(stats.dropped_count, 1);
        assert_eq!(stats.total_bytes, 3000);
        assert!((stats.avg_size() - 1500.0).abs() < 0.1);
        assert_eq!(stats.min_size, 1000);
        assert_eq!(stats.max_size, 2000);
        stats.reset();
        assert_eq!(stats.frame_count, 0);
    }

    #[test]
    fn jitter_buffer_ordering() {
        let mut jb = JitterBuffer::new(4);
        jb.push(2, vec![2]);
        jb.push(0, vec![0]);
        jb.push(1, vec![1]);
        let f0 = jb.pop().unwrap();
        assert_eq!(f0, vec![0]);
        let f1 = jb.pop().unwrap();
        assert_eq!(f1, vec![1]);
        let f2 = jb.pop().unwrap();
        assert_eq!(f2, vec![2]);
    }

    #[test]
    fn latency_tracker_stats() {
        let mut lt = LatencyTracker::new(100);
        for i in 0..10 {
            lt.record_rtt(10.0 + i as f64);
        }
        assert_eq!(lt.sample_count(), 10);
        assert!((lt.avg_rtt() - 14.5).abs() < 0.1);
        assert!(lt.p95_rtt() >= 18.0);
        assert!(lt.jitter() > 0.0);
    }

    #[test]
    fn stream_health_computation() {
        // Good conditions
        let good = compute_health(1000, 50.0, 100_000.0, 0.01);
        assert!(good > 0.7, "Good health: {}", good);
        // Bad conditions
        let bad = compute_health(100, 500.0, 10_000.0, 0.3);
        assert!(bad < 0.5, "Bad health: {}", bad);
    }

    // ─── v0.12 Tests ───

    #[test]
    fn state_snapshot_roundtrip() {
        let state = b"hello world state";
        let encoded = encode_state(42, state);
        let (version, decoded) = decode_state(&encoded).unwrap();
        assert_eq!(version, 42);
        assert_eq!(decoded, state);
    }

    #[test]
    fn gap_detector_basic() {
        let mut gd = GapDetector::new();
        assert_eq!(gd.feed(0), GapResult::Ok);
        assert_eq!(gd.feed(1), GapResult::Ok);
        assert_eq!(gd.feed(3), GapResult::Gap { expected: 2, got: 3 });
        assert_eq!(gd.feed(3), GapResult::Duplicate);
        assert_eq!(gd.feed(4), GapResult::Ok);
    }

    #[test]
    fn handshake_roundtrip() {
        let caps = vec![1, 2, 3];
        let hello = handshake_hello(12, &caps);
        let (version, parsed_caps) = parse_handshake_hello(&hello).unwrap();
        assert_eq!(version, 12);
        assert_eq!(parsed_caps, caps);
        let accept = handshake_accept(12);
        assert_eq!(accept[0], 0x41);
        assert_eq!(u16::from_le_bytes([accept[1], accept[2]]), 12);
    }

    #[test]
    fn state_decode_too_short() {
        assert!(decode_state(&[0, 1, 2]).is_none());
    }

    // ─── v0.13 Tests ───

    #[test]
    fn replay_buffer_circular() {
        let mut rb = ReplayBuffer::new(3);
        rb.push(vec![1]);
        rb.push(vec![2]);
        rb.push(vec![3]);
        assert_eq!(rb.len(), 3);
        rb.push(vec![4]); // overwrites oldest
        assert_eq!(rb.len(), 3);
        assert_eq!(rb.total_pushed(), 4);
        let snap = rb.snapshot();
        assert_eq!(snap.len(), 3);
    }

    #[test]
    fn header_serde_roundtrip() {
        let header = serialize_header(0x02, 0x01, 42, 1000, 800, 600, 256);
        let (ft, fl, seq, ts, w, h, len) = deserialize_header(&header).unwrap();
        assert_eq!(ft, 0x02);
        assert_eq!(fl, 0x01);
        assert_eq!(seq, 42);
        assert_eq!(ts, 1000);
        assert_eq!(w, 800);
        assert_eq!(h, 600);
        assert_eq!(len, 256);
    }

    #[test]
    fn codec_registry_basic() {
        let mut reg = CodecRegistry::new();
        reg.register("jpeg", 1, 1);
        reg.register("webp", 2, 2);
        assert_eq!(reg.count(), 2);
        assert_eq!(reg.list(), vec!["jpeg", "webp"]);
        let codec = reg.get("jpeg").unwrap();
        assert_eq!(codec.encode_id, 1);
    }

    #[test]
    fn replay_buffer_get_range() {
        let mut rb = ReplayBuffer::new(10);
        for i in 0..5 {
            rb.push(vec![i]);
        }
        let range = rb.get_range(1, 3);
        assert_eq!(range.len(), 2);
        assert_eq!(range[0], vec![1]);
        assert_eq!(range[1], vec![2]);
    }

    // ─── v0.14 Tests ───

    #[test]
    fn rle_compress_decompress() {
        let mut comp = FrameCompressor::new();
        let data = vec![0, 0, 0, 1, 1, 2, 2, 2, 2];
        let compressed = comp.compress(&data);
        let decompressed = comp.decompress(&compressed);
        assert_eq!(decompressed, data);
        assert!(comp.compression_ratio() < 1.0, "Should compress: {}", comp.compression_ratio());
    }

    #[test]
    fn multi_client_sync() {
        let mut sync = MultiClientSync::new();
        sync.register_client("a");
        sync.register_client("b");
        sync.update_seq("a", 10);
        sync.update_seq("b", 10);
        sync.update_ack("a", 8);
        sync.update_ack("b", 3);
        assert_eq!(sync.slowest_client(), "b");
        assert_eq!(sync.get_drift("b"), 7);
    }

    #[test]
    fn bandwidth_throttle() {
        let mut throttle = BandwidthThrottle::new(1000);
        assert!(throttle.should_send(500, 0));
        assert!(throttle.should_send(400, 0));
        assert!(!throttle.should_send(200, 0)); // over limit
        assert!(throttle.should_send(200, 1000)); // new window
    }

    #[test]
    fn rle_empty_data() {
        let mut comp = FrameCompressor::new();
        let compressed = comp.compress(&[]);
        assert!(compressed.is_empty());
        let decompressed = comp.decompress(&compressed);
        assert!(decompressed.is_empty());
    }

    // ─── v0.15 Tests ───

    #[test]
    fn adaptive_bitrate_tiers() {
        let mut ab = AdaptiveBitrate::new();
        // Good connection
        for _ in 0..10 { ab.feed_rtt(10.0); ab.feed_loss(0.0); }
        assert_eq!(ab.recommended_tier(), 4);
        // Bad connection
        for _ in 0..30 { ab.feed_rtt(250.0); ab.feed_loss(15.0); }
        assert_eq!(ab.recommended_tier(), 0);
    }

    #[test]
    fn metrics_snapshot_json() {
        let snap = MetricsSnapshot::capture(30.0, 500_000, 3, 2.5, 0.95);
        let json = snap.to_json();
        assert!(json.contains("30.0"), "Has fps: {}", json);
        assert!(json.contains("500000"), "Has bitrate: {}", json);
    }

    #[test]
    fn frame_pipeline_stages() {
        let mut pipe = FramePipeline::new();
        pipe.add_stage("encode");
        pipe.add_stage("compress");
        pipe.add_stage("encrypt");
        assert_eq!(pipe.stage_count(), 3);
        let names = pipe.stage_names();
        assert_eq!(names, vec!["encode", "compress", "encrypt"]);
        let data = vec![1, 2, 3];
        let out = pipe.process(data.clone());
        assert_eq!(out, data); // identity pass-through
    }

    // ─── v0.16 Tests ───

    #[test]
    fn xor_encrypt_decrypt() {
        let data = b"hello world";
        let key = b"secret";
        let encrypted = FrameEncryptor::encrypt(data, key);
        assert_ne!(encrypted, data.to_vec());
        let decrypted = FrameEncryptor::decrypt(&encrypted, key);
        assert_eq!(decrypted, data.to_vec());
    }

    #[test]
    fn stream_migration_handoff() {
        let mut src = StreamMigration::new();
        let state = src.prepare_handoff(42, 5, 30.0);
        let mut dst = StreamMigration::new();
        let (seq, clients, fps) = dst.accept_handoff(&state).unwrap();
        assert_eq!(seq, 42);
        assert_eq!(clients, 5);
        assert!((fps - 30.0).abs() < 0.01);
    }

    #[test]
    fn frame_dedup() {
        let mut dedup = FrameDedup::new(100);
        let frame = vec![1, 2, 3, 4, 5];
        assert!(!dedup.is_duplicate(&frame));
        assert!(dedup.is_duplicate(&frame));
        assert_eq!(dedup.dedup_savings(), 5);
    }

    #[test]
    fn priority_queue_ordering() {
        let mut pq: PriorityQueue<&str> = PriorityQueue::new();
        pq.enqueue("low", 1);
        pq.enqueue("high", 10);
        pq.enqueue("mid", 5);
        assert_eq!(pq.dequeue(), Some("high"));
        assert_eq!(pq.dequeue(), Some("mid"));
        assert_eq!(pq.dequeue(), Some("low"));
    }

    // ─── v0.17 Tests ───

    #[test]
    fn video_codec_adapter() {
        let codec = VideoCodecAdapter::new("h264", &["video/h264", "video/avc"], 0.05);
        assert_eq!(codec.codec_name(), "h264");
        assert!(codec.can_encode("video/h264"));
        assert!(!codec.can_encode("video/vp9"));
        assert!((codec.estimated_ratio() - 0.05).abs() < 0.001);
    }

    #[test]
    fn stream_router_routing() {
        let mut router = StreamRouter::new();
        router.add_route("display");
        router.add_route("record");
        assert_eq!(router.route_count(), 2);
        router.route_frame("display", vec![1, 2, 3]);
        router.route_frame("record", vec![4, 5]);
        let display = router.get_routed("display");
        assert_eq!(display.len(), 1);
        assert_eq!(display[0], vec![1, 2, 3]);
    }

    #[test]
    fn frame_interleaver() {
        let mut il = FrameInterleaver::new();
        il.interleave(0, vec![1, 2]);
        il.interleave(1, vec![3, 4]);
        il.interleave(0, vec![5, 6]);
        assert_eq!(il.pending(), 3);
        let frames = il.deinterleave();
        assert_eq!(frames.len(), 3);
        assert_eq!(frames[0].0, 0);
        assert_eq!(frames[1].0, 1);
    }

    #[test]
    fn backpressure_flow() {
        let mut bp = Backpressure::new(2);
        assert!(bp.try_push(vec![1]));
        assert!(bp.try_push(vec![2]));
        assert!(!bp.try_push(vec![3])); // full
        assert!(bp.is_full());
        let drained = bp.drain();
        assert_eq!(drained.len(), 2);
        assert!(!bp.is_full());
    }

    // ─── v0.18 Tests ───

    #[test]
    fn frame_scheduler() {
        let mut sched = FrameScheduler::new(60.0);
        assert!(sched.should_emit(0.0));
        assert!(!sched.should_emit(8.0));
        assert!(sched.should_emit(17.0));
        assert_eq!(sched.frames_emitted(), 2);
    }

    #[test]
    fn live_preview_downscale() {
        let mut preview = LivePreview::new(0.5);
        let frame = vec![1, 2, 3, 4, 5, 6, 7, 8];
        let p = preview.generate(&frame);
        assert!(p.len() < frame.len(), "Preview smaller");
        assert!(preview.last_preview_size() > 0);
    }

    #[test]
    fn stream_stats_tracking() {
        let mut stats = StreamStats::new();
        stats.record_in(1000);
        stats.record_out(200);
        stats.record_drop();
        assert!((stats.compression_ratio() - 0.2).abs() < 0.01);
        let json = stats.to_json();
        assert!(json.contains("frames_in"));
        assert!(json.contains("drops\":1"));
    }

    // ─── v0.19 Tests ───

    #[test]
    fn audio_mixer_blend() {
        let mut mixer = AudioMixer::new();
        mixer.add_source("mic");
        mixer.add_source("music");
        assert_eq!(mixer.source_count(), 2);
        let mixed = mixer.mix_sources(&[100, 200, 300], &[50, 100]);
        assert_eq!(mixed[0], 75);
        assert_eq!(mixed.len(), 3);
    }

    #[test]
    fn session_store_roundtrip() {
        let mut store = SessionStore::new();
        store.save_state("session1", &[1, 2, 3]);
        assert_eq!(store.load_state("session1"), Some(vec![1, 2, 3]));
        assert_eq!(store.list_keys().len(), 1);
        store.clear();
        assert_eq!(store.load_state("session1"), None);
    }

    #[test]
    fn frame_annotation_roundtrip() {
        let frame = vec![0xFF, 0xAA, 0xBB];
        let annotated = FrameAnnotation::annotate(&frame, "type", "keyframe");
        let (key, val, offset) = FrameAnnotation::read_annotation(&annotated).unwrap();
        assert_eq!(key, "type");
        assert_eq!(val, "keyframe");
        assert_eq!(&annotated[offset..], &frame[..]);
    }

    // ─── v0.20 Tests ───

    #[test]
    fn stream_recorder() {
        let mut rec = StreamRecorder::new();
        rec.start();
        assert!(rec.is_recording());
        rec.push(&[1, 2, 3]);
        rec.push(&[4, 5]);
        rec.stop();
        assert_eq!(rec.frame_count(), 2);
        assert_eq!(rec.get_frame(0), Some(&[1u8, 2, 3][..]));
    }

    #[test]
    fn hot_config() {
        let mut cfg = HotConfig::new();
        cfg.set("fps", "60");
        assert_eq!(cfg.get("fps"), Some("60"));
        assert_eq!(cfg.keys().len(), 1);
    }

    #[test]
    fn frame_pool_alloc_release() {
        let mut pool = FramePool::new(3, 1024);
        assert_eq!(pool.available(), 3);
        let buf = pool.alloc().unwrap();
        assert_eq!(pool.available(), 2);
        pool.release(buf);
        assert_eq!(pool.available(), 3);
    }

    // ─── v0.21 Tests ───

    #[test]
    fn stream_mux() {
        let mut mux = StreamMux::new();
        mux.create_channel("video");
        mux.create_channel("audio");
        mux.push("video", &[1, 2, 3]);
        mux.push("audio", &[4, 5]);
        assert_eq!(mux.channel_count(), 2);
        assert_eq!(mux.pull("video"), Some(vec![1, 2, 3]));
    }

    #[test]
    fn remote_control() {
        let mut rc = RemoteControl::new();
        rc.push_command("pause");
        rc.push_command("resume");
        assert_eq!(rc.pending_count(), 2);
        assert_eq!(rc.pop_command(), Some("pause".to_string()));
    }

    #[test]
    fn frame_hash_verify() {
        let data = vec![1u8, 2, 3, 4, 5];
        let h = FrameHash::hash(&data);
        assert!(FrameHash::verify(&data, h));
        assert!(!FrameHash::verify(&[9, 8, 7], h));
    }

    // ─── v0.22 Tests ───

    #[test]
    fn stream_compressor_roundtrip() {
        let data = vec![0u8; 100];
        let compressed = StreamCompressor::compress(&data);
        assert!(compressed.len() < data.len());
        let decompressed = StreamCompressor::decompress(&compressed);
        assert_eq!(decompressed, data);
    }

    #[test]
    fn telemetry_tracking() {
        let mut t = Telemetry::new();
        t.record_latency(10.0);
        t.record_latency(20.0);
        t.record_error();
        t.record_bytes(1024);
        assert_eq!(t.avg_latency(), 15.0);
        assert_eq!(t.error_count(), 1);
        assert_eq!(t.total_bytes(), 1024);
    }

    #[test]
    fn frame_ring_overflow() {
        let mut ring = FrameRing::new(3);
        ring.push(vec![1]);
        ring.push(vec![2]);
        ring.push(vec![3]);
        ring.push(vec![4]); // overflow, oldest dropped
        assert_eq!(ring.len(), 3);
        assert_eq!(ring.capacity(), 3);
    }

    // ─── v0.23 Tests ───

    #[test]
    fn frame_batcher() {
        let mut b = FrameBatcher::new(3);
        assert!(b.push(vec![1]).is_none());
        assert!(b.push(vec![2]).is_none());
        let batch = b.push(vec![3]).unwrap();
        assert_eq!(batch.len(), 3);
        assert_eq!(b.pending(), 0);
    }

    #[test]
    fn diagnostic_log() {
        let mut log = DiagnosticLog::new();
        log.log("frame_drop");
        log.log("latency_spike");
        assert_eq!(log.count(), 2);
        assert_eq!(log.get(0), Some("frame_drop"));
    }

    #[test]
    fn stream_gate() {
        let mut gate = StreamGate::new(true);
        assert!(gate.filter(&[1, 2, 3]).is_some());
        gate.close();
        assert!(gate.filter(&[1, 2, 3]).is_none());
        gate.open();
        assert!(gate.is_open());
    }

    // ─── v0.24 Tests ───

    #[test]
    fn frame_priority_queue_ordering() {
        let mut pq = FramePriorityQueue::new();
        pq.enqueue(5, vec![5]);
        pq.enqueue(1, vec![1]);
        pq.enqueue(3, vec![3]);
        assert_eq!(pq.dequeue(), Some(vec![1]));
        assert_eq!(pq.dequeue(), Some(vec![3]));
    }

    #[test]
    fn frame_watermark() {
        let mut wm = FrameWatermark::new("v1");
        let stamped = wm.stamp(&[0xAA, 0xBB]);
        assert!(stamped.len() > 2);
        assert_eq!(wm.sequence(), 1);
    }

    #[test]
    fn stream_meter_fps() {
        let mut m = StreamMeter::new(10);
        m.tick(0.0);
        m.tick(1.0);
        m.tick(2.0);
        assert!((m.fps() - 1.0).abs() < 0.01);
        assert_eq!(m.sample_count(), 3);
    }

    // ─── v0.25 Tests ───

    #[test]
    fn frame_replay_buffer() {
        let mut rb = FrameReplayBuffer::new(3);
        rb.record(vec![1]);
        rb.record(vec![2]);
        assert_eq!(rb.frame_count(), 2);
        assert_eq!(rb.replay_next(), Some(&[1u8][..]));
        assert_eq!(rb.replay_next(), Some(&[2u8][..]));
    }

    #[test]
    fn frame_diff_roundtrip() {
        let a = vec![0xAA, 0xBB, 0xCC];
        let b = vec![0xAA, 0x00, 0xCC];
        let d = FrameDiff::diff(&a, &b);
        let restored = FrameDiff::apply(&a, &d);
        assert_eq!(restored, b);
    }

    #[test]
    fn stream_throttle() {
        let mut t = StreamThrottle::new(30.0); // ~33ms interval
        assert!(t.should_pass(0.0));
        assert!(!t.should_pass(10.0)); // too soon
        assert!(t.should_pass(40.0)); // ok
    }

    // ─── v0.26 Tests ───

    #[test]
    fn chunked_transfer() {
        let ct = ChunkedTransfer::new(3);
        let chunks = ct.split(&[1, 2, 3, 4, 5]);
        assert_eq!(chunks.len(), 2);
        let merged = ChunkedTransfer::merge(&chunks);
        assert_eq!(merged, vec![1, 2, 3, 4, 5]);
    }

    #[test]
    fn metadata_annotation() {
        let mut ann = MetadataAnnotation::new();
        ann.set("source", "cam0");
        ann.set("quality", "high");
        assert_eq!(ann.get("source"), Some("cam0"));
        assert_eq!(ann.count(), 2);
    }

    #[test]
    fn stream_metrics_drop_rate() {
        let mut s = StreamMetrics::new();
        s.record_sent(100);
        s.record_sent(200);
        s.record_drop();
        assert!((s.drop_rate() - 1.0 / 3.0).abs() < 0.01);
    }

    // ─── v0.27 Tests ───

    #[test]
    fn double_buffer_swap() {
        let mut db = DoubleBuffer::new();
        db.write(vec![1, 2, 3]);
        assert_eq!(db.read().len(), 0); // front is empty
        db.swap();
        assert_eq!(db.read(), &[1, 2, 3]);
    }

    #[test]
    fn frame_sequencer() {
        let mut seq = FrameSequencer::new();
        assert_eq!(seq.next(), 0);
        assert_eq!(seq.next(), 1);
        assert_eq!(seq.current(), 2);
        seq.reset();
        assert_eq!(seq.next(), 0);
    }

    #[test]
    fn throughput_estimator() {
        let mut bw = ThroughputEstimator::new(10);
        bw.record(0.0, 1000);
        bw.record(1000.0, 1000);
        let bps = bw.estimate_bps();
        assert!(bps > 0.0); // 2000 bytes / 1 sec = 2000 B/s
    }

    // ─── v0.28 Tests ───

    #[test]
    fn buffer_pool_acquire_release() {
        let mut pool = BufferPool::new(2, 64);
        assert_eq!(pool.available(), 2);
        let f = pool.acquire();
        assert_eq!(pool.available(), 1);
        pool.release(f);
        assert_eq!(pool.available(), 2);
    }

    #[test]
    fn packet_jitter_buffer_ordering() {
        let mut jb = PacketJitterBuffer::new();
        jb.insert(1, vec![1]);
        jb.insert(0, vec![0]);
        assert_eq!(jb.try_pop(), Some(vec![0]));
        assert_eq!(jb.try_pop(), Some(vec![1]));
    }

    #[test]
    fn rtt_tracker_avg() {
        let mut lt = RttTracker::new(10);
        lt.record(10.0);
        lt.record(20.0);
        lt.record(30.0);
        assert!((lt.average() - 20.0).abs() < 0.01);
        assert!((lt.max_latency() - 30.0).abs() < 0.01);
    }

    // ─── v0.30 Tests ───

    #[test]
    fn frame_ring_buffer() {
        let mut rb = FrameRingBuffer::new(3);
        rb.push(vec![1]);
        rb.push(vec![2]);
        assert_eq!(rb.occupancy(), 2);
        assert_eq!(rb.latest(), Some(&[2u8][..]));
    }

    #[test]
    fn packet_loss_detector() {
        let mut pld = PacketLossDetector::new();
        pld.observe(0);
        pld.observe(1);
        pld.observe(5); // gap of 3
        assert_eq!(pld.total_lost(), 3);
        assert!(pld.loss_ratio() > 0.0);
    }

    #[test]
    fn connection_quality_score() {
        let good = ConnectionQuality::new(10.0, 0.0);
        let bad = ConnectionQuality::new(400.0, 0.1);
        assert!(good.score() > bad.score());
    }

    // ─── v0.40 Tests ───

    #[test]
    fn quality_adapter_v40() {
        let mut ab = QualityAdapter::new(vec![100, 500, 1000, 2000]);
        assert_eq!(ab.adjust(1.0), 2000);
        assert_eq!(ab.adjust(0.0), 100);
    }

    #[test]
    fn source_mixer_v40() {
        let mut mx = SourceMixer::new();
        mx.add("cam1", vec![1, 2]);
        mx.add("cam2", vec![3, 4]);
        assert_eq!(mx.output(), vec![1, 2, 3, 4]);
        assert_eq!(mx.source_count(), 2);
    }

    #[test]
    fn frame_dedup_v40() {
        let mut dd = FrameDeduplicator::new();
        assert!(dd.check(&[1, 2, 3]));
        assert!(!dd.check(&[1, 2, 3])); // duplicate
        assert!(dd.check(&[4, 5, 6])); // new
        assert_eq!(dd.skipped_count(), 1);
    }

    #[test]
    fn backpressure_v40() {
        let bp = BackpressureController::new(10);
        assert!(!bp.should_pause(5));
        assert!(bp.should_pause(10));
    }

    #[test]
    fn heartbeat_v40() {
        let mut hb = HeartbeatMonitor::new(1000.0);
        hb.ping(0.0);
        assert!(hb.tick(2000.0)); // within 3x
        assert!(!hb.tick(4000.0)); // too late
    }

    #[test]
    fn compression_tracker_v40() {
        let mut ct = CompressionTracker::new();
        ct.record(1000, 500);
        assert!((ct.ratio() - 0.5).abs() < 0.01);
    }

    #[test]
    fn fec_parity_v40() {
        let p = FecEncoder::parity(&[&[0xFF, 0x00], &[0x0F, 0xF0]]);
        assert_eq!(p, vec![0xF0, 0xF0]);
    }

    #[test]
    fn stream_snapshot_v40() {
        let snap = StreamSnapshot::new(100, 5000, 0.02, 0.95);
        let json = snap.to_json();
        assert!(json.contains("\"frames\":100"));
    }

    #[test]
    fn adaptive_priority_queue_v40() {
        let mut pq = AdaptivePriorityQueue::new();
        pq.push(1, 1000.0, vec![10]);
        pq.push(5, 1000.0, vec![50]);
        let item = pq.pop(0.0).unwrap();
        assert_eq!(item, vec![50]); // higher priority first
    }

    // ─── v0.50 Tests ───

    #[test]
    fn stream_cipher_v50() {
        let c = StreamCipher::new(vec![0xAB, 0xCD]);
        let plain = b"hello";
        let enc = c.encrypt(plain);
        let dec = c.decrypt(&enc);
        assert_eq!(dec, plain);
    }

    #[test]
    fn channel_mux_demux_v50() {
        let mut mux = ChannelMux::new();
        mux.send(0, vec![1, 2]);
        mux.send(1, vec![3, 4, 5]);
        let packed = mux.pack();
        let unpacked = ChannelDemux::unpack(&packed);
        assert_eq!(unpacked.len(), 2);
        assert_eq!(unpacked[0].0, 0);
        assert_eq!(unpacked[1].1, vec![3, 4, 5]);
    }

    #[test]
    fn frame_pacer_v50() {
        let mut p = FramePacer::new(16.66);
        assert!(p.should_emit(0.0));
        assert!(!p.should_emit(10.0));
        assert!(p.should_emit(17.0));
    }

    #[test]
    fn congestion_window_v50() {
        let mut cw = CongestionWindow::new(10.0, 1.0, 100.0);
        cw.on_ack();
        assert!((cw.window() - 11.0).abs() < 0.01);
        cw.on_loss();
        assert!((cw.window() - 5.5).abs() < 0.01);
    }

    #[test]
    fn flow_controller_v50() {
        let mut fc = FlowController::new(100.0, 1000.0);
        assert!(fc.consume(50.0, 0.0));
        assert!(fc.consume(50.0, 0.0));
        assert!(!fc.consume(1.0, 0.0)); // no tokens left
    }

    #[test]
    fn protocol_negotiator_v50() {
        let pn = ProtocolNegotiator::new(vec!["compress".into(), "encrypt".into()]);
        let remote = vec!["encrypt".into(), "multiplex".into()];
        let agreed = pn.negotiate(&remote);
        assert_eq!(agreed, vec!["encrypt".to_string()]);
    }

    #[test]
    fn replay_recorder_v50() {
        let mut rr = ReplayRecorder::new();
        rr.record(0.0, vec![1, 2]);
        rr.record(16.0, vec![3, 4]);
        assert_eq!(rr.frame_count(), 2);
        assert_eq!(rr.get_frame(0), Some(&[1u8, 2][..]));
    }

    #[test]
    fn bandwidth_shaper_v50() {
        let mut bs = BandwidthShaper::new(1000.0);
        assert!(bs.allow(500.0, 0.0));
        assert!(bs.allow(500.0, 0.0));
        assert!(!bs.allow(1.0, 0.0));
        assert!(bs.allow(500.0, 1000.0)); // new window
    }

    #[test]
    fn cipher_roundtrip_v50() {
        let c = StreamCipher::new(vec![0xFF]);
        let data = vec![0x00, 0x42, 0xFF];
        assert_eq!(c.decrypt(&c.encrypt(&data)), data);
    }

    // ─── v0.60 Tests ───

    #[test]
    fn crc32_v60() {
        let crc = Crc32Checker::checksum(b"hello");
        assert!(Crc32Checker::verify(b"hello", crc));
        assert!(!Crc32Checker::verify(b"hellp", crc));
    }

    #[test]
    fn conn_state_v60() {
        let mut csm = ConnectionStateMachine::new();
        assert_eq!(*csm.state(), ConnState::Connecting);
        csm.connect();
        assert_eq!(*csm.state(), ConnState::Connected);
        csm.disconnect();
        assert_eq!(*csm.state(), ConnState::Disconnected);
        csm.reconnect();
        assert_eq!(csm.retries(), 1);
    }

    #[test]
    fn packet_sequencer_v60() {
        let mut ps = PacketSequencer::new();
        assert_eq!(ps.next_seq(), 0);
        assert_eq!(ps.next_seq(), 1);
        ps.receive(5);
        assert!(ps.gap_count() > 0);
    }

    #[test]
    fn stream_resumer_v60() {
        let mut sr = StreamResumer::new();
        sr.mark(42);
        assert_eq!(sr.resume_point(), 42);
    }

    #[test]
    fn frame_interpolator_v60() {
        assert!((FrameInterpolator::lerp(0.0, 100.0, 0.5) - 50.0).abs() < 0.01);
        let result = FrameInterpolator::lerp_bytes(&[0, 100], &[100, 200], 0.5);
        assert_eq!(result, vec![50, 150]);
    }

    #[test]
    fn error_recovery_v60() {
        let er = ErrorRecovery::new(5);
        assert!((er.delay_ms(0) - 100.0).abs() < 0.01);
        assert!((er.delay_ms(3) - 800.0).abs() < 0.01);
        assert!(er.delay_ms(5) < 0.0); // exceeded max
    }

    #[test]
    fn peer_router_v60() {
        let mut pr = PeerRouter::new();
        pr.send("alice", vec![1, 2]);
        pr.send("bob", vec![3, 4]);
        assert_eq!(pr.peer_count(), 2);
        assert_eq!(pr.recv("alice"), Some(vec![1, 2]));
    }

    #[test]
    fn stats_aggregator_v60() {
        let mut sa = StatsAggregator::new(10);
        for i in 1..=5 { sa.record(i as f64); }
        assert!((sa.avg() - 3.0).abs() < 0.01);
        assert!((sa.min() - 1.0).abs() < 0.01);
        assert!((sa.max() - 5.0).abs() < 0.01);
    }

    #[test]
    fn rle_compress_v60() {
        let data = vec![0, 0, 0, 1, 1, 2];
        let compressed = PacketCompressor::compress(&data);
        let decompressed = PacketCompressor::decompress(&compressed);
        assert_eq!(decompressed, data);
    }

    // ─── v0.70 Tests ───

    #[test]
    fn protocol_version_v70() {
        let v1 = ProtocolVersion::new(0, 70, 0);
        let v2 = ProtocolVersion::new(0, 71, 0);
        assert!(v1.compatible(&v2));
        assert_eq!(v1.to_string_v(), "0.70.0");
    }

    #[test]
    fn qos_level_v70() {
        assert_eq!(QosLevel::Realtime.priority(), 4);
        assert!(QosLevel::Realtime > QosLevel::Normal);
        assert_eq!(QosLevel::from_u8(3), QosLevel::High);
    }

    #[test]
    fn frame_signer_v70() {
        let signer = FrameSigner::new(vec![0xAB, 0xCD]);
        let sig = signer.sign(b"hello");
        assert!(signer.verify(b"hello", &sig));
        assert!(!signer.verify(b"hellp", &sig));
    }

    #[test]
    fn rate_limiter_v70() {
        let mut rl = RateLimiter::new(2.0);
        assert!(rl.check(0.0));
        assert!(rl.check(0.0));
        assert!(!rl.check(0.0));
        assert!(rl.check(1000.0)); // new window
    }

    #[test]
    fn stream_splitter_v70() {
        let chunks = StreamSplitter::split(&[1, 2, 3, 4, 5, 6], 3);
        assert_eq!(chunks.len(), 3);
        assert_eq!(chunks[0], vec![1, 2]);
    }

    #[test]
    fn watermark_v70() {
        let mut wm = WatermarkTracker::new(10.0, 90.0);
        wm.update(50.0);
        assert_eq!(wm.status(), "normal");
        wm.update(95.0);
        assert_eq!(wm.status(), "high");
        wm.update(5.0);
        assert_eq!(wm.status(), "low");
    }

    #[test]
    fn packet_reorderer_v70() {
        let mut pr = PacketReorderer::new();
        pr.push(1, vec![2]);
        pr.push(0, vec![1]);
        assert_eq!(pr.pop(), Some(vec![1]));
        assert_eq!(pr.pop(), Some(vec![2]));
    }

    #[test]
    fn timeout_monitor_v70() {
        let mut tm = TimeoutMonitor::new(100.0);
        tm.start(1, 0.0);
        tm.start(2, 50.0);
        let expired = tm.check_expired(150.0);
        assert!(expired.contains(&1));
    }

    #[test]
    fn delta_encoder_v70() {
        let prev = vec![10, 20, 30];
        let cur = vec![12, 22, 30];
        let delta = DeltaEncoder::encode(&prev, &cur);
        let decoded = DeltaEncoder::decode(&prev, &delta);
        assert_eq!(decoded, cur);
    }

    // ─── v0.75 Tests ───

    #[test]
    fn journal_v75() {
        let mut j = StreamJournal::new();
        j.append("frame_sent");
        j.append("ack_recv");
        assert_eq!(j.len(), 2);
        assert_eq!(j.get(0), Some("frame_sent"));
    }

    #[test]
    fn hot_config_v75() {
        let mut c = HotConfigV2::new();
        c.set("fps", "30");
        assert_eq!(c.get("fps"), Some("30"));
        c.set("fps", "60");
        assert_eq!(c.generation(), 2);
    }

    #[test]
    fn dedup_v2_v75() {
        let mut d = FrameDedupV2::new(100);
        assert!(!d.is_dup(b"frame1"));
        assert!(d.is_dup(b"frame1"));
        assert!(!d.is_dup(b"frame2"));
    }

    #[test]
    fn histogram_v75() {
        let mut h = MetricHistogram::new(&[10.0, 50.0, 100.0]);
        h.record(5.0);
        h.record(25.0);
        h.record(75.0);
        let p50 = h.percentile(50.0);
        assert!(p50 <= 50.0);
    }

    #[test]
    fn circuit_breaker_v75() {
        let mut cb = CircuitBreaker::new(3);
        assert!(cb.is_allowed());
        cb.record_failure();
        cb.record_failure();
        cb.record_failure();
        assert!(!cb.is_allowed());
        assert_eq!(*cb.state(), BreakerState::Open);
        cb.try_half_open();
        assert_eq!(*cb.state(), BreakerState::HalfOpen);
        cb.record_success();
        assert_eq!(*cb.state(), BreakerState::Closed);
    }

    #[test]
    fn token_bucket_v2_v75() {
        let mut tb = TokenBucketV2::new(10.0, 5.0);
        assert!(tb.take(5.0, 0.0));
        assert!(tb.take(5.0, 0.0));
        assert!(!tb.take(5.0, 0.0));
        assert!(tb.take(5.0, 1000.0)); // refilled
    }

    #[test]
    fn batch_accumulator_v75() {
        let mut ba = BatchAccumulator::new(3, 1000);
        assert!(!ba.add(vec![1]));
        assert!(!ba.add(vec![2]));
        assert!(ba.add(vec![3])); // threshold
        let flushed = ba.flush();
        assert_eq!(flushed.len(), 3);
    }

    #[test]
    fn checksum_chain_v75() {
        let mut cc = ChecksumChain::new();
        let c1 = cc.feed(b"hello");
        let c2 = cc.feed(b"world");
        assert_ne!(c1, c2); // chain evolves
    }

    #[test]
    fn ping_monitor_v75() {
        let mut pm = PingMonitor::new(0.2);
        pm.record(100.0);
        pm.record(50.0);
        assert!(pm.smoothed_rtt() > 0.0);
        assert_eq!(pm.sample_count(), 2);
    }

    // ─── v0.80 Tests ───

    #[test]
    fn abr_controller_v80() {
        let mut abr = AbrController::new(vec![500, 1000, 2000, 4000]);
        assert_eq!(abr.current_bitrate(), 4000);
        abr.adjust(10.0, 300.0); // bad
        assert!(abr.current_bitrate() < 4000);
    }

    #[test]
    fn jitter_buffer_v80() {
        let mut jb = JitterBufferV2::new(2);
        jb.push(vec![1]);
        jb.push(vec![2]);
        assert_eq!(jb.pop(), None); // not enough buffered
        jb.push(vec![3]);
        assert_eq!(jb.pop(), Some(vec![1]));
    }

    #[test]
    fn frame_counter_v80() {
        let mut fc = FrameCounter::new(1000.0);
        fc.tick(0.0, 100);
        fc.tick(16.0, 100);
        assert!(fc.fps() >= 2.0);
    }

    #[test]
    fn event_bus_v80() {
        let mut eb = EventBus::new();
        eb.emit("connect");
        eb.emit("data");
        assert_eq!(eb.count(), 2);
        let events = eb.drain();
        assert_eq!(events.len(), 2);
        assert_eq!(eb.count(), 0);
    }

    #[test]
    fn stream_tee_v80() {
        let tee = StreamTee::new(3);
        let copies = tee.tee(b"hello");
        assert_eq!(copies.len(), 3);
        assert_eq!(copies[0], copies[1]);
    }

    #[test]
    fn gain_controller_v80() {
        let mut gc = GainController::new(0.3);
        let out = gc.apply(50.0, 100.0);
        assert!(out > 50.0); // should be amplified
    }

    #[test]
    fn lossy_queue_v80() {
        let mut lq = LossyQueue::new(2);
        lq.push(vec![1]);
        lq.push(vec![2]);
        lq.push(vec![3]); // drops [1]
        assert_eq!(lq.len(), 2);
        assert_eq!(lq.pop(), Some(vec![2]));
    }

    #[test]
    fn header_builder_v80() {
        let hdr = HeaderBuilder::build(42, 1, 0x80);
        assert_eq!(hdr.len(), 6);
        let (seq, ch, flags) = HeaderBuilder::parse(&hdr).unwrap();
        assert_eq!(seq, 42);
        assert_eq!(ch, 1);
        assert_eq!(flags, 0x80);
    }

    #[test]
    fn session_tracker_v80() {
        let mut st = SessionTracker::new(1001, 0.0);
        st.touch(5000.0);
        assert_eq!(st.duration_ms(), 5000.0);
        assert_eq!(st.id(), 1001);
    }

    // ─── v0.90 Tests ───

    #[test]
    fn xor_cipher_v2_v90() {
        let cipher = XorCipherV2::new(vec![0xAA, 0xBB]);
        let enc = cipher.apply(b"hello");
        let dec = cipher.apply(&enc);
        assert_eq!(dec, b"hello");
    }

    #[test]
    fn channel_router_v90() {
        let mut cr = ChannelRouter::new();
        cr.route(1, vec![10]);
        cr.route(2, vec![20]);
        cr.route(1, vec![11]);
        assert_eq!(cr.channel_count(), 2);
        let ch1 = cr.drain(1);
        assert_eq!(ch1.len(), 2);
    }

    #[test]
    fn ack_tracker_v90() {
        let mut at = AckTracker::new();
        at.send(0);
        at.send(1);
        assert_eq!(at.pending(), 2);
        at.ack(0);
        assert!(at.is_acked(0));
        assert_eq!(at.pending(), 1);
    }

    #[test]
    fn frame_pool_v90() {
        let mut fp = FramePoolV2::new(1024, 4);
        assert_eq!(fp.available(), 4);
        let buf = fp.alloc();
        assert_eq!(fp.available(), 3);
        fp.free(buf);
        assert_eq!(fp.available(), 4);
    }

    #[test]
    fn bandwidth_estimator_v90() {
        let mut be = BandwidthEstimatorV2::new(0.3);
        be.sample(1000, 100.0); // 80kbps
        assert!(be.estimate() > 0.0);
    }

    #[test]
    fn priority_mux_v90() {
        let mut mux = PriorityMux::new();
        mux.push(1, vec![0x01]);
        mux.push(3, vec![0x03]);
        mux.push(2, vec![0x02]);
        assert_eq!(mux.pop(), Some(vec![0x03])); // highest priority first
    }

    #[test]
    fn nonce_generator_v90() {
        let mut ng = NonceGenerator::new();
        assert_eq!(ng.next(), 1);
        assert_eq!(ng.next(), 2);
        assert_eq!(ng.current(), 2);
    }

    #[test]
    fn stream_validator_v90() {
        let mut sv = StreamValidator::new();
        assert!(sv.validate(0));
        assert!(sv.validate(1));
        assert!(!sv.validate(5)); // gap
        assert_eq!(sv.error_count(), 1);
    }

    #[test]
    fn retry_queue_v90() {
        let mut rq = RetryQueue::new(3);
        rq.push(vec![1, 2]);
        assert_eq!(rq.pending(), 1);
        let data = rq.pop_for_retry().unwrap();
        assert_eq!(data, vec![1, 2]);
        rq.remove_acked(&[1, 2]);
        assert_eq!(rq.pending(), 0);
    }

    // ─── v0.95 Tests ───

    #[test]
    fn lz4_lite_v95() {
        let data = vec![0u8; 50];
        let compressed = Lz4Lite::compress(&data);
        assert!(compressed.len() < data.len());
        let decompressed = Lz4Lite::decompress(&compressed);
        assert_eq!(decompressed, data);
    }

    #[test]
    fn telemetry_sink_v95() {
        let mut ts = TelemetrySink::new();
        ts.emit("fps", 60.0);
        ts.emit("bitrate", 5000.0);
        assert_eq!(ts.count(), 2);
        let events = ts.drain();
        assert_eq!(events.len(), 2);
    }

    #[test]
    fn frame_differ_v95() {
        let a = vec![10, 20, 30];
        let b = vec![10, 25, 30];
        let diff = FrameDiffer::diff(&a, &b);
        let restored = FrameDiffer::apply(&a, &diff);
        assert_eq!(restored, b);
    }

    #[test]
    fn backoff_timer_v95() {
        let mut bt = BackoffTimer::new(100.0, 10000.0);
        assert_eq!(bt.next(), 100.0);
        assert_eq!(bt.next(), 200.0);
        assert_eq!(bt.next(), 400.0);
        bt.reset();
        assert_eq!(bt.next(), 100.0);
    }

    #[test]
    fn stream_mirror_v95() {
        let mut sm = StreamMirror::new();
        sm.push(vec![1, 2]);
        assert_eq!(sm.pop_primary(), Some(vec![1, 2]));
        assert_eq!(sm.pop_mirror(), Some(vec![1, 2]));
    }

    #[test]
    fn quota_manager_v95() {
        let mut qm = QuotaManager::new(100);
        assert!(qm.consume(50));
        assert!(qm.consume(50));
        assert!(!qm.consume(1));
        qm.reset();
        assert_eq!(qm.remaining(), 100);
    }

    #[test]
    fn heartbeat_v2_v95() {
        let mut hb = HeartbeatV2::new();
        hb.ping(100.0);
        hb.pong(115.0);
        assert_eq!(hb.rtt(), 15.0);
        assert!(hb.is_alive(120.0, 30.0));
    }

    #[test]
    fn tag_filter_v95() {
        let mut tf = TagFilter::new(vec!["video".into()]);
        assert!(tf.accept("video"));
        assert!(!tf.accept("audio"));
        tf.add("audio");
        assert!(tf.accept("audio"));
    }

    #[test]
    fn moving_average_v95() {
        let mut ma = MovingAverage::new(3);
        ma.push(10.0);
        ma.push(20.0);
        ma.push(30.0);
        assert!((ma.get() - 20.0).abs() < 0.01);
        ma.push(40.0); // drops 10
        assert!((ma.get() - 30.0).abs() < 0.01);
    }

    // ─── v1.0 Tests ───

    #[test]
    fn pipeline_v100() {
        let mut p = StreamPipeline::new();
        p.add("encode");
        p.add("compress");
        assert_eq!(p.count(), 2);
    }

    #[test]
    fn protocol_header_v100() {
        let hdr = ProtocolHeader::encode(1, 0x80, 42);
        assert_eq!(hdr.len(), 7);
        let (v, f, s) = ProtocolHeader::decode(&hdr).unwrap();
        assert_eq!(v, 1);
        assert_eq!(f, 0x80);
        assert_eq!(s, 42);
    }

    #[test]
    fn frame_splitter_v2_v100() {
        let sp = FrameSplitterV2::new(3);
        let chunks = sp.split(&[1, 2, 3, 4, 5]);
        assert_eq!(chunks.len(), 2);
        let reassembled = FrameSplitterV2::reassemble(&chunks);
        assert_eq!(reassembled, vec![1, 2, 3, 4, 5]);
    }

    #[test]
    fn congestion_window_v100() {
        let mut cw = CongestionWindowV2::new();
        let w1 = cw.on_ack(); // slow start: 2.0
        assert_eq!(w1, 2.0);
        cw.on_loss();
        assert_eq!(cw.window(), 1.0);
    }

    #[test]
    fn stream_stats_v2_v100() {
        let mut ss = StreamStatsV2::new(0.0);
        ss.record(1000);
        ss.record(2000);
        ss.error();
        let s = ss.summary(5000.0);
        assert!(s.contains("frames=2"));
    }

    #[test]
    fn ack_window_v100() {
        let mut aw = AckWindow::new(8);
        assert!(aw.ack(0));
        assert!(aw.ack(1));
        let base = aw.advance();
        assert_eq!(base, 2);
    }

    #[test]
    fn codec_registry_v100() {
        let mut cr = CodecRegistryV2::new();
        cr.register("webp");
        cr.register("jpeg");
        cr.register("webp"); // dup
        assert_eq!(cr.list().len(), 2);
        assert!(cr.has("webp"));
    }

    #[test]
    fn flow_controller_v100() {
        let mut fc = FlowControllerV2::new(100);
        assert!(fc.consume(50));
        assert!(fc.consume(50));
        assert!(!fc.consume(1));
        fc.replenish(50);
        assert_eq!(fc.available(), 50);
    }

    #[test]
    fn version_negotiator_v100() {
        let vn = VersionNegotiator::new(vec!["0.9".into(), "1.0".into()]);
        assert_eq!(vn.negotiate("1.0"), "1.0");
        assert_eq!(vn.negotiate("2.0"), "1.0"); // fallback
    }
}






















