use std::fs::{self, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};

#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;

use crate::format::{ExtractError, FormatError};
use crate::metadata::validate_file_path_bytes;

const TAR_BLOCK_LEN: usize = 512;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TarEntryKind {
    Regular,
    Directory,
    Symlink,
    Hardlink,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MetadataDiagnostic {
    pub profile: &'static str,
    pub message: &'static str,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OwnedTarMember {
    pub path: Vec<u8>,
    pub kind: TarEntryKind,
    pub data: Vec<u8>,
    pub link_target: Option<Vec<u8>>,
    pub mode: u32,
    pub mtime: u64,
    pub logical_size: u64,
    pub diagnostics: Vec<MetadataDiagnostic>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParsedTarMember<'a> {
    pub path: Vec<u8>,
    pub kind: TarEntryKind,
    pub data: &'a [u8],
    pub link_target: Option<Vec<u8>>,
    pub mode: u32,
    pub mtime: u64,
    pub logical_size: u64,
    pub diagnostics: Vec<MetadataDiagnostic>,
}

impl ParsedTarMember<'_> {
    pub fn to_owned_member(&self) -> OwnedTarMember {
        OwnedTarMember {
            path: self.path.clone(),
            kind: self.kind,
            data: self.data.to_vec(),
            link_target: self.link_target.clone(),
            mode: self.mode,
            mtime: self.mtime,
            logical_size: self.logical_size,
            diagnostics: self.diagnostics.clone(),
        }
    }
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct SafeExtractionOptions {
    pub overwrite_existing: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct StreamedTarMemberMetadata {
    pub path: Vec<u8>,
    pub kind: TarEntryKind,
    pub link_target: Option<Vec<u8>>,
    pub mode: u32,
    pub mtime: u64,
    pub logical_size: u64,
    pub diagnostics: Vec<MetadataDiagnostic>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct TarStreamMemberSummary {
    pub path: Vec<u8>,
    pub kind: TarEntryKind,
    pub link_target: Option<Vec<u8>>,
    pub mode: u32,
    pub mtime: u64,
    pub logical_size: u64,
    pub diagnostics: Vec<MetadataDiagnostic>,
    pub group_start: u64,
    pub group_size: u64,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct TarStreamSummary {
    pub members: Vec<TarStreamMemberSummary>,
    pub tar_total_size: u64,
    pub total_extraction_size: u64,
}

pub(crate) trait TarMemberGroupReader {
    fn read_some_member_bytes(&mut self, buf: &mut [u8]) -> Result<usize, ExtractError>;

    fn read_exact_member_bytes(&mut self, mut buf: &mut [u8]) -> Result<(), ExtractError> {
        while !buf.is_empty() {
            let read = self.read_some_member_bytes(buf)?;
            if read == 0 {
                return Err(
                    FormatError::InvalidArchive("tar member group exceeds frame range").into(),
                );
            }
            let (_, rest) = buf.split_at_mut(read);
            buf = rest;
        }
        Ok(())
    }
}

trait TarMemberStreamHandler {
    fn on_member(&mut self, member: &StreamedTarMemberMetadata) -> Result<(), ExtractError>;
    fn write_regular_payload(&mut self, bytes: &[u8]) -> Result<(), ExtractError>;
}

pub(crate) trait TarStreamObserver {
    fn on_member_start(&mut self, _member: &StreamedTarMemberMetadata) -> Result<(), FormatError> {
        Ok(())
    }

    fn on_regular_payload(&mut self, _bytes: &[u8]) -> Result<(), FormatError> {
        Ok(())
    }

    fn on_member_complete(
        &mut self,
        member: &StreamedTarMemberMetadata,
    ) -> Result<Vec<MetadataDiagnostic>, FormatError> {
        Ok(member.diagnostics.clone())
    }
}

pub(crate) struct NoopTarStreamObserver;

impl TarStreamObserver for NoopTarStreamObserver {}

pub(crate) struct TarStreamFilesystemRestoreObserver<'a> {
    handler: FilesystemRestoreHandler<'a>,
}

impl<'a> TarStreamFilesystemRestoreObserver<'a> {
    pub(crate) fn new(root: &'a Path, options: SafeExtractionOptions) -> Self {
        Self {
            handler: FilesystemRestoreHandler::new(root, options),
        }
    }
}

impl TarStreamObserver for TarStreamFilesystemRestoreObserver<'_> {
    fn on_member_start(&mut self, member: &StreamedTarMemberMetadata) -> Result<(), FormatError> {
        self.handler
            .on_member(member)
            .map_err(format_error_from_extract_error)
    }

    fn on_regular_payload(&mut self, bytes: &[u8]) -> Result<(), FormatError> {
        self.handler
            .write_regular_payload(bytes)
            .map_err(format_error_from_extract_error)
    }

    fn on_member_complete(
        &mut self,
        member: &StreamedTarMemberMetadata,
    ) -> Result<Vec<MetadataDiagnostic>, FormatError> {
        self.handler
            .finish(member)
            .map_err(format_error_from_extract_error)
    }
}

#[derive(Default)]
struct LocalMetadata {
    pax_path: Option<Vec<u8>>,
    pax_linkpath: Option<Vec<u8>>,
    pax_size: Option<u64>,
    gnu_long_name: Option<Vec<u8>>,
    gnu_long_link: Option<Vec<u8>>,
    diagnostics: Vec<MetadataDiagnostic>,
}

pub fn parse_tar_member_group<'a>(
    group: &'a [u8],
    max_path_length: u32,
) -> Result<ParsedTarMember<'a>, FormatError> {
    if group.len() < TAR_BLOCK_LEN || group.len() % TAR_BLOCK_LEN != 0 {
        return Err(FormatError::InvalidArchive(
            "tar member group is not block aligned",
        ));
    }

    let mut cursor = 0usize;
    let mut metadata = LocalMetadata::default();

    loop {
        let header = slice(group, cursor, TAR_BLOCK_LEN)?;
        if header.iter().all(|byte| *byte == 0) {
            return Err(FormatError::InvalidArchive("tar member header is empty"));
        }
        verify_tar_checksum(header)?;
        let typeflag = header[156];
        let header_size = parse_tar_octal(&header[124..136])?;
        let is_main = matches!(typeflag, 0 | b'0' | b'5' | b'2' | b'1');
        let effective_size = if is_main {
            metadata.pax_size.unwrap_or(header_size)
        } else {
            header_size
        };
        let payload_start = checked_add(cursor, TAR_BLOCK_LEN)?;
        let payload_len = to_usize(effective_size)?;
        let payload_end = checked_add(payload_start, payload_len)?;
        let padded_end = checked_add(payload_end, padding_to_512(payload_len))?;
        let payload = slice(group, payload_start, payload_len)?;
        if padded_end > group.len() {
            return Err(FormatError::InvalidArchive(
                "tar member payload exceeds group",
            ));
        }
        if group[payload_end..padded_end].iter().any(|byte| *byte != 0) {
            return Err(FormatError::InvalidArchive(
                "tar member padding is non-zero",
            ));
        }

        match typeflag {
            b'x' => {
                parse_pax_records(payload, &mut metadata)?;
                cursor = padded_end;
            }
            b'g' => {
                return Err(FormatError::InvalidArchive(
                    "global PAX headers are not allowed",
                ));
            }
            b'V' | b'M' | b'N' => {
                return Err(FormatError::InvalidArchive(
                    "global GNU headers are not allowed",
                ));
            }
            b'L' => {
                metadata.gnu_long_name = Some(trimmed_metadata_payload(payload));
                cursor = padded_end;
            }
            b'K' => {
                metadata.gnu_long_link = Some(trimmed_metadata_payload(payload));
                cursor = padded_end;
            }
            b'S' => {
                return Err(FormatError::ReaderUnsupported(
                    "unsupported GNU sparse tar entry",
                ));
            }
            0 | b'0' | b'5' | b'2' | b'1' => {
                if padded_end != group.len() {
                    return Err(FormatError::InvalidArchive(
                        "tar member group has bytes after main entry",
                    ));
                }
                let kind = match typeflag {
                    b'5' => TarEntryKind::Directory,
                    b'2' => TarEntryKind::Symlink,
                    b'1' => TarEntryKind::Hardlink,
                    _ => TarEntryKind::Regular,
                };
                let mode = parse_tar_octal(&header[100..108])? as u32;
                let mtime = parse_tar_octal(&header[136..148])?;
                let path = canonical_main_path(header, kind, &metadata, max_path_length)?;
                let link_target =
                    canonical_link_target(header, kind, &path, &metadata, max_path_length)?;
                if kind != TarEntryKind::Regular && effective_size != 0 {
                    return Err(FormatError::InvalidArchive(
                        "non-regular tar entry has non-zero payload size",
                    ));
                }
                let logical_size = if kind == TarEntryKind::Regular {
                    effective_size
                } else {
                    0
                };
                return Ok(ParsedTarMember {
                    path,
                    kind,
                    data: if kind == TarEntryKind::Regular {
                        payload
                    } else {
                        &[]
                    },
                    mode,
                    mtime,
                    link_target,
                    logical_size,
                    diagnostics: metadata.diagnostics,
                });
            }
            _ => {
                return Err(FormatError::ReaderUnsupported("unsupported tar entry type"));
            }
        }

        if cursor >= group.len() {
            return Err(FormatError::InvalidArchive(
                "tar member group has metadata records but no main entry",
            ));
        }
    }
}

pub fn validate_tar_stream_total_extraction_size(
    stream: &[u8],
    max_path_length: u32,
    cap: u64,
) -> Result<(), FormatError> {
    if stream.len() % TAR_BLOCK_LEN != 0 {
        return Err(FormatError::InvalidArchive(
            "tar stream is not block aligned",
        ));
    }

    let mut cursor = 0usize;
    let mut total = 0u64;
    while cursor < stream.len() {
        let group_end = tar_member_group_end(stream, cursor)?;
        let member = parse_tar_member_group(&stream[cursor..group_end], max_path_length)?;
        if member.kind == TarEntryKind::Regular {
            total = total
                .checked_add(member.logical_size)
                .ok_or(FormatError::InvalidArchive(
                    "total extraction size overflow",
                ))?;
            if total > cap {
                return Err(FormatError::ReaderUnsupported(
                    "total extraction size exceeds configured cap",
                ));
            }
        }
        cursor = group_end;
    }
    Ok(())
}

pub(crate) struct TarStreamTotalExtractionSizeValidator {
    cursor: usize,
    total: u64,
    max_path_length: u32,
    cap: u64,
}

impl TarStreamTotalExtractionSizeValidator {
    pub(crate) fn new(max_path_length: u32, cap: u64) -> Self {
        Self {
            cursor: 0,
            total: 0,
            max_path_length,
            cap,
        }
    }

    pub(crate) fn observe(&mut self, stream: &[u8]) -> Result<(), FormatError> {
        while self.cursor < stream.len() {
            let Some(group_end) = try_tar_member_group_end(stream, self.cursor)? else {
                return Ok(());
            };
            let member =
                parse_tar_member_group(&stream[self.cursor..group_end], self.max_path_length)?;
            if member.kind == TarEntryKind::Regular {
                self.total = self.total.checked_add(member.logical_size).ok_or(
                    FormatError::InvalidArchive("total extraction size overflow"),
                )?;
                if self.total > self.cap {
                    return Err(FormatError::ReaderUnsupported(
                        "total extraction size exceeds configured cap",
                    ));
                }
            }
            self.cursor = group_end;
        }
        Ok(())
    }
}

