use crc32c::crc32c;

use crate::crypto::KdfParams;
use crate::format::{
    AeadAlgo, BlockKind, CompressionAlgo, FecAlgo, FormatError, KdfAlgo, BLOCK_RECORD_FRAMING_LEN,
    BOOTSTRAP_SIDECAR_HEADER_LEN, CRYPTO_EXTENSION_HEADER_LEN, CRYPTO_EXTENSION_MAX_VALUE_LEN,
    CRYPTO_HEADER_FIXED_LEN, CRYPTO_HEADER_HMAC_LEN, FORMAT_VERSION, MANIFEST_FOOTER_LEN,
    READER_MAX_BLOCK_SIZE, READER_MAX_CHUNK_SIZE, READER_MAX_CRYPTO_HEADER_LEN,
    READER_MAX_ENVELOPE_TARGET_SIZE, READER_MAX_FEC_CLASS_SHARDS,
    READER_MAX_INDEX_FEC_CLASS_SHARDS, READER_MAX_INDEX_ROOT_FEC_CLASS_SHARDS,
    READER_MAX_PATH_LENGTH, READER_MAX_STRIPE_WIDTH, VOLUME_FORMAT_REV, VOLUME_HEADER_LEN,
    VOLUME_TRAILER_LEN,
};

const TZAP_MAGIC: [u8; 4] = *b"TZAP";
const TZCH_MAGIC: [u8; 4] = *b"TZCH";
const TZBK_MAGIC: [u8; 4] = *b"TZBK";
const TZMF_MAGIC: [u8; 4] = *b"TZMF";
const TZVT_MAGIC: [u8; 4] = *b"TZVT";
const TZBS_MAGIC: [u8; 4] = *b"TZBS";

const BLOCK_LAST_DATA_FLAG: u8 = 0x01;
const BLOCK_RESERVED_FLAGS: u8 = !BLOCK_LAST_DATA_FLAG;

const SIDECAR_MANIFEST_PRESENT: u32 = 0x01;
const SIDECAR_INDEX_ROOT_PRESENT: u32 = 0x02;
const SIDECAR_DICTIONARY_PRESENT: u32 = 0x04;
const SIDECAR_KNOWN_FLAGS: u32 =
    SIDECAR_MANIFEST_PRESENT | SIDECAR_INDEX_ROOT_PRESENT | SIDECAR_DICTIONARY_PRESENT;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VolumeHeader {
    pub format_version: u16,
    pub volume_format_rev: u16,
    pub volume_index: u32,
    pub stripe_width: u32,
    pub archive_uuid: [u8; 16],
    pub session_id: [u8; 16],
    pub crypto_header_offset: u32,
    pub crypto_header_length: u32,
    pub header_crc32c: u32,
}

impl VolumeHeader {
    pub fn parse(bytes: &[u8]) -> Result<Self, FormatError> {
        expect_len("VolumeHeader", VOLUME_HEADER_LEN, bytes.len())?;
        expect_magic("VolumeHeader", TZAP_MAGIC, &bytes[0..4])?;
        expect_crc("VolumeHeader", &bytes[..124], read_u32(bytes, 124)?)?;
        expect_zero("VolumeHeader", &bytes[56..124])?;

        let header = Self {
            format_version: read_u16(bytes, 4)?,
            volume_format_rev: read_u16(bytes, 6)?,
            volume_index: read_u32(bytes, 8)?,
            stripe_width: read_u32(bytes, 12)?,
            archive_uuid: read_array_16(bytes, 16)?,
            session_id: read_array_16(bytes, 32)?,
            crypto_header_offset: read_u32(bytes, 48)?,
            crypto_header_length: read_u32(bytes, 52)?,
            header_crc32c: read_u32(bytes, 124)?,
        };
        header.validate()?;
        Ok(header)
    }

