//! `BfldFrame::from_bytes` trailing-bytes contract. Pins the current
//! behavior: the parser reads exactly `header.payload_len` bytes after the
//! header and silently ignores anything past `BFLD_HEADER_SIZE +
//! header.payload_len`. This matches how the parser is used in iter-4
//! through iter-15: callers hand a sliced buffer that may include framing
//! noise (UDP MTU padding, ESP-NOW trailer alignment), and the parser
//! extracts only what the header declares.
//!
//! If a future iter decides to tighten this (reject trailing bytes as
//! `MalformedFrame`), updating this test makes the policy change deliberate
//! and traceable rather than silent.

#![cfg(feature = "std")]

use wifi_densepose_bfld::{BfldFrame, BfldFrameHeader, BfldPayload, BFLD_HEADER_SIZE};

fn frame_with_typed_payload() -> BfldFrame {
    let payload = BfldPayload {
        compressed_angle_matrix: vec![0x11; 32],
        amplitude_proxy: vec![0x22; 16],
        phase_proxy: vec![0x33; 16],
        snr_vector: vec![0x44; 8],
        csi_delta: None,
        vendor_extension: vec![],
    };
    BfldFrame::from_payload(BfldFrameHeader::empty(), &payload)
}

#[test]
fn parser_accepts_buffer_with_one_trailing_byte() {
    let frame = frame_with_typed_payload();
    let mut bytes = frame.to_bytes();
    let canonical_len = bytes.len();
    bytes.push(0xFF);
    let parsed = BfldFrame::from_bytes(&bytes).expect("trailing byte must be tolerated");
    assert_eq!(
        parsed.payload.len(),
        { parsed.header.payload_len } as usize,
        "parsed payload size must equal header.payload_len, not buffer.len() - HEADER",
    );
    // Implicit: the trailing 0xFF byte is NOT in parsed.payload.
    assert_ne!(parsed.payload.last().copied(), Some(0xFF));
    let _ = canonical_len; // sanity anchor
}

#[test]
fn parser_accepts_many_trailing_bytes() {
    let frame = frame_with_typed_payload();
    let mut bytes = frame.to_bytes();
    bytes.extend_from_slice(&[0xCC; 256]);
    let parsed = BfldFrame::from_bytes(&bytes).expect("256 trailing bytes must be tolerated");
    assert_eq!(parsed.payload.len(), { parsed.header.payload_len } as usize);
}

#[test]
fn parsed_payload_round_trips_back_to_typed_payload_with_trailing_bytes_present() {
    // The trailing-bytes parser leniency must not corrupt the section parser
    // downstream. After from_bytes + parse_payload, the typed payload should
    // match the original BfldPayload byte-for-byte.
    let original_payload = BfldPayload {
        compressed_angle_matrix: vec![0x11; 32],
        amplitude_proxy: vec![0x22; 16],
        phase_proxy: vec![0x33; 16],
        snr_vector: vec![0x44; 8],
        csi_delta: None,
        vendor_extension: vec![],
    };
    let frame = BfldFrame::from_payload(BfldFrameHeader::empty(), &original_payload);
    let mut bytes = frame.to_bytes();
    bytes.extend_from_slice(&[0xEE; 64]);
    let parsed_frame = BfldFrame::from_bytes(&bytes).unwrap();
    let parsed_payload = parsed_frame.parse_payload().expect("typed payload parse");
    assert_eq!(parsed_payload, original_payload);
}

#[test]
fn header_only_buffer_at_exactly_header_size_with_zero_payload_len_succeeds() {
    let header = BfldFrameHeader::empty();
    let frame = BfldFrame::new(header, Vec::new());
    let bytes = frame.to_bytes();
    assert_eq!(bytes.len(), BFLD_HEADER_SIZE, "empty-payload frame is exactly header size");
    let parsed = BfldFrame::from_bytes(&bytes).expect("parse");
    assert!(parsed.payload.is_empty());
}

#[test]
fn header_only_buffer_with_trailing_bytes_but_zero_payload_len_ignores_them() {
    let header = BfldFrameHeader::empty();
    let frame = BfldFrame::new(header, Vec::new());
    let mut bytes = frame.to_bytes();
    bytes.extend_from_slice(&[0xAA; 100]);
    let parsed = BfldFrame::from_bytes(&bytes).expect("parse");
    assert_eq!({ parsed.header.payload_len }, 0);
    assert!(parsed.payload.is_empty(), "trailing bytes must not leak into payload");
}

#[test]
fn trailing_bytes_do_not_affect_crc_validation_when_payload_intact() {
    let frame = frame_with_typed_payload();
    let mut bytes = frame.to_bytes();
    let crc_before_extension = { frame.header.payload_crc32 };
    bytes.extend_from_slice(&[0xFF; 32]);
    let parsed = BfldFrame::from_bytes(&bytes).expect("CRC over payload-only must still match");
    assert_eq!({ parsed.header.payload_crc32 }, crc_before_extension);
}