pub(crate) struct TarStreamSummaryValidator<O = NoopTarStreamObserver> {
    state: StreamingTarState,
    max_path_length: u32,
    total_extraction_size: u64,
    extraction_cap: u64,
    max_metadata_payload_bytes: usize,
    max_member_count: u64,
    members: Vec<TarStreamMemberSummary>,
    observer: O,
}

impl<O: TarStreamObserver> TarStreamSummaryValidator<O> {
    pub(crate) fn with_observer(
        max_path_length: u32,
        extraction_cap: u64,
        max_metadata_payload_bytes: usize,
        max_member_count: u64,
        observer: O,
    ) -> Self {
        Self {
            state: StreamingTarState::new_member(0),
            max_path_length,
            total_extraction_size: 0,
            extraction_cap,
            max_metadata_payload_bytes,
            max_member_count,
            members: Vec::new(),
            observer,
        }
    }

    pub(crate) fn observe(&mut self, mut input: &[u8]) -> Result<(), FormatError> {
        while !input.is_empty() {
            let state = std::mem::replace(&mut self.state, StreamingTarState::new_member(0));
            let (consumed, next) = self.consume_state(state, input)?;
            self.state = self.resolve_ready_state(next)?;
            input = &input[consumed..];
        }
        Ok(())
    }

    fn consume_state(
        &mut self,
        state: StreamingTarState,
        input: &[u8],
    ) -> Result<(usize, StreamingTarState), FormatError> {
        match state {
            StreamingTarState::Header {
                metadata,
                group_start,
                mut group_size,
                mut header,
            } => {
                let needed = TAR_BLOCK_LEN - header.len();
                let take = needed.min(input.len());
                header.extend_from_slice(&input[..take]);
                group_size = checked_u64_add(group_size, take as u64)?;
                checked_u64_add(group_start, group_size)?;
                let next = if header.len() == TAR_BLOCK_LEN {
                    let mut header_bytes = [0u8; TAR_BLOCK_LEN];
                    header_bytes.copy_from_slice(&header);
                    self.state_after_header(metadata, group_start, group_size, header_bytes)?
                } else {
                    StreamingTarState::Header {
                        metadata,
                        group_start,
                        group_size,
                        header,
                    }
                };
                Ok((take, next))
            }
            StreamingTarState::Payload {
                metadata,
                group_start,
                mut group_size,
                mut entry,
                mut remaining,
                padding_remaining,
            } => {
                let take = remaining.min(input.len() as u64) as usize;
                if let Some(payload) = entry.metadata_payload_mut() {
                    let next_len = checked_add(payload.len(), take)?;
                    if next_len > self.max_metadata_payload_bytes {
                        return Err(FormatError::ReaderUnsupported(
                            "tar metadata payload exceeds configured streaming cap",
                        ));
                    }
                    payload.extend_from_slice(&input[..take]);
                } else if take > 0 && entry.is_regular_main() {
                    self.observer.on_regular_payload(&input[..take])?;
                }
                remaining -= take as u64;
                group_size = checked_u64_add(group_size, take as u64)?;
                checked_u64_add(group_start, group_size)?;
                let next = if remaining == 0 {
                    StreamingTarState::Padding {
                        metadata,
                        group_start,
                        group_size,
                        entry,
                        remaining: padding_remaining,
                    }
                } else {
                    StreamingTarState::Payload {
                        metadata,
                        group_start,
                        group_size,
                        entry,
                        remaining,
                        padding_remaining,
                    }
                };
                Ok((take, next))
            }
            StreamingTarState::Padding {
                metadata,
                group_start,
                mut group_size,
                entry,
                mut remaining,
            } => {
                let take = remaining.min(input.len() as u64) as usize;
                if input[..take].iter().any(|byte| *byte != 0) {
                    return Err(FormatError::InvalidArchive(
                        "tar member padding is non-zero",
                    ));
                }
                remaining -= take as u64;
                group_size = checked_u64_add(group_size, take as u64)?;
                checked_u64_add(group_start, group_size)?;
                let next = if remaining == 0 {
                    self.finish_entry_parts(metadata, group_start, group_size, entry)?
                } else {
                    StreamingTarState::Padding {
                        metadata,
                        group_start,
                        group_size,
                        entry,
                        remaining,
                    }
                };
                Ok((take, next))
            }
        }
    }

    fn resolve_ready_state(
        &mut self,
        mut state: StreamingTarState,
    ) -> Result<StreamingTarState, FormatError> {
        loop {
            state = match state {
                StreamingTarState::Payload {
                    metadata,
                    group_start,
                    group_size,
                    entry,
                    remaining: 0,
                    padding_remaining,
                } => StreamingTarState::Padding {
                    metadata,
                    group_start,
                    group_size,
                    entry,
                    remaining: padding_remaining,
                },
                StreamingTarState::Padding {
                    metadata,
                    group_start,
                    group_size,
                    entry,
                    remaining: 0,
                } => self.finish_entry_parts(metadata, group_start, group_size, entry)?,
                other => return Ok(other),
            };
        }
    }

    pub(crate) fn tar_total_size(&self) -> u64 {
        match &self.state {
            StreamingTarState::Header {
                group_start,
                group_size,
                ..
            }
            | StreamingTarState::Payload {
                group_start,
                group_size,
                ..
            }
            | StreamingTarState::Padding {
                group_start,
                group_size,
                ..
            } => group_start + group_size,
        }
    }

    pub(crate) fn finish(self) -> Result<TarStreamSummary, FormatError> {
        let tar_total_size = self.tar_total_size();
        match self.state {
            StreamingTarState::Header {
                header, group_size, ..
            } if header.is_empty() && group_size == 0 => Ok(TarStreamSummary {
                members: self.members,
                tar_total_size,
                total_extraction_size: self.total_extraction_size,
            }),
            _ => Err(FormatError::InvalidArchive(
                "tar stream ended inside member group",
            )),
        }
    }

    fn state_after_header(
        &mut self,
        mut metadata: LocalMetadata,
        group_start: u64,
        group_size: u64,
        header: [u8; TAR_BLOCK_LEN],
    ) -> Result<StreamingTarState, FormatError> {
        if header.iter().all(|byte| *byte == 0) {
            return Err(FormatError::InvalidArchive("tar member header is empty"));
        }
        verify_tar_checksum(&header)?;
        let typeflag = header[156];
        let header_size = parse_tar_octal(&header[124..136])?;
        let is_main = matches!(typeflag, 0 | b'0' | b'5' | b'2' | b'1');
        let effective_size = if is_main {
            metadata.pax_size.unwrap_or(header_size)
        } else {
            header_size
        };
        let padding_remaining = padding_to_512_u64(effective_size);

        let entry = match typeflag {
            b'x' => PendingTarEntry::LocalPax {
                payload: Vec::new(),
            },
            b'L' => PendingTarEntry::GnuLongName {
                payload: Vec::new(),
            },
            b'K' => PendingTarEntry::GnuLongLink {
                payload: Vec::new(),
            },
            b'g' => {
                return Err(FormatError::InvalidArchive(
                    "global PAX headers are not allowed",
                ))
            }
            b'V' | b'M' | b'N' => {
                return Err(FormatError::InvalidArchive(
                    "global GNU headers are not allowed",
                ))
            }
            b'S' => {
                return Err(FormatError::ReaderUnsupported(
                    "unsupported GNU sparse tar entry",
                ))
            }
            0 | b'0' | b'5' | b'2' | b'1' => {
                let kind = match typeflag {
                    b'5' => TarEntryKind::Directory,
                    b'2' => TarEntryKind::Symlink,
                    b'1' => TarEntryKind::Hardlink,
                    _ => TarEntryKind::Regular,
                };
                let mode = parse_tar_octal(&header[100..108])? as u32;
                let mtime = parse_tar_octal(&header[136..148])?;
                let path = canonical_main_path(&header, kind, &metadata, self.max_path_length)?;
                let link_target =
                    canonical_link_target(&header, kind, &path, &metadata, self.max_path_length)?;
                if kind != TarEntryKind::Regular && effective_size != 0 {
                    return Err(FormatError::InvalidArchive(
                        "non-regular tar entry has non-zero payload size",
                    ));
                }
                let logical_size = if kind == TarEntryKind::Regular {
                    effective_size
                } else {
                    0
                };
                if kind == TarEntryKind::Regular {
                    self.total_extraction_size =
                        self.total_extraction_size.checked_add(logical_size).ok_or(
                            FormatError::InvalidArchive("total extraction size overflow"),
                        )?;
                    if self.total_extraction_size > self.extraction_cap {
                        return Err(FormatError::ReaderUnsupported(
                            "total extraction size exceeds configured cap",
                        ));
                    }
                }
                let member = StreamedTarMemberMetadata {
                    path,
                    kind,
                    link_target,
                    mode,
                    mtime,
                    logical_size,
                    diagnostics: std::mem::take(&mut metadata.diagnostics),
                };
                self.observer.on_member_start(&member)?;
                PendingTarEntry::Main {
                    member,
                    group_start,
                }
            }
            _ => return Err(FormatError::ReaderUnsupported("unsupported tar entry type")),
        };

        self.resolve_ready_state(StreamingTarState::Payload {
            metadata,
            group_start,
            group_size,
            entry,
            remaining: effective_size,
            padding_remaining,
        })
    }

    fn finish_entry_parts(
        &mut self,
        mut metadata: LocalMetadata,
        group_start: u64,
        group_size: u64,
        entry: PendingTarEntry,
    ) -> Result<StreamingTarState, FormatError> {
        match entry {
            PendingTarEntry::LocalPax { payload } => {
                parse_pax_records(&payload, &mut metadata)?;
                Ok(StreamingTarState::Header {
                    metadata,
                    group_start,
                    group_size,
                    header: Vec::new(),
                })
            }
            PendingTarEntry::GnuLongName { payload } => {
                metadata.gnu_long_name = Some(trimmed_metadata_payload(&payload));
                Ok(StreamingTarState::Header {
                    metadata,
                    group_start,
                    group_size,
                    header: Vec::new(),
                })
            }
            PendingTarEntry::GnuLongLink { payload } => {
                metadata.gnu_long_link = Some(trimmed_metadata_payload(&payload));
                Ok(StreamingTarState::Header {
                    metadata,
                    group_start,
                    group_size,
                    header: Vec::new(),
                })
            }
            PendingTarEntry::Main {
                member,
                group_start,
            } => {
                if self.members.len() as u64 >= self.max_member_count {
                    return Err(FormatError::ReaderUnsupported(
                        "tar member count exceeds configured streaming cap",
                    ));
                }
                let diagnostics = self.observer.on_member_complete(&member)?;
                self.members.push(TarStreamMemberSummary {
                    path: member.path,
                    kind: member.kind,
                    link_target: member.link_target,
                    mode: member.mode,
                    mtime: member.mtime,
                    logical_size: member.logical_size,
                    diagnostics,
                    group_start,
                    group_size,
                });
                Ok(StreamingTarState::new_member(checked_u64_add(
                    group_start,
                    group_size,
                )?))
            }
        }
    }
}