    pub fn validate(&self) -> Result<(), FormatError> {
        if self.format_version != FORMAT_VERSION {
            return Err(FormatError::UnsupportedFormatVersion(self.format_version));
        }
        if self.volume_format_rev != VOLUME_FORMAT_REV {
            return Err(FormatError::UnsupportedVolumeFormatRevision(
                self.volume_format_rev,
            ));
        }
        if self.stripe_width == 0 {
            return Err(FormatError::ZeroStripeWidth);
        }
        if self.volume_index >= self.stripe_width {
            return Err(FormatError::VolumeIndexOutOfRange {
                volume_index: self.volume_index,
                stripe_width: self.stripe_width,
            });
        }
        if self.crypto_header_offset != VOLUME_HEADER_LEN as u32 {
            return Err(FormatError::NonCanonicalCryptoHeaderOffset(
                self.crypto_header_offset,
            ));
        }
        if self.crypto_header_length > READER_MAX_CRYPTO_HEADER_LEN {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "CryptoHeader length",
                cap: READER_MAX_CRYPTO_HEADER_LEN as u64,
                actual: self.crypto_header_length as u64,
            });
        }
        Ok(())
    }

    pub fn to_bytes(&self) -> [u8; VOLUME_HEADER_LEN] {
        let mut bytes = [0u8; VOLUME_HEADER_LEN];
        bytes[0..4].copy_from_slice(&TZAP_MAGIC);
        write_u16(&mut bytes, 4, self.format_version);
        write_u16(&mut bytes, 6, self.volume_format_rev);
        write_u32(&mut bytes, 8, self.volume_index);
        write_u32(&mut bytes, 12, self.stripe_width);
        bytes[16..32].copy_from_slice(&self.archive_uuid);
        bytes[32..48].copy_from_slice(&self.session_id);
        write_u32(&mut bytes, 48, self.crypto_header_offset);
        write_u32(&mut bytes, 52, self.crypto_header_length);
        let crc = crc32c(&bytes[..124]);
        write_u32(&mut bytes, 124, crc);
        bytes
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CryptoHeaderFixed {
    pub length: u32,
    pub compression_algo: CompressionAlgo,
    pub aead_algo: AeadAlgo,
    pub fec_algo: FecAlgo,
    pub kdf_algo: KdfAlgo,
    pub chunk_size: u32,
    pub envelope_target_size: u32,
    pub block_size: u32,
    pub fec_data_shards: u16,
    pub fec_parity_shards: u16,
    pub index_fec_data_shards: u16,
    pub index_fec_parity_shards: u16,
    pub index_root_fec_data_shards: u16,
    pub index_root_fec_parity_shards: u16,
    pub stripe_width: u32,
    pub volume_loss_tolerance: u8,
    pub bit_rot_buffer_pct: u8,
    pub has_dictionary: u8,
    pub max_path_length: u32,
    pub expected_volume_size: u64,
}

impl CryptoHeaderFixed {
    pub fn parse(bytes: &[u8], volume_crypto_header_length: u32) -> Result<Self, FormatError> {
        expect_len("CryptoHeaderFixed", CRYPTO_HEADER_FIXED_LEN, bytes.len())?;
        expect_magic("CryptoHeaderFixed", TZCH_MAGIC, &bytes[0..4])?;
        expect_zero("CryptoHeaderFixed", &bytes[47..48])?;
        expect_zero("CryptoHeaderFixed", &bytes[60..76])?;

        let length = read_u32(bytes, 4)?;
        if length != volume_crypto_header_length {
            return Err(FormatError::CryptoHeaderLengthMismatch {
                fixed: length,
                volume: volume_crypto_header_length,
            });
        }
        if length > READER_MAX_CRYPTO_HEADER_LEN {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "CryptoHeader length",
                cap: READER_MAX_CRYPTO_HEADER_LEN as u64,
                actual: length as u64,
            });
        }

        let header = Self {
            length,
            compression_algo: CompressionAlgo::try_from(read_u16(bytes, 8)?)?,
            aead_algo: AeadAlgo::try_from(read_u16(bytes, 10)?)?,
            fec_algo: FecAlgo::try_from(read_u16(bytes, 12)?)?,
            kdf_algo: KdfAlgo::try_from(read_u16(bytes, 14)?)?,
            chunk_size: read_u32(bytes, 16)?,
            envelope_target_size: read_u32(bytes, 20)?,
            block_size: read_u32(bytes, 24)?,
            fec_data_shards: read_u16(bytes, 28)?,
            fec_parity_shards: read_u16(bytes, 30)?,
            index_fec_data_shards: read_u16(bytes, 32)?,
            index_fec_parity_shards: read_u16(bytes, 34)?,
            index_root_fec_data_shards: read_u16(bytes, 36)?,
            index_root_fec_parity_shards: read_u16(bytes, 38)?,
            stripe_width: read_u32(bytes, 40)?,
            volume_loss_tolerance: bytes[44],
            bit_rot_buffer_pct: bytes[45],
            has_dictionary: bytes[46],
            max_path_length: read_u32(bytes, 48)?,
            expected_volume_size: read_u64(bytes, 52)?,
        };
        header.validate_v36()?;
        Ok(header)
    }

    pub fn validate_v36(&self) -> Result<(), FormatError> {
        if self.compression_algo != CompressionAlgo::ZstdFramed {
            return Err(FormatError::UnsupportedCompressionForV36(
                self.compression_algo,
            ));
        }
        if self.fec_algo != FecAlgo::ReedSolomonGF16 {
            return Err(FormatError::UnsupportedFecForV36(self.fec_algo));
        }
        if self.has_dictionary > 1 {
            return Err(FormatError::InvalidBoolean {
                field: "has_dictionary",
                value: self.has_dictionary,
            });
        }
        if self.stripe_width == 0 {
            return Err(FormatError::ZeroStripeWidth);
        }
        if self.stripe_width > READER_MAX_STRIPE_WIDTH {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "stripe_width",
                cap: READER_MAX_STRIPE_WIDTH as u64,
                actual: self.stripe_width as u64,
            });
        }
        if self.volume_loss_tolerance as u32 >= self.stripe_width {
            return Err(FormatError::VolumeLossToleranceOutOfRange {
                volume_loss_tolerance: self.volume_loss_tolerance,
                stripe_width: self.stripe_width,
            });
        }
        if self.bit_rot_buffer_pct > 100 {
            return Err(FormatError::BitRotBufferPctTooLarge(
                self.bit_rot_buffer_pct,
            ));
        }
        if self.fec_data_shards == 0 {
            return Err(FormatError::ZeroDataShardMaximum {
                field: "fec_data_shards",
            });
        }
        if self.index_fec_data_shards == 0 {
            return Err(FormatError::ZeroDataShardMaximum {
                field: "index_fec_data_shards",
            });
        }
        if self.index_root_fec_data_shards == 0 {
            return Err(FormatError::ZeroDataShardMaximum {
                field: "index_root_fec_data_shards",
            });
        }
        validate_fec_class_shards(
            "fec_data_shards + fec_parity_shards",
            self.fec_data_shards,
            self.fec_parity_shards,
            READER_MAX_FEC_CLASS_SHARDS,
        )?;
        validate_fec_class_shards(
            "index_fec_data_shards + index_fec_parity_shards",
            self.index_fec_data_shards,
            self.index_fec_parity_shards,
            READER_MAX_INDEX_FEC_CLASS_SHARDS,
        )?;
        validate_fec_class_shards(
            "index_root_fec_data_shards + index_root_fec_parity_shards",
            self.index_root_fec_data_shards,
            self.index_root_fec_parity_shards,
            READER_MAX_INDEX_ROOT_FEC_CLASS_SHARDS,
        )?;
        if self.chunk_size == 0 {
            return Err(FormatError::ZeroChunkSize);
        }
        if self.envelope_target_size == 0 {
            return Err(FormatError::ZeroEnvelopeTargetSize);
        }
        if self.chunk_size > self.envelope_target_size {
            return Err(FormatError::ChunkSizeExceedsEnvelopeTarget {
                chunk_size: self.chunk_size,
                envelope_target_size: self.envelope_target_size,
            });
        }
        if self.chunk_size > READER_MAX_CHUNK_SIZE {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "chunk_size",
                cap: READER_MAX_CHUNK_SIZE as u64,
                actual: self.chunk_size as u64,
            });
        }
        if self.envelope_target_size > READER_MAX_ENVELOPE_TARGET_SIZE {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "envelope_target_size",
                cap: READER_MAX_ENVELOPE_TARGET_SIZE as u64,
                actual: self.envelope_target_size as u64,
            });
        }
        if self.block_size < 4096 {
            return Err(FormatError::BlockSizeTooSmall(self.block_size));
        }
        if self.block_size % 2 != 0 {
            return Err(FormatError::OddBlockSize(self.block_size));
        }
        if self.block_size > READER_MAX_BLOCK_SIZE {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "block_size",
                cap: READER_MAX_BLOCK_SIZE as u64,
                actual: self.block_size as u64,
            });
        }
        if self.max_path_length > READER_MAX_PATH_LENGTH {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "max_path_length",
                cap: READER_MAX_PATH_LENGTH as u64,
                actual: self.max_path_length as u64,
            });
        }
        Ok(())
    }

    pub fn to_bytes(&self) -> [u8; CRYPTO_HEADER_FIXED_LEN] {
        let mut bytes = [0u8; CRYPTO_HEADER_FIXED_LEN];
        bytes[0..4].copy_from_slice(&TZCH_MAGIC);
        write_u32(&mut bytes, 4, self.length);
        write_u16(&mut bytes, 8, self.compression_algo as u16);
        write_u16(&mut bytes, 10, self.aead_algo as u16);
        write_u16(&mut bytes, 12, self.fec_algo as u16);
        write_u16(&mut bytes, 14, self.kdf_algo as u16);
        write_u32(&mut bytes, 16, self.chunk_size);
        write_u32(&mut bytes, 20, self.envelope_target_size);
        write_u32(&mut bytes, 24, self.block_size);
        write_u16(&mut bytes, 28, self.fec_data_shards);
        write_u16(&mut bytes, 30, self.fec_parity_shards);
        write_u16(&mut bytes, 32, self.index_fec_data_shards);
        write_u16(&mut bytes, 34, self.index_fec_parity_shards);
        write_u16(&mut bytes, 36, self.index_root_fec_data_shards);
        write_u16(&mut bytes, 38, self.index_root_fec_parity_shards);
        write_u32(&mut bytes, 40, self.stripe_width);
        bytes[44] = self.volume_loss_tolerance;
        bytes[45] = self.bit_rot_buffer_pct;
        bytes[46] = self.has_dictionary;
        write_u32(&mut bytes, 48, self.max_path_length);
        write_u64(&mut bytes, 52, self.expected_volume_size);
        bytes
    }
}

fn validate_fec_class_shards(
    field: &'static str,
    data_shards: u16,
    parity_shards: u16,
    cap: u32,
) -> Result<(), FormatError> {
    let total = data_shards as u32 + parity_shards as u32;
    if total > cap {
        return Err(FormatError::ReaderResourceLimitExceeded {
            field,
            cap: cap as u64,
            actual: total as u64,
        });
    }
    Ok(())
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtensionTlv<'a> {
    pub tag: u16,
    pub value: &'a [u8],
}

pub fn scan_crypto_extension_tlvs(bytes: &[u8]) -> Result<Vec<ExtensionTlv<'_>>, FormatError> {
    let mut offset = 0usize;
    let mut extensions = Vec::new();
    loop {
        if offset == bytes.len() {
            return Err(FormatError::MissingExtensionTerminator);
        }
        if bytes.len() - offset < CRYPTO_EXTENSION_HEADER_LEN {
            return Err(FormatError::TruncatedExtensionHeader);
        }
        let tag = read_u16(bytes, offset)?;
        let length = read_u32(bytes, offset + 2)?;
        offset += CRYPTO_EXTENSION_HEADER_LEN;
        if tag == 0 {
            if length != 0 {
                return Err(FormatError::MalformedExtensionTerminator);
            }
            if offset != bytes.len() {
                return Err(FormatError::BytesAfterExtensionTerminator);
            }
            return Ok(extensions);
        }
        if length > CRYPTO_EXTENSION_MAX_VALUE_LEN {
            return Err(FormatError::ExtensionPayloadTooLarge(length));
        }
        let length = length as usize;
        if bytes.len() - offset < length {
            return Err(FormatError::TruncatedExtensionPayload);
        }
        extensions.push(ExtensionTlv {
            tag,
            value: &bytes[offset..offset + length],
        });
        offset += length;
    }
}