enum StreamingTarState {
    Header {
        metadata: LocalMetadata,
        group_start: u64,
        group_size: u64,
        header: Vec<u8>,
    },
    Payload {
        metadata: LocalMetadata,
        group_start: u64,
        group_size: u64,
        entry: PendingTarEntry,
        remaining: u64,
        padding_remaining: u64,
    },
    Padding {
        metadata: LocalMetadata,
        group_start: u64,
        group_size: u64,
        entry: PendingTarEntry,
        remaining: u64,
    },
}

impl StreamingTarState {
    fn new_member(group_start: u64) -> Self {
        Self::Header {
            metadata: LocalMetadata::default(),
            group_start,
            group_size: 0,
            header: Vec::new(),
        }
    }
}

enum PendingTarEntry {
    LocalPax {
        payload: Vec<u8>,
    },
    GnuLongName {
        payload: Vec<u8>,
    },
    GnuLongLink {
        payload: Vec<u8>,
    },
    Main {
        member: StreamedTarMemberMetadata,
        group_start: u64,
    },
}

impl PendingTarEntry {
    fn metadata_payload_mut(&mut self) -> Option<&mut Vec<u8>> {
        match self {
            Self::LocalPax { payload }
            | Self::GnuLongName { payload }
            | Self::GnuLongLink { payload } => Some(payload),
            Self::Main { .. } => None,
        }
    }

    fn is_regular_main(&self) -> bool {
        matches!(
            self,
            Self::Main {
                member: StreamedTarMemberMetadata {
                    kind: TarEntryKind::Regular,
                    ..
                },
                ..
            }
        )
    }
}

fn checked_u64_add(lhs: u64, rhs: u64) -> Result<u64, FormatError> {
    lhs.checked_add(rhs).ok_or(FormatError::InvalidArchive(
        "tar member arithmetic overflow",
    ))
}

pub(crate) fn try_tar_member_group_end(
    stream: &[u8],
    start: usize,
) -> Result<Option<usize>, FormatError> {
    let mut cursor = start;
    let mut metadata = LocalMetadata::default();

    loop {
        let Some(header) = try_slice(stream, cursor, TAR_BLOCK_LEN)? else {
            return Ok(None);
        };
        if header.iter().all(|byte| *byte == 0) {
            return Err(FormatError::InvalidArchive("tar member header is empty"));
        }
        verify_tar_checksum(header)?;
        let typeflag = header[156];
        let header_size = parse_tar_octal(&header[124..136])?;
        let is_main = matches!(typeflag, 0 | b'0' | b'5' | b'2' | b'1');
        let effective_size = if is_main {
            metadata.pax_size.unwrap_or(header_size)
        } else {
            header_size
        };
        let payload_start = checked_add(cursor, TAR_BLOCK_LEN)?;
        let payload_len = to_usize(effective_size)?;
        let payload_end = checked_add(payload_start, payload_len)?;
        let padded_end = checked_add(payload_end, padding_to_512(payload_len))?;
        let Some(payload) = try_slice(stream, payload_start, payload_len)? else {
            return Ok(None);
        };
        if padded_end > stream.len() {
            return Ok(None);
        }
        if stream[payload_end..padded_end]
            .iter()
            .any(|byte| *byte != 0)
        {
            return Err(FormatError::InvalidArchive(
                "tar member padding is non-zero",
            ));
        }

        match typeflag {
            b'x' => {
                parse_pax_records(payload, &mut metadata)?;
                cursor = padded_end;
            }
            b'L' | b'K' => {
                cursor = padded_end;
            }
            b'g' => {
                return Err(FormatError::InvalidArchive(
                    "global PAX headers are not allowed",
                ));
            }
            b'V' | b'M' | b'N' => {
                return Err(FormatError::InvalidArchive(
                    "global GNU headers are not allowed",
                ));
            }
            b'S' => {
                return Err(FormatError::ReaderUnsupported(
                    "unsupported GNU sparse tar entry",
                ));
            }
            0 | b'0' | b'5' | b'2' | b'1' => return Ok(Some(padded_end)),
            _ => return Err(FormatError::ReaderUnsupported("unsupported tar entry type")),
        }

        if cursor >= stream.len() {
            return Ok(None);
        }
    }
}

fn try_slice(stream: &[u8], offset: usize, len: usize) -> Result<Option<&[u8]>, FormatError> {
    let end = checked_add(offset, len)?;
    if end > stream.len() {
        return Ok(None);
    }
    Ok(Some(&stream[offset..end]))
}

pub(crate) fn stream_regular_tar_member_group_to_writer<R, W>(
    reader: &mut R,
    expected_path: &[u8],
    expected_file_data_size: u64,
    group_len: u64,
    max_path_length: u32,
    writer: &mut W,
) -> Result<Vec<MetadataDiagnostic>, ExtractError>
where
    R: TarMemberGroupReader,
    W: Write,
{
    let mut handler = RegularWriterHandler { writer };
    let member = stream_tar_member_group(
        reader,
        expected_path,
        expected_file_data_size,
        group_len,
        max_path_length,
        &mut handler,
    )?;
    Ok(member.diagnostics)
}

pub(crate) fn restore_streaming_tar_member_group<R>(
    root: &Path,
    expected_path: &[u8],
    expected_file_data_size: u64,
    group_len: u64,
    max_path_length: u32,
    options: SafeExtractionOptions,
    reader: &mut R,
) -> Result<Vec<MetadataDiagnostic>, ExtractError>
where
    R: TarMemberGroupReader,
{
    let mut handler = FilesystemRestoreHandler::new(root, options);
    let member = stream_tar_member_group(
        reader,
        expected_path,
        expected_file_data_size,
        group_len,
        max_path_length,
        &mut handler,
    )?;
    handler.finish(&member)
}

fn stream_tar_member_group<R, H>(
    reader: &mut R,
    expected_path: &[u8],
    expected_file_data_size: u64,
    group_len: u64,
    max_path_length: u32,
    handler: &mut H,
) -> Result<StreamedTarMemberMetadata, ExtractError>
where
    R: TarMemberGroupReader,
    H: TarMemberStreamHandler,
{
    if group_len < TAR_BLOCK_LEN as u64 || group_len % TAR_BLOCK_LEN as u64 != 0 {
        return Err(FormatError::InvalidArchive("tar member group is not block aligned").into());
    }

    let mut remaining = group_len;
    let mut metadata = LocalMetadata::default();

    loop {
        let mut header = [0u8; TAR_BLOCK_LEN];
        read_member_bytes(reader, &mut header, &mut remaining)?;
        if header.iter().all(|byte| *byte == 0) {
            return Err(FormatError::InvalidArchive("tar member header is empty").into());
        }
        verify_tar_checksum(&header)?;

        let typeflag = header[156];
        let header_size = parse_tar_octal(&header[124..136])?;
        let is_main = matches!(typeflag, 0 | b'0' | b'5' | b'2' | b'1');
        let effective_size = if is_main {
            metadata.pax_size.unwrap_or(header_size)
        } else {
            header_size
        };
        let padding_len = padding_to_512_u64(effective_size);
        let entry_payload_len =
            effective_size
                .checked_add(padding_len)
                .ok_or(FormatError::InvalidArchive(
                    "tar member arithmetic overflow",
                ))?;
        if entry_payload_len > remaining {
            return Err(FormatError::InvalidArchive("tar member payload exceeds group").into());
        }

        match typeflag {
            b'x' => {
                let payload = read_member_vec(reader, effective_size, &mut remaining)?;
                parse_pax_records(&payload, &mut metadata)?;
                read_zero_padding(reader, padding_len, &mut remaining)?;
            }
            b'g' => {
                return Err(
                    FormatError::InvalidArchive("global PAX headers are not allowed").into(),
                );
            }
            b'V' | b'M' | b'N' => {
                return Err(
                    FormatError::InvalidArchive("global GNU headers are not allowed").into(),
                );
            }
            b'L' => {
                let payload = read_member_vec(reader, effective_size, &mut remaining)?;
                metadata.gnu_long_name = Some(trimmed_metadata_payload(&payload));
                read_zero_padding(reader, padding_len, &mut remaining)?;
            }
            b'K' => {
                let payload = read_member_vec(reader, effective_size, &mut remaining)?;
                metadata.gnu_long_link = Some(trimmed_metadata_payload(&payload));
                read_zero_padding(reader, padding_len, &mut remaining)?;
            }
            b'S' => {
                return Err(
                    FormatError::ReaderUnsupported("unsupported GNU sparse tar entry").into(),
                );
            }
            0 | b'0' | b'5' | b'2' | b'1' => {
                let kind = match typeflag {
                    b'5' => TarEntryKind::Directory,
                    b'2' => TarEntryKind::Symlink,
                    b'1' => TarEntryKind::Hardlink,
                    _ => TarEntryKind::Regular,
                };
                let mode = parse_tar_octal(&header[100..108])? as u32;
                let mtime = parse_tar_octal(&header[136..148])?;
                let path = canonical_main_path(&header, kind, &metadata, max_path_length)?;
                let link_target =
                    canonical_link_target(&header, kind, &path, &metadata, max_path_length)?;
                if kind != TarEntryKind::Regular && effective_size != 0 {
                    return Err(FormatError::InvalidArchive(
                        "non-regular tar entry has non-zero payload size",
                    )
                    .into());
                }
                let logical_size = if kind == TarEntryKind::Regular {
                    effective_size
                } else {
                    0
                };
                let member = StreamedTarMemberMetadata {
                    path,
                    kind,
                    link_target,
                    mode,
                    mtime,
                    logical_size,
                    diagnostics: std::mem::take(&mut metadata.diagnostics),
                };
                if member.path != expected_path {
                    return Err(FormatError::InvalidArchive(
                        "tar member path does not match FileEntry path",
                    )
                    .into());
                }
                if member.logical_size != expected_file_data_size {
                    return Err(FormatError::InvalidArchive(
                        "tar member size does not match FileEntry file_data_size",
                    )
                    .into());
                }
                handler.on_member(&member)?;
                if member.kind == TarEntryKind::Regular {
                    stream_regular_payload(reader, effective_size, &mut remaining, handler)?;
                }
                read_zero_padding(reader, padding_len, &mut remaining)?;
                if remaining != 0 {
                    return Err(FormatError::InvalidArchive(
                        "tar member group has bytes after main entry",
                    )
                    .into());
                }
                return Ok(member);
            }
            _ => {
                return Err(FormatError::ReaderUnsupported("unsupported tar entry type").into());
            }
        }

        if remaining == 0 {
            return Err(FormatError::InvalidArchive(
                "tar member group has metadata records but no main entry",
            )
            .into());
        }
    }
}

struct RegularWriterHandler<'a, W> {
    writer: &'a mut W,
}

impl<W: Write> TarMemberStreamHandler for RegularWriterHandler<'_, W> {
    fn on_member(&mut self, member: &StreamedTarMemberMetadata) -> Result<(), ExtractError> {
        if member.kind != TarEntryKind::Regular {
            return Err(FormatError::ReaderUnsupported(
                "extract_file_to_writer returns only regular file payloads",
            )
            .into());
        }
        Ok(())
    }

    fn write_regular_payload(&mut self, bytes: &[u8]) -> Result<(), ExtractError> {
        self.writer.write_all(bytes).map_err(ExtractError::Output)
    }
}

struct FilesystemRestoreHandler<'a> {
    root: &'a Path,
    options: SafeExtractionOptions,
    destination: Option<PathBuf>,
    temp_path: Option<PathBuf>,
    file: Option<fs::File>,
}

impl<'a> FilesystemRestoreHandler<'a> {
    fn new(root: &'a Path, options: SafeExtractionOptions) -> Self {
        Self {
            root,
            options,
            destination: None,
            temp_path: None,
            file: None,
        }
    }

    fn finish(
        &mut self,
        member: &StreamedTarMemberMetadata,
    ) -> Result<Vec<MetadataDiagnostic>, ExtractError> {
        let mut diagnostics = member.diagnostics.clone();
        if member.kind != TarEntryKind::Regular {
            return Ok(diagnostics);
        }

        let mut file = self.file.take().ok_or(FormatError::InvalidArchive(
            "regular file output is missing",
        ))?;
        file.flush()
            .map_err(|_| FormatError::FilesystemExtractionFailed("failed to write regular file"))?;
        apply_restored_regular_file_metadata_parts(
            &file,
            member.mode,
            member.mtime,
            &mut diagnostics,
        );
        drop(file);

        let destination = self.destination.take().ok_or(FormatError::InvalidArchive(
            "regular file destination is missing",
        ))?;
        let temp_path = self.temp_path.take().ok_or(FormatError::InvalidArchive(
            "regular file temp path is missing",
        ))?;
        if self.options.overwrite_existing && destination.exists() {
            fs::remove_file(&destination).map_err(|_| {
                FormatError::FilesystemExtractionFailed("failed to remove old file")
            })?;
        }
        fs::rename(&temp_path, &destination).map_err(|_| {
            FormatError::FilesystemExtractionFailed("failed to create regular file")
        })?;
        Ok(diagnostics)
    }
}

impl Drop for FilesystemRestoreHandler<'_> {
    fn drop(&mut self) {
        if let Some(path) = self.temp_path.take() {
            let _ = fs::remove_file(path);
        }
    }
}

impl TarMemberStreamHandler for FilesystemRestoreHandler<'_> {
    fn on_member(&mut self, member: &StreamedTarMemberMetadata) -> Result<(), ExtractError> {
        let destination = prepare_destination(self.root, &member.path, member.kind, self.options)?;
        match member.kind {
            TarEntryKind::Regular => {
                let (temp_path, file) = create_temp_regular_file(&destination)?;
                self.destination = Some(destination);
                self.temp_path = Some(temp_path);
                self.file = Some(file);
            }
            TarEntryKind::Directory => create_directory(&destination)?,
            TarEntryKind::Symlink => {
                let target = member
                    .link_target
                    .as_deref()
                    .ok_or(FormatError::InvalidArchive("symlink target is missing"))?;
                validate_symlink_target(&member.path, target)?;
                create_symlink(&destination, target, self.options)?;
            }
            TarEntryKind::Hardlink => {
                let target = member
                    .link_target
                    .as_deref()
                    .ok_or(FormatError::InvalidArchive("hardlink target is missing"))?;
                let target_path = existing_safe_regular_path(self.root, target)?;
                create_hardlink(&destination, &target_path, self.options)?;
            }
        }
        Ok(())
    }

    fn write_regular_payload(&mut self, bytes: &[u8]) -> Result<(), ExtractError> {
        let file = self.file.as_mut().ok_or(FormatError::InvalidArchive(
            "regular file output is missing",
        ))?;
        file.write_all(bytes)
            .map_err(|_| FormatError::FilesystemExtractionFailed("failed to write regular file"))?;
        Ok(())
    }
}

fn format_error_from_extract_error(error: ExtractError) -> FormatError {
    match error {
        ExtractError::Format(error) => error,
        ExtractError::Output(_) => {
            FormatError::FilesystemExtractionFailed("failed to write regular file")
        }
    }
}

fn read_member_bytes<R: TarMemberGroupReader>(
    reader: &mut R,
    buf: &mut [u8],
    remaining: &mut u64,
) -> Result<(), ExtractError> {
    if buf.len() as u64 > *remaining {
        return Err(FormatError::InvalidArchive("tar member payload exceeds group").into());
    }
    reader.read_exact_member_bytes(buf)?;
    *remaining -= buf.len() as u64;
    Ok(())
}

fn read_member_vec<R: TarMemberGroupReader>(
    reader: &mut R,
    len: u64,
    remaining: &mut u64,
) -> Result<Vec<u8>, ExtractError> {
    let mut out = vec![0u8; to_usize(len)?];
    read_member_bytes(reader, &mut out, remaining)?;
    Ok(out)
}

fn read_zero_padding<R: TarMemberGroupReader>(
    reader: &mut R,
    len: u64,
    remaining: &mut u64,
) -> Result<(), ExtractError> {
    let mut pending = len;
    let mut buf = [0u8; 8192];
    while pending > 0 {
        let chunk_len = pending.min(buf.len() as u64) as usize;
        read_member_bytes(reader, &mut buf[..chunk_len], remaining)?;
        if buf[..chunk_len].iter().any(|byte| *byte != 0) {
            return Err(FormatError::InvalidArchive("tar member padding is non-zero").into());
        }
        pending -= chunk_len as u64;
    }
    Ok(())
}

fn stream_regular_payload<R, H>(
    reader: &mut R,
    len: u64,
    remaining: &mut u64,
    handler: &mut H,
) -> Result<(), ExtractError>
where
    R: TarMemberGroupReader,
    H: TarMemberStreamHandler,
{
    let mut pending = len;
    let mut buf = [0u8; 64 * 1024];
    while pending > 0 {
        let chunk_len = pending.min(buf.len() as u64).min(*remaining) as usize;
        let read = reader.read_some_member_bytes(&mut buf[..chunk_len])?;
        if read == 0 {
            return Err(FormatError::InvalidArchive("tar member group exceeds frame range").into());
        }
        *remaining -= read as u64;
        pending -= read as u64;
        handler.write_regular_payload(&buf[..read])?;
    }
    Ok(())
}

fn tar_member_group_end(stream: &[u8], start: usize) -> Result<usize, FormatError> {
    let mut cursor = start;
    let mut metadata = LocalMetadata::default();

    loop {
        let header = slice(stream, cursor, TAR_BLOCK_LEN)?;
        if header.iter().all(|byte| *byte == 0) {
            return Err(FormatError::InvalidArchive("tar member header is empty"));
        }
        verify_tar_checksum(header)?;
        let typeflag = header[156];
        let header_size = parse_tar_octal(&header[124..136])?;
        let is_main = matches!(typeflag, 0 | b'0' | b'5' | b'2' | b'1');
        let effective_size = if is_main {
            metadata.pax_size.unwrap_or(header_size)
        } else {
            header_size
        };
        let payload_start = checked_add(cursor, TAR_BLOCK_LEN)?;
        let payload_len = to_usize(effective_size)?;
        let payload_end = checked_add(payload_start, payload_len)?;
        let padded_end = checked_add(payload_end, padding_to_512(payload_len))?;
        let payload = slice(stream, payload_start, payload_len)?;
        if padded_end > stream.len() {
            return Err(FormatError::InvalidArchive(
                "tar member payload exceeds stream",
            ));
        }
        if stream[payload_end..padded_end]
            .iter()
            .any(|byte| *byte != 0)
        {
            return Err(FormatError::InvalidArchive(
                "tar member padding is non-zero",
            ));
        }

        match typeflag {
            b'x' => {
                parse_pax_records(payload, &mut metadata)?;
                cursor = padded_end;
            }
            b'L' | b'K' => {
                cursor = padded_end;
            }
            b'g' => {
                return Err(FormatError::InvalidArchive(
                    "global PAX headers are not allowed",
                ));
            }
            b'V' | b'M' | b'N' => {
                return Err(FormatError::InvalidArchive(
                    "global GNU headers are not allowed",
                ));
            }
            b'S' => {
                return Err(FormatError::ReaderUnsupported(
                    "unsupported GNU sparse tar entry",
                ));
            }
            0 | b'0' | b'5' | b'2' | b'1' => return Ok(padded_end),
            _ => return Err(FormatError::ReaderUnsupported("unsupported tar entry type")),
        }

        if cursor >= stream.len() {
            return Err(FormatError::InvalidArchive(
                "tar member group has metadata records but no main entry",
            ));
        }
    }
}

pub fn restore_tar_member(
    root: &Path,
    member: &OwnedTarMember,
    options: SafeExtractionOptions,
) -> Result<Vec<MetadataDiagnostic>, FormatError> {
    let destination = prepare_destination(root, &member.path, member.kind, options)?;
    let mut diagnostics = member.diagnostics.clone();
    match member.kind {
        TarEntryKind::Regular => {
            let file = write_regular_file(&destination, &member.data, options)?;
            apply_restored_regular_file_metadata(&file, member, &mut diagnostics);
        }
        TarEntryKind::Directory => create_directory(&destination)?,
        TarEntryKind::Symlink => {
            let target = member
                .link_target
                .as_deref()
                .ok_or(FormatError::InvalidArchive("symlink target is missing"))?;
            validate_symlink_target(&member.path, target)?;
            create_symlink(&destination, target, options)?;
        }
        TarEntryKind::Hardlink => {
            let target = member
                .link_target
                .as_deref()
                .ok_or(FormatError::InvalidArchive("hardlink target is missing"))?;
            let target_path = existing_safe_regular_path(root, target)?;
            create_hardlink(&destination, &target_path, options)?;
        }
    }
    Ok(diagnostics)
}

fn apply_restored_regular_file_metadata(
    file: &fs::File,
    member: &OwnedTarMember,
    diagnostics: &mut Vec<MetadataDiagnostic>,
) {
    apply_restored_regular_file_metadata_parts(file, member.mode, member.mtime, diagnostics);
}

fn apply_restored_regular_file_metadata_parts(
    file: &fs::File,
    mode: u32,
    mtime: u64,
    diagnostics: &mut Vec<MetadataDiagnostic>,
) {
    apply_regular_file_mtime(file, mtime, diagnostics);
    apply_regular_file_mode(file, mode, diagnostics);
}

#[cfg(unix)]
fn apply_regular_file_mode(file: &fs::File, mode: u32, diagnostics: &mut Vec<MetadataDiagnostic>) {
    match file.metadata() {
        Ok(metadata) => {
            let mut permissions = metadata.permissions();
            permissions.set_mode(mode & 0o7777);
            if file.set_permissions(permissions).is_err() {
                diagnostics.push(MetadataDiagnostic {
                    profile: "ustar-baseline",
                    message: "failed to apply mode metadata",
                });
            }
        }
        Err(_) => diagnostics.push(MetadataDiagnostic {
            profile: "ustar-baseline",
            message: "failed to apply mode metadata",
        }),
    }
}

#[cfg(not(unix))]
fn apply_regular_file_mode(file: &fs::File, mode: u32, diagnostics: &mut Vec<MetadataDiagnostic>) {
    match file.metadata() {
        Ok(metadata) => {
            let mut permissions = metadata.permissions();
            permissions.set_readonly(mode & 0o222 == 0);
            if file.set_permissions(permissions).is_err() {
                diagnostics.push(MetadataDiagnostic {
                    profile: "ustar-baseline",
                    message: "failed to apply mode metadata",
                });
                return;
            }
            if mode & 0o777 != 0o444 && mode & 0o777 != 0o666 {
                diagnostics.push(MetadataDiagnostic {
                    profile: "ustar-baseline",
                    message: "mode metadata was only partially applied on this platform",
                });
            }
        }
        Err(_) => diagnostics.push(MetadataDiagnostic {
            profile: "ustar-baseline",
            message: "failed to apply mode metadata",
        }),
    }
}