pub fn validate_crypto_extension_semantics(
    extensions: &[ExtensionTlv<'_>],
) -> Result<(), FormatError> {
    let mut seen_known = Vec::new();
    for extension in extensions {
        let ext_tag = extension.tag & 0x7fff;
        let is_critical = extension.tag & 0x8000 != 0;
        if matches!(ext_tag, 0x0004 | 0x0006) {
            return Err(FormatError::ForbiddenExtensionTag(ext_tag));
        }
        if is_known_extension(ext_tag) {
            if seen_known.contains(&ext_tag) {
                return Err(FormatError::DuplicateKnownExtension(ext_tag));
            }
            validate_known_extension(ext_tag, extension.value)?;
            seen_known.push(ext_tag);
        } else if is_critical {
            return Err(FormatError::UnknownCriticalExtension(ext_tag));
        }
    }
    Ok(())
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CryptoHeader<'a> {
    pub fixed: CryptoHeaderFixed,
    pub kdf_params: KdfParams,
    pub extensions: Vec<ExtensionTlv<'a>>,
    pub header_hmac: [u8; 32],
    pub hmac_covered_bytes: &'a [u8],
}

impl<'a> CryptoHeader<'a> {
    pub fn parse(bytes: &'a [u8], volume_crypto_header_length: u32) -> Result<Self, FormatError> {
        let declared_len = volume_crypto_header_length as usize;
        if volume_crypto_header_length > READER_MAX_CRYPTO_HEADER_LEN {
            return Err(FormatError::ReaderResourceLimitExceeded {
                field: "CryptoHeader length",
                cap: READER_MAX_CRYPTO_HEADER_LEN as u64,
                actual: volume_crypto_header_length as u64,
            });
        }
        if bytes.len() != declared_len {
            return Err(FormatError::InvalidLength {
                structure: "CryptoHeader",
                expected: declared_len,
                actual: bytes.len(),
            });
        }
        let min_len =
            CRYPTO_HEADER_FIXED_LEN + 2 + CRYPTO_EXTENSION_HEADER_LEN + CRYPTO_HEADER_HMAC_LEN;
        if bytes.len() < min_len {
            return Err(FormatError::CryptoHeaderTooShort {
                min: min_len,
                actual: bytes.len(),
            });
        }

        let fixed = CryptoHeaderFixed::parse(
            &bytes[..CRYPTO_HEADER_FIXED_LEN],
            volume_crypto_header_length,
        )?;
        let hmac_offset = bytes.len() - CRYPTO_HEADER_HMAC_LEN;
        let (kdf_params, kdf_len) =
            KdfParams::parse(fixed.kdf_algo, &bytes[CRYPTO_HEADER_FIXED_LEN..hmac_offset])?;
        let extension_bytes = &bytes[CRYPTO_HEADER_FIXED_LEN + kdf_len..hmac_offset];
        let extensions = scan_crypto_extension_tlvs(extension_bytes)?;
        let header_hmac = read_array_32(bytes, hmac_offset)?;

        Ok(Self {
            fixed,
            kdf_params,
            extensions,
            header_hmac,
            hmac_covered_bytes: &bytes[..hmac_offset],
        })
    }

    pub fn validate_extension_semantics(&self) -> Result<(), FormatError> {
        validate_crypto_extension_semantics(&self.extensions)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockRecord {
    pub block_index: u64,
    pub kind: BlockKind,
    pub flags: u8,
    pub payload: Vec<u8>,
    pub record_crc32c: u32,
}

impl BlockRecord {
    pub fn parse(bytes: &[u8], block_size: usize) -> Result<Self, FormatError> {
        let expected = block_size + BLOCK_RECORD_FRAMING_LEN;
        expect_len("BlockRecord", expected, bytes.len())?;
        expect_magic("BlockRecord", TZBK_MAGIC, &bytes[0..4])?;
        expect_zero("BlockRecord", &bytes[14..16])?;
        expect_crc(
            "BlockRecord",
            &bytes[..16 + block_size],
            read_u32(bytes, 16 + block_size)?,
        )?;

        let kind = BlockKind::try_from(bytes[12])?;
        let flags = bytes[13];
        if flags & BLOCK_RESERVED_FLAGS != 0 {
            return Err(FormatError::InvalidBlockFlags(flags));
        }
        if kind.is_parity() && flags & BLOCK_LAST_DATA_FLAG != 0 {
            return Err(FormatError::ParityBlockHasLastDataFlag);
        }

        Ok(Self {
            block_index: read_u64(bytes, 4)?,
            kind,
            flags,
            payload: bytes[16..16 + block_size].to_vec(),
            record_crc32c: read_u32(bytes, 16 + block_size)?,
        })
    }

    pub fn is_last_data(&self) -> bool {
        self.flags & BLOCK_LAST_DATA_FLAG != 0
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = vec![0u8; self.payload.len() + BLOCK_RECORD_FRAMING_LEN];
        bytes[0..4].copy_from_slice(&TZBK_MAGIC);
        write_u64(&mut bytes, 4, self.block_index);
        bytes[12] = self.kind as u8;
        bytes[13] = self.flags;
        bytes[16..16 + self.payload.len()].copy_from_slice(&self.payload);
        let crc = crc32c(&bytes[..16 + self.payload.len()]);
        let crc_offset = 16 + self.payload.len();
        write_u32(&mut bytes, crc_offset, crc);
        bytes
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ManifestFooter {
    pub archive_uuid: [u8; 16],
    pub session_id: [u8; 16],
    pub volume_index: u32,
    pub is_authoritative: u8,
    pub total_volumes: u32,
    pub index_root_first_block: u64,
    pub index_root_data_block_count: u32,
    pub index_root_parity_block_count: u32,
    pub index_root_encrypted_size: u32,
    pub index_root_decompressed_size: u32,
    pub manifest_hmac: [u8; 32],
}

impl ManifestFooter {
    pub fn parse(bytes: &[u8]) -> Result<Self, FormatError> {
        expect_len("ManifestFooter", MANIFEST_FOOTER_LEN, bytes.len())?;
        expect_magic("ManifestFooter", TZMF_MAGIC, &bytes[0..4])?;
        expect_zero("ManifestFooter", &bytes[41..44])?;
        expect_zero("ManifestFooter", &bytes[72..104])?;
        let is_authoritative = bytes[40];
        if is_authoritative > 1 {
            return Err(FormatError::InvalidAuthoritativeFlag(is_authoritative));
        }

        Ok(Self {
            archive_uuid: read_array_16(bytes, 4)?,
            session_id: read_array_16(bytes, 20)?,
            volume_index: read_u32(bytes, 36)?,
            is_authoritative,
            total_volumes: read_u32(bytes, 44)?,
            index_root_first_block: read_u64(bytes, 48)?,
            index_root_data_block_count: read_u32(bytes, 56)?,
            index_root_parity_block_count: read_u32(bytes, 60)?,
            index_root_encrypted_size: read_u32(bytes, 64)?,
            index_root_decompressed_size: read_u32(bytes, 68)?,
            manifest_hmac: read_array_32(bytes, 104)?,
        })
    }

    pub fn validate_index_root_extent(&self, block_size: u32) -> Result<(), FormatError> {
        if self.index_root_data_block_count == 0 || self.index_root_encrypted_size == 0 {
            return Err(FormatError::EmptyIndexRootExtent);
        }
        let expected = self
            .index_root_data_block_count
            .checked_mul(block_size)
            .ok_or(FormatError::IndexRootSizeMismatch)?;
        if expected != self.index_root_encrypted_size {
            return Err(FormatError::IndexRootSizeMismatch);
        }
        Ok(())
    }

    pub fn to_bytes(&self) -> [u8; MANIFEST_FOOTER_LEN] {
        let mut bytes = [0u8; MANIFEST_FOOTER_LEN];
        bytes[0..4].copy_from_slice(&TZMF_MAGIC);
        bytes[4..20].copy_from_slice(&self.archive_uuid);
        bytes[20..36].copy_from_slice(&self.session_id);
        write_u32(&mut bytes, 36, self.volume_index);
        bytes[40] = self.is_authoritative;
        write_u32(&mut bytes, 44, self.total_volumes);
        write_u64(&mut bytes, 48, self.index_root_first_block);
        write_u32(&mut bytes, 56, self.index_root_data_block_count);
        write_u32(&mut bytes, 60, self.index_root_parity_block_count);
        write_u32(&mut bytes, 64, self.index_root_encrypted_size);
        write_u32(&mut bytes, 68, self.index_root_decompressed_size);
        bytes[104..136].copy_from_slice(&self.manifest_hmac);
        bytes
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VolumeTrailer {
    pub archive_uuid: [u8; 16],
    pub session_id: [u8; 16],
    pub volume_index: u32,
    pub block_count: u64,
    pub bytes_written: u64,
    pub manifest_footer_offset: u64,
    pub manifest_footer_length: u32,
    pub closed_at_ns: i64,
    pub trailer_hmac: [u8; 32],
}

impl VolumeTrailer {
    pub fn parse(bytes: &[u8]) -> Result<Self, FormatError> {
        expect_len("VolumeTrailer", VOLUME_TRAILER_LEN, bytes.len())?;
        expect_magic("VolumeTrailer", TZVT_MAGIC, &bytes[0..4])?;
        expect_zero("VolumeTrailer", &bytes[76..96])?;
        let manifest_footer_length = read_u32(bytes, 64)?;
        if manifest_footer_length != MANIFEST_FOOTER_LEN as u32 {
            return Err(FormatError::InvalidManifestFooterLength(
                manifest_footer_length,
            ));
        }

        Ok(Self {
            archive_uuid: read_array_16(bytes, 4)?,
            session_id: read_array_16(bytes, 20)?,
            volume_index: read_u32(bytes, 36)?,
            block_count: read_u64(bytes, 40)?,
            bytes_written: read_u64(bytes, 48)?,
            manifest_footer_offset: read_u64(bytes, 56)?,
            manifest_footer_length,
            closed_at_ns: read_i64(bytes, 68)?,
            trailer_hmac: read_array_32(bytes, 96)?,
        })
    }

    pub fn to_bytes(&self) -> [u8; VOLUME_TRAILER_LEN] {
        let mut bytes = [0u8; VOLUME_TRAILER_LEN];
        bytes[0..4].copy_from_slice(&TZVT_MAGIC);
        bytes[4..20].copy_from_slice(&self.archive_uuid);
        bytes[20..36].copy_from_slice(&self.session_id);
        write_u32(&mut bytes, 36, self.volume_index);
        write_u64(&mut bytes, 40, self.block_count);
        write_u64(&mut bytes, 48, self.bytes_written);
        write_u64(&mut bytes, 56, self.manifest_footer_offset);
        write_u32(&mut bytes, 64, self.manifest_footer_length);
        write_i64(&mut bytes, 68, self.closed_at_ns);
        bytes[96..128].copy_from_slice(&self.trailer_hmac);
        bytes
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BootstrapSidecarHeader {
    pub archive_uuid: [u8; 16],
    pub session_id: [u8; 16],
    pub flags: u32,
    pub manifest_footer_offset: u64,
    pub manifest_footer_length: u32,
    pub index_root_records_offset: u64,
    pub index_root_records_length: u64,
    pub dictionary_records_offset: u64,
    pub dictionary_records_length: u64,
    pub sidecar_hmac: [u8; 32],
    pub header_crc32c: u32,
}

impl BootstrapSidecarHeader {
    pub fn parse(bytes: &[u8]) -> Result<Self, FormatError> {
        expect_len(
            "BootstrapSidecarHeader",
            BOOTSTRAP_SIDECAR_HEADER_LEN,
            bytes.len(),
        )?;
        expect_magic("BootstrapSidecarHeader", TZBS_MAGIC, &bytes[0..4])?;
        let version = read_u32(bytes, 4)?;
        if version != 1 {
            return Err(FormatError::UnsupportedBootstrapSidecarVersion(version));
        }
        expect_zero("BootstrapSidecarHeader", &bytes[88..92])?;
        expect_crc(
            "BootstrapSidecarHeader",
            &bytes[..124],
            read_u32(bytes, 124)?,
        )?;

        let header = Self {
            archive_uuid: read_array_16(bytes, 8)?,
            session_id: read_array_16(bytes, 24)?,
            flags: read_u32(bytes, 40)?,
            manifest_footer_offset: read_u64(bytes, 44)?,
            manifest_footer_length: read_u32(bytes, 52)?,
            index_root_records_offset: read_u64(bytes, 56)?,
            index_root_records_length: read_u64(bytes, 64)?,
            dictionary_records_offset: read_u64(bytes, 72)?,
            dictionary_records_length: read_u64(bytes, 80)?,
            sidecar_hmac: read_array_32(bytes, 92)?,
            header_crc32c: read_u32(bytes, 124)?,
        };
        header.validate_sections()?;
        Ok(header)
    }

    pub fn validate_packed_layout(&self, file_size: u64) -> Result<(), FormatError> {
        let mut cursor = BOOTSTRAP_SIDECAR_HEADER_LEN as u64;
        cursor = self.validate_section_cursor(
            self.has_manifest_footer(),
            self.manifest_footer_offset,
            self.manifest_footer_length as u64,
            cursor,
        )?;
        cursor = self.validate_section_cursor(
            self.has_index_root_records(),
            self.index_root_records_offset,
            self.index_root_records_length,
            cursor,
        )?;
        cursor = self.validate_section_cursor(
            self.has_dictionary_records(),
            self.dictionary_records_offset,
            self.dictionary_records_length,
            cursor,
        )?;
        if cursor != file_size {
            return Err(FormatError::NonCanonicalBootstrapSidecarLayout);
        }
        Ok(())
    }

    pub fn has_manifest_footer(&self) -> bool {
        self.flags & SIDECAR_MANIFEST_PRESENT != 0
    }

    pub fn has_index_root_records(&self) -> bool {
        self.flags & SIDECAR_INDEX_ROOT_PRESENT != 0
    }

    pub fn has_dictionary_records(&self) -> bool {
        self.flags & SIDECAR_DICTIONARY_PRESENT != 0
    }

    pub fn to_bytes(&self) -> [u8; BOOTSTRAP_SIDECAR_HEADER_LEN] {
        let mut bytes = [0u8; BOOTSTRAP_SIDECAR_HEADER_LEN];
        bytes[0..4].copy_from_slice(&TZBS_MAGIC);
        write_u32(&mut bytes, 4, 1);
        bytes[8..24].copy_from_slice(&self.archive_uuid);
        bytes[24..40].copy_from_slice(&self.session_id);
        write_u32(&mut bytes, 40, self.flags);
        write_u64(&mut bytes, 44, self.manifest_footer_offset);
        write_u32(&mut bytes, 52, self.manifest_footer_length);
        write_u64(&mut bytes, 56, self.index_root_records_offset);
        write_u64(&mut bytes, 64, self.index_root_records_length);
        write_u64(&mut bytes, 72, self.dictionary_records_offset);
        write_u64(&mut bytes, 80, self.dictionary_records_length);
        bytes[92..124].copy_from_slice(&self.sidecar_hmac);
        let crc = crc32c(&bytes[..124]);
        write_u32(&mut bytes, 124, crc);
        bytes
    }

    fn validate_sections(&self) -> Result<(), FormatError> {
        if self.flags & !SIDECAR_KNOWN_FLAGS != 0 {
            return Err(FormatError::UnknownBootstrapSidecarFlags(self.flags));
        }
        self.validate_presence_fields(
            self.has_manifest_footer(),
            self.manifest_footer_offset,
            self.manifest_footer_length as u64,
        )?;
        if self.has_manifest_footer() && self.manifest_footer_length != MANIFEST_FOOTER_LEN as u32 {
            return Err(FormatError::InvalidManifestFooterLength(
                self.manifest_footer_length,
            ));
        }
        self.validate_presence_fields(
            self.has_index_root_records(),
            self.index_root_records_offset,
            self.index_root_records_length,
        )?;
        self.validate_presence_fields(
            self.has_dictionary_records(),
            self.dictionary_records_offset,
            self.dictionary_records_length,
        )?;
        Ok(())
    }

    fn validate_presence_fields(
        &self,
        present: bool,
        offset: u64,
        length: u64,
    ) -> Result<(), FormatError> {
        match (present, offset, length) {
            (true, 0, _) | (true, _, 0) => Err(FormatError::EmptyBootstrapSidecarSection),
            (false, 0, 0) => Ok(()),
            (false, _, _) => Err(FormatError::NonZeroAbsentBootstrapSidecarSection),
            (true, _, _) => Ok(()),
        }
    }

    fn validate_section_cursor(
        &self,
        present: bool,
        offset: u64,
        length: u64,
        cursor: u64,
    ) -> Result<u64, FormatError> {
        if present {
            if offset != cursor {
                return Err(FormatError::NonCanonicalBootstrapSidecarLayout);
            }
            cursor
                .checked_add(length)
                .ok_or(FormatError::NonCanonicalBootstrapSidecarLayout)
        } else {
            Ok(cursor)
        }
    }
}

fn is_known_extension(ext_tag: u16) -> bool {
    matches!(ext_tag, 0x0001 | 0x0002 | 0x0003 | 0x0005)
}

fn validate_known_extension(ext_tag: u16, value: &[u8]) -> Result<(), FormatError> {
    match ext_tag {
        0x0001 | 0x0002 | 0x0005 => std::str::from_utf8(value)
            .map(|_| ())
            .map_err(|_| FormatError::MalformedKnownExtension(ext_tag)),
        0x0003 => {
            if value.len() == 8 {
                Ok(())
            } else {
                Err(FormatError::MalformedKnownExtension(ext_tag))
            }
        }
        _ => Ok(()),
    }
}

fn expect_len(structure: &'static str, expected: usize, actual: usize) -> Result<(), FormatError> {
    if actual != expected {
        return Err(FormatError::InvalidLength {
            structure,
            expected,
            actual,
        });
    }
    Ok(())
}

fn expect_magic(
    structure: &'static str,
    expected: [u8; 4],
    actual: &[u8],
) -> Result<(), FormatError> {
    if actual != expected {
        return Err(FormatError::BadMagic { structure });
    }
    Ok(())
}

fn expect_zero(structure: &'static str, bytes: &[u8]) -> Result<(), FormatError> {
    if bytes.iter().any(|byte| *byte != 0) {
        return Err(FormatError::NonZeroReserved { structure });
    }
    Ok(())
}

fn expect_crc(structure: &'static str, covered: &[u8], expected: u32) -> Result<(), FormatError> {
    if crc32c(covered) != expected {
        return Err(FormatError::BadCrc { structure });
    }
    Ok(())
}

fn read_array_16(bytes: &[u8], offset: usize) -> Result<[u8; 16], FormatError> {
    let mut value = [0u8; 16];
    value.copy_from_slice(
        bytes
            .get(offset..offset + 16)
            .ok_or(FormatError::InvalidLength {
                structure: "array16",
                expected: offset + 16,
                actual: bytes.len(),
            })?,
    );
    Ok(value)
}

fn read_array_32(bytes: &[u8], offset: usize) -> Result<[u8; 32], FormatError> {
    let mut value = [0u8; 32];
    value.copy_from_slice(
        bytes
            .get(offset..offset + 32)
            .ok_or(FormatError::InvalidLength {
                structure: "array32",
                expected: offset + 32,
                actual: bytes.len(),
            })?,
    );
    Ok(value)
}

fn read_u16(bytes: &[u8], offset: usize) -> Result<u16, FormatError> {
    let array: [u8; 2] = bytes
        .get(offset..offset + 2)
        .ok_or(FormatError::InvalidLength {
            structure: "u16",
            expected: offset + 2,
            actual: bytes.len(),
        })?
        .try_into()
        .expect("slice length checked");
    Ok(u16::from_le_bytes(array))
}

fn read_u32(bytes: &[u8], offset: usize) -> Result<u32, FormatError> {
    let array: [u8; 4] = bytes
        .get(offset..offset + 4)
        .ok_or(FormatError::InvalidLength {
            structure: "u32",
            expected: offset + 4,
            actual: bytes.len(),
        })?
        .try_into()
        .expect("slice length checked");
    Ok(u32::from_le_bytes(array))
}

fn read_u64(bytes: &[u8], offset: usize) -> Result<u64, FormatError> {
    let array: [u8; 8] = bytes
        .get(offset..offset + 8)
        .ok_or(FormatError::InvalidLength {
            structure: "u64",
            expected: offset + 8,
            actual: bytes.len(),
        })?
        .try_into()
        .expect("slice length checked");
    Ok(u64::from_le_bytes(array))
}

fn read_i64(bytes: &[u8], offset: usize) -> Result<i64, FormatError> {
    let array: [u8; 8] = bytes
        .get(offset..offset + 8)
        .ok_or(FormatError::InvalidLength {
            structure: "i64",
            expected: offset + 8,
            actual: bytes.len(),
        })?
        .try_into()
        .expect("slice length checked");
    Ok(i64::from_le_bytes(array))
}

fn write_u16(bytes: &mut [u8], offset: usize, value: u16) {
    bytes[offset..offset + 2].copy_from_slice(&value.to_le_bytes());
}

fn write_u32(bytes: &mut [u8], offset: usize, value: u32) {
    bytes[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
}

fn write_u64(bytes: &mut [u8], offset: usize, value: u64) {
    bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
}

fn write_i64(bytes: &mut [u8], offset: usize, value: i64) {
    bytes[offset..offset + 8].copy_from_slice(&value.to_le_bytes());
}

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

    fn uuid() -> [u8; 16] {
        [0x11; 16]
    }

    fn session() -> [u8; 16] {
        [0x22; 16]
    }

    fn volume_header() -> VolumeHeader {
        VolumeHeader {
            format_version: FORMAT_VERSION,
            volume_format_rev: VOLUME_FORMAT_REV,
            volume_index: 0,
            stripe_width: 1,
            archive_uuid: uuid(),
            session_id: session(),
            crypto_header_offset: VOLUME_HEADER_LEN as u32,
            crypto_header_length: 128,
            header_crc32c: 0,
        }
    }

    fn crypto_fixed() -> CryptoHeaderFixed {
        CryptoHeaderFixed {
            length: (CRYPTO_HEADER_FIXED_LEN + 2 + 6 + CRYPTO_HEADER_HMAC_LEN) as u32,
            compression_algo: CompressionAlgo::ZstdFramed,
            aead_algo: AeadAlgo::AesGcmSiv256,
            fec_algo: FecAlgo::ReedSolomonGF16,
            kdf_algo: KdfAlgo::Raw,
            chunk_size: 262_144,
            envelope_target_size: 4 * 1024 * 1024,
            block_size: 4096,
            fec_data_shards: 16,
            fec_parity_shards: 2,
            index_fec_data_shards: 16,
            index_fec_parity_shards: 2,
            index_root_fec_data_shards: 16,
            index_root_fec_parity_shards: 2,
            stripe_width: 1,
            volume_loss_tolerance: 0,
            bit_rot_buffer_pct: 5,
            has_dictionary: 0,
            max_path_length: 4096,
            expected_volume_size: 0,
        }
    }

    fn raw_crypto_header_bytes() -> Vec<u8> {
        let fixed = crypto_fixed();
        let mut bytes = Vec::new();
        bytes.extend_from_slice(&fixed.to_bytes());
        bytes.extend_from_slice(&(KdfAlgo::Raw as u16).to_le_bytes());
        bytes.extend_from_slice(&0u16.to_le_bytes());
        bytes.extend_from_slice(&0u32.to_le_bytes());
        bytes.extend_from_slice(&[0xab; CRYPTO_HEADER_HMAC_LEN]);
        bytes
    }

    #[test]
    fn volume_header_round_trips_and_validates() {
        let bytes = volume_header().to_bytes();
        let parsed = VolumeHeader::parse(&bytes).unwrap();
        assert_eq!(parsed.format_version, FORMAT_VERSION);
        assert_eq!(parsed.volume_format_rev, VOLUME_FORMAT_REV);
        assert_eq!(parsed.crypto_header_offset, VOLUME_HEADER_LEN as u32);
    }

    #[test]
    fn volume_header_rejects_mutations() {
        let mut bytes = volume_header().to_bytes();
        bytes[0] = b'X';
        assert_eq!(
            VolumeHeader::parse(&bytes).unwrap_err(),
            FormatError::BadMagic {
                structure: "VolumeHeader"
            }
        );

        let mut bytes = volume_header().to_bytes();
        write_u16(&mut bytes, 6, 35);
        let crc = crc32c(&bytes[..124]);
        write_u32(&mut bytes, 124, crc);
        assert_eq!(
            VolumeHeader::parse(&bytes).unwrap_err(),
            FormatError::UnsupportedVolumeFormatRevision(35)
        );

        let mut bytes = volume_header().to_bytes();
        bytes[124] ^= 1;
        assert_eq!(
            VolumeHeader::parse(&bytes).unwrap_err(),
            FormatError::BadCrc {
                structure: "VolumeHeader"
            }
        );

        let mut bytes = volume_header().to_bytes();
        write_u32(&mut bytes, 48, 129);
        let crc = crc32c(&bytes[..124]);
        write_u32(&mut bytes, 124, crc);
        assert_eq!(
            VolumeHeader::parse(&bytes).unwrap_err(),
            FormatError::NonCanonicalCryptoHeaderOffset(129)
        );

        let mut bytes = volume_header().to_bytes();
        write_u32(&mut bytes, 52, READER_MAX_CRYPTO_HEADER_LEN + 1);
        let crc = crc32c(&bytes[..124]);
        write_u32(&mut bytes, 124, crc);
        assert_eq!(
            VolumeHeader::parse(&bytes).unwrap_err(),
            FormatError::ReaderResourceLimitExceeded {
                field: "CryptoHeader length",
                cap: READER_MAX_CRYPTO_HEADER_LEN as u64,
                actual: (READER_MAX_CRYPTO_HEADER_LEN + 1) as u64,
            }
        );
    }

    #[test]
    fn crypto_header_fixed_round_trips_and_validates() {
        let header = crypto_fixed();
        let bytes = header.to_bytes();
        let parsed = CryptoHeaderFixed::parse(&bytes, header.length).unwrap();
        assert_eq!(parsed.compression_algo, CompressionAlgo::ZstdFramed);
        assert_eq!(parsed.fec_algo, FecAlgo::ReedSolomonGF16);
    }

    #[test]
    fn crypto_header_fixed_rejects_v36_incompatible_values() {
        let mut header = crypto_fixed();
        header.compression_algo = CompressionAlgo::None;
        assert_eq!(
            CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap_err(),
            FormatError::UnsupportedCompressionForV36(CompressionAlgo::None)
        );

        let mut header = crypto_fixed();
        header.block_size = 4097;
        assert_eq!(
            CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap_err(),
            FormatError::OddBlockSize(4097)
        );

        let mut bytes = crypto_fixed().to_bytes();
        bytes[47] = 1;
        assert_eq!(
            CryptoHeaderFixed::parse(&bytes, crypto_fixed().length).unwrap_err(),
            FormatError::NonZeroReserved {
                structure: "CryptoHeaderFixed"
            }
        );
    }

    #[test]
    fn crypto_header_fixed_treats_expected_volume_size_as_advisory_not_reserved() {
        let mut header = crypto_fixed();
        header.expected_volume_size = 1u64 << 56;

        let parsed = CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap();

        assert_eq!(parsed.expected_volume_size, 1u64 << 56);
    }

    #[test]
    fn crypto_header_fixed_rejects_reader_cap_excesses() {
        let mut header = crypto_fixed();
        header.chunk_size = READER_MAX_CHUNK_SIZE + 1;
        header.envelope_target_size = header.chunk_size;
        assert_eq!(
            CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap_err(),
            FormatError::ReaderResourceLimitExceeded {
                field: "chunk_size",
                cap: READER_MAX_CHUNK_SIZE as u64,
                actual: (READER_MAX_CHUNK_SIZE + 1) as u64,
            }
        );

        let mut header = crypto_fixed();
        header.block_size = READER_MAX_BLOCK_SIZE + 2;
        assert_eq!(
            CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap_err(),
            FormatError::ReaderResourceLimitExceeded {
                field: "block_size",
                cap: READER_MAX_BLOCK_SIZE as u64,
                actual: (READER_MAX_BLOCK_SIZE + 2) as u64,
            }
        );

        let mut header = crypto_fixed();
        header.max_path_length = READER_MAX_PATH_LENGTH + 1;
        assert_eq!(
            CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap_err(),
            FormatError::ReaderResourceLimitExceeded {
                field: "max_path_length",
                cap: READER_MAX_PATH_LENGTH as u64,
                actual: (READER_MAX_PATH_LENGTH + 1) as u64,
            }
        );

        let mut header = crypto_fixed();
        header.fec_data_shards = READER_MAX_FEC_CLASS_SHARDS as u16;
        header.fec_parity_shards = 1;
        assert_eq!(
            CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap_err(),
            FormatError::ReaderResourceLimitExceeded {
                field: "fec_data_shards + fec_parity_shards",
                cap: READER_MAX_FEC_CLASS_SHARDS as u64,
                actual: (READER_MAX_FEC_CLASS_SHARDS + 1) as u64,
            }
        );

        let mut header = crypto_fixed();
        header.index_fec_data_shards = READER_MAX_INDEX_FEC_CLASS_SHARDS as u16;
        header.index_fec_parity_shards = 1;
        assert_eq!(
            CryptoHeaderFixed::parse(&header.to_bytes(), header.length).unwrap_err(),
            FormatError::ReaderResourceLimitExceeded {
                field: "index_fec_data_shards + index_fec_parity_shards",
                cap: READER_MAX_INDEX_FEC_CLASS_SHARDS as u64,
                actual: (READER_MAX_INDEX_FEC_CLASS_SHARDS + 1) as u64,
            }
        );
    }

    #[test]
    fn crypto_extension_scanner_enforces_terminator_and_caps() {
        let bytes = [0u8; CRYPTO_EXTENSION_HEADER_LEN];
        assert!(scan_crypto_extension_tlvs(&bytes).unwrap().is_empty());

        let mut bytes = Vec::new();
        bytes.extend_from_slice(&0x0001u16.to_le_bytes());
        bytes.extend_from_slice(&3u32.to_le_bytes());
        bytes.extend_from_slice(b"hey");
        bytes.extend_from_slice(&0u16.to_le_bytes());
        bytes.extend_from_slice(&0u32.to_le_bytes());
        let tlvs = scan_crypto_extension_tlvs(&bytes).unwrap();
        assert_eq!(tlvs[0].tag, 1);
        assert_eq!(tlvs[0].value, b"hey");

        let mut bytes = Vec::new();
        bytes.extend_from_slice(&0x0001u16.to_le_bytes());
        bytes.extend_from_slice(&257u32.to_le_bytes());
        assert_eq!(
            scan_crypto_extension_tlvs(&bytes).unwrap_err(),
            FormatError::ExtensionPayloadTooLarge(257)
        );

        assert_eq!(
            scan_crypto_extension_tlvs(&[]).unwrap_err(),
            FormatError::MissingExtensionTerminator
        );
    }

    #[test]
    fn crypto_header_parse_splits_fixed_kdf_extensions_and_hmac() {
        let bytes = raw_crypto_header_bytes();
        let header = CryptoHeader::parse(&bytes, bytes.len() as u32).unwrap();
        assert_eq!(header.fixed.kdf_algo, KdfAlgo::Raw);
        assert_eq!(header.kdf_params, KdfParams::Raw);
        assert!(header.extensions.is_empty());
        assert_eq!(header.header_hmac, [0xab; CRYPTO_HEADER_HMAC_LEN]);
        assert_eq!(
            header.hmac_covered_bytes.len(),
            bytes.len() - CRYPTO_HEADER_HMAC_LEN
        );
    }

    #[test]
    fn crypto_header_parse_rejects_truncated_and_bad_kdf_params() {
        let mut bytes = raw_crypto_header_bytes();
        bytes.truncate(CRYPTO_HEADER_FIXED_LEN + 1);
        assert_eq!(
            CryptoHeader::parse(&bytes, bytes.len() as u32).unwrap_err(),
            FormatError::CryptoHeaderTooShort {
                min: CRYPTO_HEADER_FIXED_LEN
                    + 2
                    + CRYPTO_EXTENSION_HEADER_LEN
                    + CRYPTO_HEADER_HMAC_LEN,
                actual: CRYPTO_HEADER_FIXED_LEN + 1
            }
        );

        let mut bytes = raw_crypto_header_bytes();
        write_u16(
            &mut bytes,
            CRYPTO_HEADER_FIXED_LEN,
            KdfAlgo::Argon2id as u16,
        );
        assert_eq!(
            CryptoHeader::parse(&bytes, bytes.len() as u32).unwrap_err(),
            FormatError::KdfAlgoTagMismatch {
                expected: 0,
                actual: 1
            }
        );
    }

    #[test]
    fn crypto_extension_semantics_reject_forbidden_duplicate_and_critical() {
        let duplicate = vec![
            ExtensionTlv {
                tag: 0x0001,
                value: b"one",
            },
            ExtensionTlv {
                tag: 0x0001,
                value: b"two",
            },
        ];
        assert_eq!(
            validate_crypto_extension_semantics(&duplicate).unwrap_err(),
            FormatError::DuplicateKnownExtension(0x0001)
        );

        let forbidden = vec![ExtensionTlv {
            tag: 0x8004,
            value: b"",
        }];
        assert_eq!(
            validate_crypto_extension_semantics(&forbidden).unwrap_err(),
            FormatError::ForbiddenExtensionTag(0x0004)
        );

        let unknown_critical = vec![ExtensionTlv {
            tag: 0x8123,
            value: b"",
        }];
        assert_eq!(
            validate_crypto_extension_semantics(&unknown_critical).unwrap_err(),
            FormatError::UnknownCriticalExtension(0x0123)
        );

        let malformed_known = vec![ExtensionTlv {
            tag: 0x0003,
            value: b"short",
        }];
        assert_eq!(
            validate_crypto_extension_semantics(&malformed_known).unwrap_err(),
            FormatError::MalformedKnownExtension(0x0003)
        );
    }

    #[test]
    fn block_record_round_trips_and_validates_crc() {
        let record = BlockRecord {
            block_index: 0,
            kind: BlockKind::PayloadData,
            flags: BLOCK_LAST_DATA_FLAG,
            payload: vec![7; 4096],
            record_crc32c: 0,
        };
        let bytes = record.to_bytes();
        let parsed = BlockRecord::parse(&bytes, 4096).unwrap();
        assert_eq!(parsed.kind, BlockKind::PayloadData);
        assert!(parsed.is_last_data());

        let mut corrupted = bytes;
        corrupted[20] ^= 1;
        assert_eq!(
            BlockRecord::parse(&corrupted, 4096).unwrap_err(),
            FormatError::BadCrc {
                structure: "BlockRecord"
            }
        );
    }

    #[test]
    fn block_record_rejects_reserved_kind_flags_and_parity_last_flag() {
        let mut record = BlockRecord {
            block_index: 0,
            kind: BlockKind::PayloadData,
            flags: 0x02,
            payload: vec![0; 4096],
            record_crc32c: 0,
        };
        assert_eq!(
            BlockRecord::parse(&record.to_bytes(), 4096).unwrap_err(),
            FormatError::InvalidBlockFlags(0x02)
        );

        record.kind = BlockKind::PayloadParity;
        record.flags = BLOCK_LAST_DATA_FLAG;
        assert_eq!(
            BlockRecord::parse(&record.to_bytes(), 4096).unwrap_err(),
            FormatError::ParityBlockHasLastDataFlag
        );

        let mut bytes = record.to_bytes();
        bytes[12] = 10;
        let crc = crc32c(&bytes[..4112]);
        write_u32(&mut bytes, 4112, crc);
        assert_eq!(
            BlockRecord::parse(&bytes, 4096).unwrap_err(),
            FormatError::UnknownBlockKind(10)
        );
    }

    #[test]
    fn manifest_footer_round_trips_and_validates_index_extent() {
        let footer = ManifestFooter {
            archive_uuid: uuid(),
            session_id: session(),
            volume_index: 0,
            is_authoritative: 1,
            total_volumes: 1,
            index_root_first_block: 0,
            index_root_data_block_count: 2,
            index_root_parity_block_count: 1,
            index_root_encrypted_size: 8192,
            index_root_decompressed_size: 120,
            manifest_hmac: [0xaa; 32],
        };
        let parsed = ManifestFooter::parse(&footer.to_bytes()).unwrap();
        parsed.validate_index_root_extent(4096).unwrap();

        let mut bad = footer.clone();
        bad.index_root_encrypted_size = 4096;
        assert_eq!(
            ManifestFooter::parse(&bad.to_bytes())
                .unwrap()
                .validate_index_root_extent(4096)
                .unwrap_err(),
            FormatError::IndexRootSizeMismatch
        );
    }

    #[test]
    fn volume_trailer_round_trips_and_requires_manifest_length() {
        let trailer = VolumeTrailer {
            archive_uuid: uuid(),
            session_id: session(),
            volume_index: 0,
            block_count: 3,
            bytes_written: 10_000,
            manifest_footer_offset: 9_864,
            manifest_footer_length: MANIFEST_FOOTER_LEN as u32,
            closed_at_ns: 123,
            trailer_hmac: [0xbb; 32],
        };
        let parsed = VolumeTrailer::parse(&trailer.to_bytes()).unwrap();
        assert_eq!(parsed.block_count, 3);

        let mut bad = trailer;
        bad.manifest_footer_length = 100;
        assert_eq!(
            VolumeTrailer::parse(&bad.to_bytes()).unwrap_err(),
            FormatError::InvalidManifestFooterLength(100)
        );
    }

    #[test]
    fn bootstrap_sidecar_header_validates_presence_and_layout() {
        let header = BootstrapSidecarHeader {
            archive_uuid: uuid(),
            session_id: session(),
            flags: SIDECAR_MANIFEST_PRESENT | SIDECAR_INDEX_ROOT_PRESENT,
            manifest_footer_offset: BOOTSTRAP_SIDECAR_HEADER_LEN as u64,
            manifest_footer_length: MANIFEST_FOOTER_LEN as u32,
            index_root_records_offset: (BOOTSTRAP_SIDECAR_HEADER_LEN + MANIFEST_FOOTER_LEN) as u64,
            index_root_records_length: 40,
            dictionary_records_offset: 0,
            dictionary_records_length: 0,
            sidecar_hmac: [0xcc; 32],
            header_crc32c: 0,
        };
        let parsed = BootstrapSidecarHeader::parse(&header.to_bytes()).unwrap();
        parsed
            .validate_packed_layout(
                BOOTSTRAP_SIDECAR_HEADER_LEN as u64 + MANIFEST_FOOTER_LEN as u64 + 40,
            )
            .unwrap();

        let mut bad = header.clone();
        bad.flags |= 0x08;
        assert_eq!(
            BootstrapSidecarHeader::parse(&bad.to_bytes()).unwrap_err(),
            FormatError::UnknownBootstrapSidecarFlags(0x0b)
        );

        let mut bad = header;
        bad.index_root_records_offset += 1;
        let parsed = BootstrapSidecarHeader::parse(&bad.to_bytes()).unwrap();
        assert_eq!(
            parsed
                .validate_packed_layout(
                    BOOTSTRAP_SIDECAR_HEADER_LEN as u64 + MANIFEST_FOOTER_LEN as u64 + 40
                )
                .unwrap_err(),
            FormatError::NonCanonicalBootstrapSidecarLayout
        );
    }
}