fn apply_regular_file_mtime(
    file: &fs::File,
    mtime: u64,
    diagnostics: &mut Vec<MetadataDiagnostic>,
) {
    let Some(modified) = SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(mtime)) else {
        diagnostics.push(MetadataDiagnostic {
            profile: "ustar-baseline",
            message: "failed to apply mtime metadata",
        });
        return;
    };
    let times = fs::FileTimes::new().set_modified(modified);
    if file.set_times(times).is_err() {
        diagnostics.push(MetadataDiagnostic {
            profile: "ustar-baseline",
            message: "failed to apply mtime metadata",
        });
    }
}

fn canonical_main_path(
    header: &[u8],
    kind: TarEntryKind,
    metadata: &LocalMetadata,
    max_path_length: u32,
) -> Result<Vec<u8>, FormatError> {
    let mut path = metadata
        .pax_path
        .clone()
        .or_else(|| metadata.gnu_long_name.clone())
        .unwrap_or_else(|| ustar_path(header));
    if kind == TarEntryKind::Directory && path.ends_with(b"/") && !path.ends_with(b"//") {
        path.pop();
    }
    validate_file_path_bytes(&path, max_path_length)?;
    Ok(path)
}

fn canonical_link_target(
    header: &[u8],
    kind: TarEntryKind,
    link_path: &[u8],
    metadata: &LocalMetadata,
    max_path_length: u32,
) -> Result<Option<Vec<u8>>, FormatError> {
    if !matches!(kind, TarEntryKind::Symlink | TarEntryKind::Hardlink) {
        return Ok(None);
    }
    let target = metadata
        .pax_linkpath
        .clone()
        .or_else(|| metadata.gnu_long_link.clone())
        .unwrap_or_else(|| nul_trimmed(&header[157..257]).to_vec());
    if target.is_empty() {
        return Err(FormatError::UnsafeArchivePath);
    }
    match kind {
        TarEntryKind::Hardlink => validate_file_path_bytes(&target, max_path_length)?,
        TarEntryKind::Symlink => validate_symlink_target(link_path, &target)?,
        _ => {}
    }
    Ok(Some(target))
}

fn parse_pax_records(payload: &[u8], metadata: &mut LocalMetadata) -> Result<(), FormatError> {
    let mut cursor = 0usize;
    while cursor < payload.len() {
        let len_digits_start = cursor;
        while cursor < payload.len() && payload[cursor].is_ascii_digit() {
            cursor += 1;
        }
        if cursor == len_digits_start || cursor >= payload.len() || payload[cursor] != b' ' {
            return Err(FormatError::InvalidArchive("malformed PAX record"));
        }
        let len = parse_decimal(&payload[len_digits_start..cursor])?;
        let record_start = len_digits_start;
        let record_end = checked_add(record_start, len)?;
        if record_end > payload.len() || len < 4 {
            return Err(FormatError::InvalidArchive("malformed PAX record"));
        }
        let body_start = cursor + 1;
        let record = &payload[body_start..record_end];
        if record.last().copied() != Some(b'\n') {
            return Err(FormatError::InvalidArchive("malformed PAX record"));
        }
        let body = &record[..record.len() - 1];
        let eq = body
            .iter()
            .position(|byte| *byte == b'=')
            .ok_or(FormatError::InvalidArchive("malformed PAX record"))?;
        let key = std::str::from_utf8(&body[..eq])
            .map_err(|_| FormatError::InvalidArchive("malformed PAX key"))?;
        let value = &body[eq + 1..];
        match key {
            "path" => metadata.pax_path = Some(value.to_vec()),
            "linkpath" => metadata.pax_linkpath = Some(value.to_vec()),
            "size" => metadata.pax_size = Some(parse_decimal(value)? as u64),
            "atime" | "ctime" | "mtime" => metadata.diagnostics.push(MetadataDiagnostic {
                profile: "pax-posix-2001",
                message: "unsupported PAX timestamp metadata was ignored",
            }),
            key if key.starts_with("SCHILY.xattr.")
                || key.starts_with("LIBARCHIVE.xattr.")
                || key.starts_with("SCHILY.acl.")
                || key.starts_with("LIBARCHIVE.acl.") =>
            {
                metadata.diagnostics.push(MetadataDiagnostic {
                    profile: "pax-xattrs-acls",
                    message: "unsupported xattr/ACL PAX metadata was ignored",
                });
            }
            key if key.starts_with("GNU.sparse.") => {
                metadata.diagnostics.push(MetadataDiagnostic {
                    profile: "gnu-sparse",
                    message: "unsupported sparse-file PAX metadata was ignored",
                });
            }
            _ => metadata.diagnostics.push(MetadataDiagnostic {
                profile: "pax-posix-2001",
                message: "unsupported PAX key was ignored",
            }),
        }
        cursor = record_end;
    }
    Ok(())
}

fn validate_symlink_target(link_path: &[u8], target: &[u8]) -> Result<(), FormatError> {
    if target.is_empty()
        || target.contains(&0)
        || target.contains(&b'\\')
        || target.contains(&b':')
        || target[0] == b'/'
    {
        return Err(FormatError::UnsafeArchivePath);
    }
    let target = std::str::from_utf8(target).map_err(|_| FormatError::UnsafeArchivePath)?;
    let link_path = std::str::from_utf8(link_path).map_err(|_| FormatError::UnsafeArchivePath)?;
    let mut stack = link_path
        .split('/')
        .take(link_path.split('/').count().saturating_sub(1))
        .map(str::to_owned)
        .collect::<Vec<_>>();
    for component in target.split('/') {
        if component.is_empty() || component == "." {
            return Err(FormatError::UnsafeArchivePath);
        }
        if component == ".." {
            if stack.pop().is_none() {
                return Err(FormatError::UnsafeArchivePath);
            }
        } else {
            validate_file_path_bytes(component.as_bytes(), u32::MAX)?;
            stack.push(component.to_owned());
        }
    }
    Ok(())
}

fn prepare_destination(
    root: &Path,
    archive_path: &[u8],
    kind: TarEntryKind,
    options: SafeExtractionOptions,
) -> Result<PathBuf, FormatError> {
    let components = path_components(archive_path)?;
    validate_root(root)?;
    let mut current = root.to_path_buf();
    for component in &components[..components.len().saturating_sub(1)] {
        current.push(component);
        match fs::symlink_metadata(&current) {
            Ok(metadata) => {
                let file_type = metadata.file_type();
                if file_type.is_symlink() || !file_type.is_dir() {
                    return Err(FormatError::UnsafeArchivePath);
                }
            }
            Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
                match fs::create_dir(&current) {
                    Ok(()) => {}
                    Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {}
                    Err(_) => {
                        return Err(FormatError::FilesystemExtractionFailed(
                            "failed to create parent directory",
                        ));
                    }
                }
                let metadata = fs::symlink_metadata(&current).map_err(|_| {
                    FormatError::FilesystemExtractionFailed(
                        "failed to inspect created parent directory",
                    )
                })?;
                if metadata.file_type().is_symlink() || !metadata.file_type().is_dir() {
                    return Err(FormatError::UnsafeArchivePath);
                }
            }
            Err(_) => {
                return Err(FormatError::FilesystemExtractionFailed(
                    "failed to inspect parent directory",
                ));
            }
        }
    }

    current.push(components.last().ok_or(FormatError::UnsafeArchivePath)?);
    match fs::symlink_metadata(&current) {
        Ok(metadata) => {
            let file_type = metadata.file_type();
            if file_type.is_symlink() {
                return Err(FormatError::UnsafeArchivePath);
            }
            if kind == TarEntryKind::Directory {
                if file_type.is_dir() {
                    return Ok(current);
                }
                return Err(FormatError::UnsafeOverwrite);
            }
            if file_type.is_dir() {
                return Err(FormatError::UnsafeOverwrite);
            }
            if !options.overwrite_existing {
                return Err(FormatError::UnsafeOverwrite);
            }
        }
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
        Err(_) => {
            return Err(FormatError::FilesystemExtractionFailed(
                "failed to inspect destination",
            ));
        }
    }
    Ok(current)
}

fn validate_root(root: &Path) -> Result<(), FormatError> {
    let metadata = fs::symlink_metadata(root).map_err(|_| {
        FormatError::FilesystemExtractionFailed("extraction root must already exist")
    })?;
    if metadata.file_type().is_symlink() || !metadata.file_type().is_dir() {
        return Err(FormatError::UnsafeArchivePath);
    }
    Ok(())
}

fn existing_safe_regular_path(root: &Path, archive_path: &[u8]) -> Result<PathBuf, FormatError> {
    validate_file_path_bytes(archive_path, u32::MAX)?;
    let components = path_components(archive_path)?;
    validate_root(root)?;
    let mut current = root.to_path_buf();
    for (idx, component) in components.iter().enumerate() {
        current.push(component);
        let metadata =
            fs::symlink_metadata(&current).map_err(|_| FormatError::UnsafeArchivePath)?;
        if metadata.file_type().is_symlink() {
            return Err(FormatError::UnsafeArchivePath);
        }
        if idx + 1 != components.len() {
            if !metadata.file_type().is_dir() {
                return Err(FormatError::UnsafeArchivePath);
            }
        } else if !metadata.file_type().is_file() {
            return Err(FormatError::UnsafeArchivePath);
        }
    }
    Ok(current)
}

fn write_regular_file(
    destination: &Path,
    data: &[u8],
    options: SafeExtractionOptions,
) -> Result<fs::File, FormatError> {
    if options.overwrite_existing && destination.exists() {
        fs::remove_file(destination)
            .map_err(|_| FormatError::FilesystemExtractionFailed("failed to remove old file"))?;
    }
    let mut file = OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(destination)
        .map_err(|_| FormatError::FilesystemExtractionFailed("failed to create regular file"))?;
    file.write_all(data)
        .map_err(|_| FormatError::FilesystemExtractionFailed("failed to write regular file"))?;
    Ok(file)
}

fn create_temp_regular_file(destination: &Path) -> Result<(PathBuf, fs::File), FormatError> {
    let pid = std::process::id();
    for attempt in 0..1000u32 {
        let mut candidate = destination.as_os_str().to_os_string();
        candidate.push(format!(".tzap-tmp-{pid}-{attempt}"));
        let path = PathBuf::from(candidate);
        match OpenOptions::new().write(true).create_new(true).open(&path) {
            Ok(file) => return Ok((path, file)),
            Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {}
            Err(_) => {
                return Err(FormatError::FilesystemExtractionFailed(
                    "failed to create regular file",
                ));
            }
        }
    }
    Err(FormatError::FilesystemExtractionFailed(
        "failed to create regular file",
    ))
}

fn create_directory(destination: &Path) -> Result<(), FormatError> {
    match fs::create_dir(destination) {
        Ok(()) => Ok(()),
        Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => {
            let metadata =
                fs::symlink_metadata(destination).map_err(|_| FormatError::UnsafeOverwrite)?;
            if metadata.file_type().is_dir() {
                Ok(())
            } else {
                Err(FormatError::UnsafeOverwrite)
            }
        }
        Err(_) => Err(FormatError::FilesystemExtractionFailed(
            "failed to create directory",
        )),
    }
}

fn create_hardlink(
    destination: &Path,
    target: &Path,
    options: SafeExtractionOptions,
) -> Result<(), FormatError> {
    if options.overwrite_existing && destination.exists() {
        fs::remove_file(destination)
            .map_err(|_| FormatError::FilesystemExtractionFailed("failed to remove old file"))?;
    }
    fs::hard_link(target, destination)
        .map_err(|_| FormatError::FilesystemExtractionFailed("failed to create hardlink"))
}

#[cfg(unix)]
fn create_symlink(
    destination: &Path,
    target: &[u8],
    options: SafeExtractionOptions,
) -> Result<(), FormatError> {
    if options.overwrite_existing && destination.exists() {
        fs::remove_file(destination)
            .map_err(|_| FormatError::FilesystemExtractionFailed("failed to remove old file"))?;
    }
    let target = std::str::from_utf8(target).map_err(|_| FormatError::UnsafeArchivePath)?;
    std::os::unix::fs::symlink(target, destination)
        .map_err(|_| FormatError::FilesystemExtractionFailed("failed to create symlink"))
}

#[cfg(windows)]
fn create_symlink(
    destination: &Path,
    target: &[u8],
    options: SafeExtractionOptions,
) -> Result<(), FormatError> {
    if options.overwrite_existing && destination.exists() {
        fs::remove_file(destination)
            .map_err(|_| FormatError::FilesystemExtractionFailed("failed to remove old file"))?;
    }
    let target = std::str::from_utf8(target).map_err(|_| FormatError::UnsafeArchivePath)?;
    std::os::windows::fs::symlink_file(target, destination)
        .map_err(|_| FormatError::FilesystemExtractionFailed("failed to create symlink"))
}

fn path_components(path: &[u8]) -> Result<Vec<String>, FormatError> {
    validate_file_path_bytes(path, u32::MAX)?;
    let path = std::str::from_utf8(path).map_err(|_| FormatError::UnsafeArchivePath)?;
    Ok(path.split('/').map(str::to_owned).collect())
}

fn ustar_path(header: &[u8]) -> Vec<u8> {
    let name = nul_trimmed(&header[0..100]);
    let prefix = nul_trimmed(&header[345..500]);
    if prefix.is_empty() {
        name.to_vec()
    } else {
        let mut out = Vec::with_capacity(prefix.len() + 1 + name.len());
        out.extend_from_slice(prefix);
        out.push(b'/');
        out.extend_from_slice(name);
        out
    }
}

fn trimmed_metadata_payload(payload: &[u8]) -> Vec<u8> {
    let mut end = payload.len();
    while end > 0 && payload[end - 1] == 0 {
        end -= 1;
    }
    payload[..end].to_vec()
}

fn verify_tar_checksum(header: &[u8]) -> Result<(), FormatError> {
    let stored = parse_tar_octal(&header[148..156])?;
    let mut sum = 0u64;
    for (idx, byte) in header.iter().enumerate() {
        if (148..156).contains(&idx) {
            sum += b' ' as u64;
        } else {
            sum += *byte as u64;
        }
    }
    if stored != sum {
        return Err(FormatError::InvalidArchive("tar header checksum mismatch"));
    }
    Ok(())
}

fn parse_tar_octal(field: &[u8]) -> Result<u64, FormatError> {
    let mut value = 0u64;
    let mut saw_digit = false;
    for byte in field {
        match *byte {
            0 | b' ' if saw_digit => break,
            0 | b' ' => {}
            b'0'..=b'7' => {
                saw_digit = true;
                value = value
                    .checked_mul(8)
                    .and_then(|acc| acc.checked_add((*byte - b'0') as u64))
                    .ok_or(FormatError::InvalidArchive("tar octal field overflow"))?;
            }
            _ => return Err(FormatError::InvalidArchive("malformed tar octal field")),
        }
    }
    Ok(value)
}

fn parse_decimal(field: &[u8]) -> Result<usize, FormatError> {
    let mut value = 0usize;
    if field.is_empty() {
        return Err(FormatError::InvalidArchive("malformed decimal field"));
    }
    for byte in field {
        if !byte.is_ascii_digit() {
            return Err(FormatError::InvalidArchive("malformed decimal field"));
        }
        value = value
            .checked_mul(10)
            .and_then(|acc| acc.checked_add((byte - b'0') as usize))
            .ok_or(FormatError::InvalidArchive("decimal field overflow"))?;
    }
    Ok(value)
}

fn nul_trimmed(bytes: &[u8]) -> &[u8] {
    let end = bytes
        .iter()
        .position(|byte| *byte == 0)
        .unwrap_or(bytes.len());
    &bytes[..end]
}

fn padding_to_512(len: usize) -> usize {
    let remainder = len % TAR_BLOCK_LEN;
    if remainder == 0 {
        0
    } else {
        TAR_BLOCK_LEN - remainder
    }
}

fn padding_to_512_u64(len: u64) -> u64 {
    let remainder = len % TAR_BLOCK_LEN as u64;
    if remainder == 0 {
        0
    } else {
        TAR_BLOCK_LEN as u64 - remainder
    }
}

fn slice(bytes: &[u8], offset: usize, len: usize) -> Result<&[u8], FormatError> {
    let end = checked_add(offset, len)?;
    bytes.get(offset..end).ok_or(FormatError::InvalidLength {
        structure: "tar member",
        expected: end,
        actual: bytes.len(),
    })
}

fn checked_add(lhs: usize, rhs: usize) -> Result<usize, FormatError> {
    lhs.checked_add(rhs).ok_or(FormatError::InvalidArchive(
        "tar member arithmetic overflow",
    ))
}

fn to_usize(value: u64) -> Result<usize, FormatError> {
    usize::try_from(value).map_err(|_| FormatError::InvalidArchive("tar member size overflow"))
}

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

    fn header(path: &[u8], kind: u8, size: usize, link: &[u8]) -> [u8; TAR_BLOCK_LEN] {
        let mut header = [0u8; TAR_BLOCK_LEN];
        header[..path.len()].copy_from_slice(path);
        write_octal(&mut header[100..108], 0o644);
        write_octal(&mut header[108..116], 0);
        write_octal(&mut header[116..124], 0);
        write_octal(&mut header[124..136], size as u64);
        write_octal(&mut header[136..148], 0);
        header[148..156].fill(b' ');
        header[156] = kind;
        header[157..157 + link.len()].copy_from_slice(link);
        header[257..263].copy_from_slice(b"ustar\0");
        header[263..265].copy_from_slice(b"00");
        let checksum = header.iter().map(|byte| *byte as u64).sum::<u64>();
        write_checksum(&mut header[148..156], checksum);
        header
    }

    fn member(path: &[u8], kind: u8, data: &[u8], link: &[u8]) -> Vec<u8> {
        member_with_declared_size(path, kind, data.len(), data, link)
    }

    fn member_with_declared_size(
        path: &[u8],
        kind: u8,
        declared_size: usize,
        data: &[u8],
        link: &[u8],
    ) -> Vec<u8> {
        let mut out = Vec::new();
        out.extend_from_slice(&header(path, kind, declared_size, link));
        out.extend_from_slice(data);
        out.resize(out.len() + padding_to_512(data.len()), 0);
        out
    }

    fn member_with_prefix(prefix: &[u8], path: &[u8], kind: u8, data: &[u8]) -> Vec<u8> {
        let mut header = header(path, kind, data.len(), b"");
        header[345..345 + prefix.len()].copy_from_slice(prefix);
        header[148..156].fill(b' ');
        let checksum = header.iter().map(|byte| *byte as u64).sum::<u64>();
        write_checksum(&mut header[148..156], checksum);

        let mut out = Vec::new();
        out.extend_from_slice(&header);
        out.extend_from_slice(data);
        out.resize(out.len() + padding_to_512(data.len()), 0);
        out
    }

    fn pax_record(key: &str, value: &[u8]) -> Vec<u8> {
        let mut len = key.len() + value.len() + 4;
        loop {
            let candidate = len.to_string().len() + 1 + key.len() + 1 + value.len() + 1;
            if candidate == len {
                break;
            }
            len = candidate;
        }
        let mut out = Vec::new();
        out.extend_from_slice(len.to_string().as_bytes());
        out.push(b' ');
        out.extend_from_slice(key.as_bytes());
        out.push(b'=');
        out.extend_from_slice(value);
        out.push(b'\n');
        out
    }

    fn write_octal(field: &mut [u8], value: u64) {
        let digits = format!("{value:o}");
        field.fill(0);
        let start = field.len() - 1 - digits.len();
        field[..start].fill(b'0');
        field[start..start + digits.len()].copy_from_slice(digits.as_bytes());
    }

    fn write_checksum(field: &mut [u8], value: u64) {
        let digits = format!("{value:06o}");
        field[0..6].copy_from_slice(digits.as_bytes());
        field[6] = 0;
        field[7] = b' ';
    }

    #[test]
    fn parses_ustar_regular_member() {
        let bytes = member(b"dir/file.txt", b'0', b"hello", b"");
        let parsed = parse_tar_member_group(&bytes, 4096).unwrap();

        assert_eq!(parsed.kind, TarEntryKind::Regular);
        assert_eq!(parsed.path, b"dir/file.txt");
        assert_eq!(parsed.data, b"hello");
        assert_eq!(parsed.logical_size, 5);
    }

    #[test]
    fn canonicalizes_one_directory_trailing_slash_only_for_directories() {
        let dir = member(b"dir/", b'5', b"", b"");
        assert_eq!(parse_tar_member_group(&dir, 4096).unwrap().path, b"dir");

        let file = member(b"dir/", b'0', b"", b"");
        assert_eq!(
            parse_tar_member_group(&file, 4096).unwrap_err(),
            FormatError::UnsafeArchivePath
        );
    }

    #[test]
    fn rejects_global_pax_headers() {
        let bytes = member(b"pax", b'g', b"11 path=x\n", b"");
        assert_eq!(
            parse_tar_member_group(&bytes, 4096).unwrap_err(),
            FormatError::InvalidArchive("global PAX headers are not allowed")
        );
    }

    #[test]
    fn rejects_global_pax_before_main_entry() {
        let global_pax = pax_record("path", b"poisoned.txt");
        let mut bytes = member(b"GlobalHead/path", b'g', &global_pax, b"");
        bytes.extend_from_slice(&member(b"safe.txt", b'0', b"abc", b""));

        assert_eq!(
            parse_tar_member_group(&bytes, 4096).unwrap_err(),
            FormatError::InvalidArchive("global PAX headers are not allowed")
        );
    }

    #[test]
    fn rejects_global_gnu_headers() {
        for typeflag in [b'V', b'M', b'N'] {
            let bytes = member(b"global", typeflag, b"archive-label", b"");

            assert_eq!(
                parse_tar_member_group(&bytes, 4096).unwrap_err(),
                FormatError::InvalidArchive("global GNU headers are not allowed"),
                "typeflag {typeflag:?}"
            );
        }
    }

    #[test]
    fn rejects_unsupported_gnu_sparse_entry_type() {
        let bytes = member(b"sparse.bin", b'S', b"", b"");

        assert_eq!(
            parse_tar_member_group(&bytes, 4096).unwrap_err(),
            FormatError::ReaderUnsupported("unsupported GNU sparse tar entry")
        );
    }

    #[test]
    fn applies_local_pax_path_and_size() {
        let pax = pax_record("path", b"long/name.txt");
        let mut bytes = member(b"PaxHeaders/name", b'x', &pax, b"");
        bytes.extend_from_slice(&member(b"short", b'0', b"abc", b""));

        let parsed = parse_tar_member_group(&bytes, 4096).unwrap();
        assert_eq!(parsed.path, b"long/name.txt");
        assert_eq!(parsed.data, b"abc");
    }

    #[test]
    fn applies_local_gnu_long_name_and_link_to_following_entry() {
        let mut named = member(b"././@LongLink", b'L', b"long/path.txt\0", b"");
        named.extend_from_slice(&member(b"short", b'0', b"abc", b""));
        let parsed = parse_tar_member_group(&named, 4096).unwrap();
        assert_eq!(parsed.path, b"long/path.txt");
        assert_eq!(parsed.data, b"abc");

        let mut linked = member(b"././@LongLink", b'K', b"target/file.txt\0", b"");
        linked.extend_from_slice(&member(b"short-link", b'2', b"", b"fallback"));
        let parsed = parse_tar_member_group(&linked, 4096).unwrap();
        assert_eq!(parsed.path, b"short-link");
        assert_eq!(
            parsed.link_target.as_deref(),
            Some(b"target/file.txt".as_slice())
        );
    }

    #[test]
    fn supported_tar_metadata_profile_matrix_matches_buffered_and_streaming_parsers() {
        struct Case {
            name: &'static str,
            bytes: Vec<u8>,
            expected_path: &'static [u8],
            expected_kind: TarEntryKind,
            expected_data: &'static [u8],
            expected_link_target: Option<&'static [u8]>,
            expected_logical_size: u64,
        }

        let mut pax_path_and_size = member(
            b"PaxHeaders/pax-file",
            b'x',
            &[
                pax_record("path", b"deep/pax-name.txt"),
                pax_record("size", b"5"),
            ]
            .concat(),
            b"",
        );
        pax_path_and_size.extend_from_slice(&member_with_declared_size(
            b"fallback",
            b'0',
            0,
            b"hello",
            b"",
        ));

        let mut pax_linkpath = member(
            b"PaxHeaders/pax-link",
            b'x',
            &pax_record("linkpath", b"target/file.txt"),
            b"",
        );
        pax_linkpath.extend_from_slice(&member(b"links/link", b'2', b"", b"fallback-target"));

        let mut gnu_long_name = member(b"././@LongLink", b'L', b"long/path/name.txt\0", b"");
        gnu_long_name.extend_from_slice(&member(b"short", b'0', b"abc", b""));

        let mut gnu_long_link = member(b"././@LongLink", b'K', b"target/hard.txt\0", b"");
        gnu_long_link.extend_from_slice(&member(b"links/hard", b'1', b"", b"fallback"));

        let cases = vec![
            Case {
                name: "regular ustar member",
                bytes: member(b"dir/file.txt", b'0', b"hello", b""),
                expected_path: b"dir/file.txt",
                expected_kind: TarEntryKind::Regular,
                expected_data: b"hello",
                expected_link_target: None,
                expected_logical_size: 5,
            },
            Case {
                name: "ustar prefix plus name",
                bytes: member_with_prefix(b"dir/prefix", b"file.txt", b'0', b"abc"),
                expected_path: b"dir/prefix/file.txt",
                expected_kind: TarEntryKind::Regular,
                expected_data: b"abc",
                expected_link_target: None,
                expected_logical_size: 3,
            },
            Case {
                name: "directory trailing slash",
                bytes: member(b"dir/", b'5', b"", b""),
                expected_path: b"dir",
                expected_kind: TarEntryKind::Directory,
                expected_data: b"",
                expected_link_target: None,
                expected_logical_size: 0,
            },
            Case {
                name: "local pax path and size",
                bytes: pax_path_and_size,
                expected_path: b"deep/pax-name.txt",
                expected_kind: TarEntryKind::Regular,
                expected_data: b"hello",
                expected_link_target: None,
                expected_logical_size: 5,
            },
            Case {
                name: "local pax linkpath",
                bytes: pax_linkpath,
                expected_path: b"links/link",
                expected_kind: TarEntryKind::Symlink,
                expected_data: b"",
                expected_link_target: Some(b"target/file.txt"),
                expected_logical_size: 0,
            },
            Case {
                name: "gnu long name",
                bytes: gnu_long_name,
                expected_path: b"long/path/name.txt",
                expected_kind: TarEntryKind::Regular,
                expected_data: b"abc",
                expected_link_target: None,
                expected_logical_size: 3,
            },
            Case {
                name: "gnu long link",
                bytes: gnu_long_link,
                expected_path: b"links/hard",
                expected_kind: TarEntryKind::Hardlink,
                expected_data: b"",
                expected_link_target: Some(b"target/hard.txt"),
                expected_logical_size: 0,
            },
        ];

        for case in cases {
            let parsed = parse_tar_member_group(&case.bytes, 4096).unwrap_or_else(|err| {
                panic!("{} should parse in buffered tar parser: {err:?}", case.name)
            });
            assert_eq!(parsed.path, case.expected_path, "{}", case.name);
            assert_eq!(parsed.kind, case.expected_kind, "{}", case.name);
            assert_eq!(parsed.data, case.expected_data, "{}", case.name);
            assert_eq!(
                parsed.link_target.as_deref(),
                case.expected_link_target,
                "{}",
                case.name
            );
            assert_eq!(
                parsed.logical_size, case.expected_logical_size,
                "{}",
                case.name
            );

            let mut streaming = TarStreamSummaryValidator::with_observer(
                4096,
                u64::MAX,
                4096,
                16,
                NoopTarStreamObserver,
            );
            streaming.observe(&case.bytes).unwrap_or_else(|err| {
                panic!(
                    "{} should parse in streaming tar parser: {err:?}",
                    case.name
                )
            });
            let summary = streaming.finish().unwrap_or_else(|err| {
                panic!(
                    "{} should finish in streaming tar parser: {err:?}",
                    case.name
                )
            });
            assert_eq!(summary.members.len(), 1, "{}", case.name);
            let member = &summary.members[0];
            assert_eq!(member.path, case.expected_path, "{}", case.name);
            assert_eq!(member.kind, case.expected_kind, "{}", case.name);
            assert_eq!(
                member.link_target.as_deref(),
                case.expected_link_target,
                "{}",
                case.name
            );
            assert_eq!(
                member.logical_size, case.expected_logical_size,
                "{}",
                case.name
            );
        }
    }

    #[test]
    fn tar_metadata_rejects_unsafe_or_inconsistent_overrides_matrix() {
        let mut pax_absolute_path = member(
            b"PaxHeaders/file",
            b'x',
            &pax_record("path", b"/absolute"),
            b"",
        );
        pax_absolute_path.extend_from_slice(&member(b"fallback", b'0', b"abc", b""));

        let mut pax_parent_path = member(
            b"PaxHeaders/file",
            b'x',
            &pax_record("path", b"../escape"),
            b"",
        );
        pax_parent_path.extend_from_slice(&member(b"fallback", b'0', b"abc", b""));

        let mut pax_absolute_link = member(
            b"PaxHeaders/link",
            b'x',
            &pax_record("linkpath", b"/target"),
            b"",
        );
        pax_absolute_link.extend_from_slice(&member(b"links/link", b'2', b"", b"safe"));

        let mut gnu_unsafe_name = member(b"././@LongLink", b'L', b"bad:name.txt\0", b"");
        gnu_unsafe_name.extend_from_slice(&member(b"fallback", b'0', b"abc", b""));

        let mut gnu_parent_hardlink = member(b"././@LongLink", b'K', b"../target.txt\0", b"");
        gnu_parent_hardlink.extend_from_slice(&member(b"links/hard", b'1', b"", b"safe"));

        let mut pax_size_on_directory =
            member(b"PaxHeaders/dir", b'x', &pax_record("size", b"1"), b"");
        pax_size_on_directory
            .extend_from_slice(&member_with_declared_size(b"dir", b'5', 0, b"x", b""));

        for (name, bytes, expected) in [
            (
                "pax absolute path",
                pax_absolute_path,
                FormatError::UnsafeArchivePath,
            ),
            (
                "pax parent path",
                pax_parent_path,
                FormatError::UnsafeArchivePath,
            ),
            (
                "pax absolute symlink target",
                pax_absolute_link,
                FormatError::UnsafeArchivePath,
            ),
            (
                "gnu unsafe long name",
                gnu_unsafe_name,
                FormatError::UnsafeArchivePath,
            ),
            (
                "gnu hardlink parent target",
                gnu_parent_hardlink,
                FormatError::UnsafeArchivePath,
            ),
            (
                "pax size on directory",
                pax_size_on_directory,
                FormatError::InvalidArchive("non-regular tar entry has non-zero payload size"),
            ),
        ] {
            assert_eq!(
                parse_tar_member_group(&bytes, 4096).unwrap_err(),
                expected,
                "{name}"
            );

            let mut streaming = TarStreamSummaryValidator::with_observer(
                4096,
                u64::MAX,
                4096,
                16,
                NoopTarStreamObserver,
            );
            assert_eq!(streaming.observe(&bytes).unwrap_err(), expected, "{name}");
        }
    }

    #[test]
    fn pax_size_exceeding_available_group_is_rejected_by_buffered_and_streaming_parsers() {
        let mut bytes = member(b"PaxHeaders/file", b'x', &pax_record("size", b"4096"), b"");
        bytes.extend_from_slice(&member_with_declared_size(b"file", b'0', 0, b"short", b""));

        assert_eq!(
            parse_tar_member_group(&bytes, 4096).unwrap_err(),
            FormatError::InvalidLength {
                structure: "tar member",
                expected: 5632,
                actual: 2048,
            }
        );

        let mut streaming = TarStreamSummaryValidator::with_observer(
            4096,
            u64::MAX,
            4096,
            16,
            NoopTarStreamObserver,
        );
        streaming.observe(&bytes).unwrap();
        assert_eq!(
            streaming.finish().unwrap_err(),
            FormatError::InvalidArchive("tar stream ended inside member group")
        );
    }

    #[test]
    fn malformed_pax_record_matrix_rejects_before_metadata_is_trusted() {
        let cases: Vec<(&str, Vec<u8>)> = vec![
            ("missing length", b"path=file\n".to_vec()),
            ("missing space", b"12path=file\n".to_vec()),
            ("record too short", b"3 a\n".to_vec()),
            ("missing newline", b"11 path=file".to_vec()),
            ("missing equals", b"10 pathfile\n".to_vec()),
            ("non utf8 key", vec![7, b' ', 0xff, b'=', b'x', b'\n']),
            ("bad size value", pax_record("size", b"12x")),
        ];

        for (name, payload) in cases {
            let mut bytes = member(b"PaxHeaders/file", b'x', &payload, b"");
            bytes.extend_from_slice(&member(b"file", b'0', b"abc", b""));

            assert!(
                matches!(
                    parse_tar_member_group(&bytes, 4096).unwrap_err(),
                    FormatError::InvalidArchive(_)
                ),
                "{name}"
            );

            let mut streaming = TarStreamSummaryValidator::with_observer(
                4096,
                u64::MAX,
                4096,
                16,
                NoopTarStreamObserver,
            );
            assert!(
                matches!(
                    streaming.observe(&bytes).unwrap_err(),
                    FormatError::InvalidArchive(_)
                ),
                "{name}"
            );
        }
    }

    #[test]
    fn reports_degraded_diagnostics_for_xattr_and_acl_pax_profiles() {
        let mut pax = Vec::new();
        pax.extend_from_slice(&pax_record("SCHILY.xattr.user.comment", b"hello"));
        pax.extend_from_slice(&pax_record("LIBARCHIVE.xattr.user.comment", b"hello"));
        pax.extend_from_slice(&pax_record("SCHILY.acl.access", b"user::rw-"));
        pax.extend_from_slice(&pax_record("LIBARCHIVE.acl.access", b"user::rw-"));
        let mut bytes = member(b"PaxHeaders/file", b'x', &pax, b"");
        bytes.extend_from_slice(&member(b"file.txt", b'0', b"abc", b""));

        let parsed = parse_tar_member_group(&bytes, 4096).unwrap();

        assert_eq!(parsed.path, b"file.txt");
        assert_eq!(parsed.data, b"abc");
        assert_eq!(
            parsed
                .diagnostics
                .iter()
                .filter(|diagnostic| **diagnostic
                    == MetadataDiagnostic {
                        profile: "pax-xattrs-acls",
                        message: "unsupported xattr/ACL PAX metadata was ignored",
                    })
                .count(),
            4
        );
    }

    #[test]
    fn reports_degraded_diagnostics_for_pax_timestamp_precision() {
        let mut pax = Vec::new();
        pax.extend_from_slice(&pax_record("atime", b"1.123456789"));
        pax.extend_from_slice(&pax_record("ctime", b"2.123456789"));
        pax.extend_from_slice(&pax_record("mtime", b"3.123456789"));
        let mut bytes = member(b"PaxHeaders/file", b'x', &pax, b"");
        bytes.extend_from_slice(&member(b"file.txt", b'0', b"abc", b""));

        let parsed = parse_tar_member_group(&bytes, 4096).unwrap();

        assert_eq!(
            parsed
                .diagnostics
                .iter()
                .filter(|diagnostic| **diagnostic
                    == MetadataDiagnostic {
                        profile: "pax-posix-2001",
                        message: "unsupported PAX timestamp metadata was ignored",
                    })
                .count(),
            3
        );
    }

    #[test]
    fn reports_degraded_diagnostics_for_sparse_and_unknown_pax_profiles() {
        let mut pax = Vec::new();
        pax.extend_from_slice(&pax_record("GNU.sparse.realsize", b"1024"));
        pax.extend_from_slice(&pax_record("GNU.sparse.map", b"0,1"));
        pax.extend_from_slice(&pax_record("comment", b"ignored"));
        let mut bytes = member(b"PaxHeaders/file", b'x', &pax, b"");
        bytes.extend_from_slice(&member(b"file.txt", b'0', b"abc", b""));

        let parsed = parse_tar_member_group(&bytes, 4096).unwrap();

        assert_eq!(
            parsed
                .diagnostics
                .iter()
                .filter(|diagnostic| **diagnostic
                    == MetadataDiagnostic {
                        profile: "gnu-sparse",
                        message: "unsupported sparse-file PAX metadata was ignored",
                    })
                .count(),
            2
        );
        assert_eq!(
            parsed
                .diagnostics
                .iter()
                .filter(|diagnostic| **diagnostic
                    == MetadataDiagnostic {
                        profile: "pax-posix-2001",
                        message: "unsupported PAX key was ignored",
                    })
                .count(),
            1
        );
    }

    #[test]
    fn reports_degraded_diagnostics_for_unsupported_local_pax_profiles() {
        let mut pax = Vec::new();
        pax.extend_from_slice(&pax_record("SCHILY.xattr.user.comment", b"hello"));
        pax.extend_from_slice(&pax_record("GNU.sparse.realsize", b"1024"));
        pax.extend_from_slice(&pax_record("mtime", b"1.123456789"));
        pax.extend_from_slice(&pax_record("comment", b"ignored"));
        let mut bytes = member(b"PaxHeaders/file", b'x', &pax, b"");
        bytes.extend_from_slice(&member(b"file.txt", b'0', b"abc", b""));

        let parsed = parse_tar_member_group(&bytes, 4096).unwrap();

        assert_eq!(parsed.path, b"file.txt");
        assert_eq!(parsed.data, b"abc");
        assert!(parsed.diagnostics.contains(&MetadataDiagnostic {
            profile: "pax-xattrs-acls",
            message: "unsupported xattr/ACL PAX metadata was ignored",
        }));
        assert!(parsed.diagnostics.contains(&MetadataDiagnostic {
            profile: "gnu-sparse",
            message: "unsupported sparse-file PAX metadata was ignored",
        }));
        assert!(parsed.diagnostics.contains(&MetadataDiagnostic {
            profile: "pax-posix-2001",
            message: "unsupported PAX timestamp metadata was ignored",
        }));
        assert!(parsed.diagnostics.contains(&MetadataDiagnostic {
            profile: "pax-posix-2001",
            message: "unsupported PAX key was ignored",
        }));
    }

    #[test]
    fn rejects_platform_escape_paths() {
        for path in [
            b"/abs".as_slice(),
            b"../up".as_slice(),
            b"a//b".as_slice(),
            b"a\\b".as_slice(),
            b"a:b".as_slice(),
            b"CON".as_slice(),
        ] {
            let bytes = member(path, b'0', b"", b"");
            assert_eq!(
                parse_tar_member_group(&bytes, 4096).unwrap_err(),
                FormatError::UnsafeArchivePath
            );
        }
    }

    #[cfg(unix)]
    #[test]
    fn safe_restore_rejects_symlink_parent() {
        let tmp = tempdir().unwrap();
        let outside = tempdir().unwrap();
        std::os::unix::fs::symlink(outside.path(), tmp.path().join("link")).unwrap();

        let member = OwnedTarMember {
            path: b"link/file.txt".to_vec(),
            kind: TarEntryKind::Regular,
            data: b"blocked".to_vec(),
            link_target: None,
            mode: 0o644,
            mtime: 0,
            logical_size: 7,
            diagnostics: Vec::new(),
        };

        assert_eq!(
            restore_tar_member(tmp.path(), &member, SafeExtractionOptions::default()).unwrap_err(),
            FormatError::UnsafeArchivePath
        );
    }

    #[test]
    fn safe_restore_requires_hardlink_target_to_be_existing_regular_file() {
        let tmp = tempdir().unwrap();
        fs::write(tmp.path().join("target.txt"), b"target").unwrap();
        let member = OwnedTarMember {
            path: b"linked.txt".to_vec(),
            kind: TarEntryKind::Hardlink,
            data: Vec::new(),
            link_target: Some(b"target.txt".to_vec()),
            mode: 0o644,
            mtime: 0,
            logical_size: 0,
            diagnostics: Vec::new(),
        };

        restore_tar_member(tmp.path(), &member, SafeExtractionOptions::default()).unwrap();
        assert_eq!(fs::read(tmp.path().join("linked.txt")).unwrap(), b"target");
    }

    #[cfg(unix)]
    #[test]
    fn restore_applies_regular_file_mode_metadata() {
        let tmp = tempdir().unwrap();
        let member = OwnedTarMember {
            path: b"script.sh".to_vec(),
            kind: TarEntryKind::Regular,
            data: b"#!/bin/sh\n".to_vec(),
            link_target: None,
            mode: 0o755,
            mtime: 0,
            logical_size: 10,
            diagnostics: Vec::new(),
        };

        let diagnostics =
            restore_tar_member(tmp.path(), &member, SafeExtractionOptions::default()).unwrap();

        assert!(diagnostics.is_empty());
        let mode = fs::metadata(tmp.path().join("script.sh"))
            .unwrap()
            .permissions()
            .mode()
            & 0o777;
        assert_eq!(mode, 0o755);
    }

    #[test]
    fn restore_applies_regular_file_mtime_metadata() {
        let tmp = tempdir().unwrap();
        let member = OwnedTarMember {
            path: b"dated.txt".to_vec(),
            kind: TarEntryKind::Regular,
            data: b"dated".to_vec(),
            link_target: None,
            mode: 0o666,
            mtime: 1_700_000_000,
            logical_size: 5,
            diagnostics: Vec::new(),
        };

        let diagnostics =
            restore_tar_member(tmp.path(), &member, SafeExtractionOptions::default()).unwrap();

        assert!(diagnostics.is_empty());
        let modified = fs::metadata(tmp.path().join("dated.txt"))
            .unwrap()
            .modified()
            .unwrap()
            .duration_since(SystemTime::UNIX_EPOCH)
            .unwrap()
            .as_secs();
        assert_eq!(modified, 1_700_000_000);
    }

    #[test]
    fn restore_revalidates_symlink_targets_from_owned_members() {
        let tmp = tempdir().unwrap();
        let member = OwnedTarMember {
            path: b"link".to_vec(),
            kind: TarEntryKind::Symlink,
            data: Vec::new(),
            link_target: Some(b"/outside".to_vec()),
            mode: 0o644,
            mtime: 0,
            logical_size: 0,
            diagnostics: Vec::new(),
        };

        assert_eq!(
            restore_tar_member(tmp.path(), &member, SafeExtractionOptions::default()).unwrap_err(),
            FormatError::UnsafeArchivePath
        );
        assert!(!tmp.path().join("link").exists());
    }

    #[test]
    fn safe_restore_rejects_directory_over_existing_file_even_with_overwrite() {
        let tmp = tempdir().unwrap();
        let conflict = tmp.path().join("conflict");
        fs::write(&conflict, b"not a directory").unwrap();
        let member = OwnedTarMember {
            path: b"conflict".to_vec(),
            kind: TarEntryKind::Directory,
            data: Vec::new(),
            link_target: None,
            mode: 0o644,
            mtime: 0,
            logical_size: 0,
            diagnostics: Vec::new(),
        };

        assert_eq!(
            restore_tar_member(
                tmp.path(),
                &member,
                SafeExtractionOptions {
                    overwrite_existing: true
                }
            )
            .unwrap_err(),
            FormatError::UnsafeOverwrite
        );
        assert!(conflict.is_file());
    }

    #[test]
    fn hardlink_target_checks_use_component_position_not_value() {
        let tmp = tempdir().unwrap();
        fs::create_dir(tmp.path().join("a")).unwrap();
        fs::write(tmp.path().join("a").join("a"), b"target").unwrap();
        let member = OwnedTarMember {
            path: b"linked.txt".to_vec(),
            kind: TarEntryKind::Hardlink,
            data: Vec::new(),
            link_target: Some(b"a/a".to_vec()),
            mode: 0o644,
            mtime: 0,
            logical_size: 0,
            diagnostics: Vec::new(),
        };

        restore_tar_member(tmp.path(), &member, SafeExtractionOptions::default()).unwrap();
        assert_eq!(fs::read(tmp.path().join("linked.txt")).unwrap(), b"target");
    }

    #[test]
    fn hardlink_targets_obey_max_path_length() {
        let bytes = member(b"link", b'1', b"", b"long/name");

        assert_eq!(
            parse_tar_member_group(&bytes, 4).unwrap_err(),
            FormatError::UnsafeArchivePath
        );
    }
}
