use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

pub(crate) type FdNumber = i32;
pub(crate) type ProcessId = u64;
pub(crate) type ModelThreadId = u64;
type OpenFileId = u64;

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) enum ShadowKind {
    HostSameFd,
    LocalOnly,
    HostPassthrough,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct ShadowObject {
    pub(crate) kind: ShadowKind,
    /// Model id for the supervisor-owned host fd/memfd/socket handle.
    pub(crate) supervisor_fd: i32,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) enum ShadowBacking {
    /// A live-only handle without enough information to rebuild it after a full
    /// process restart. Useful for early proof-model tests, but production
    /// runtime code should prefer a reconstructable variant.
    Ephemeral,
    /// A shadow that can be rebuilt by reopening/copying a guest-rootfs path.
    GuestPath { path: String, writable: bool },
    /// A live host path that can be reopened directly for kboxlike process
    /// restore. This is distinct from GuestPath because it is not rooted under
    /// a baked guest filesystem.
    HostPath { path: String, writable: bool },
    /// A host-passthrough handle that must be recreated by backend-specific
    /// host state, e.g. socketpair/eventfd/signalfd/netlink setup.
    HostPassthrough { label: String },
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct DescriptorInfo {
    pub(crate) fd: FdNumber,
    pub(crate) lkl_fd: Option<i64>,
    pub(crate) shadow: Option<ShadowObject>,
    pub(crate) cloexec: bool,
    pub(crate) open_file: u64,
    pub(crate) ref_count: usize,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum FdOperation {
    Read,
    Write,
    Readv,
    Writev,
    Preadv,
    Pwritev,
    Pwritev2,
    Mmap,
    FcntlLock,
    Generic,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum FdRoute {
    Lkl { fd: i64 },
    HostShadow { shadow: ShadowObject },
    HostPassthrough { shadow: ShadowObject },
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ShadowInstallPlan {
    pub(crate) source_fd: FdNumber,
    pub(crate) target_fd: FdNumber,
    pub(crate) shadow: ShadowObject,
    pub(crate) cloexec: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct ShadowInstallPlanWithBacking {
    pub(crate) install: ShadowInstallPlan,
    pub(crate) backing: Option<ShadowBacking>,
}

pub(crate) trait ShadowRebuilder {
    fn rebuild_shadow(
        &mut self,
        kind: ShadowKind,
        backing: &ShadowBacking,
    ) -> Result<ShadowObject, FdError>;
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Descriptor {
    open_file: OpenFileId,
    cloexec: bool,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
struct OpenFileDescription {
    lkl_fd: Option<i64>,
    shadow: Option<ShadowObject>,
    shadow_backing: Option<ShadowBacking>,
    #[serde(default)]
    socket_shutdown: Option<KboxlikeSocketShutdownState>,
    ref_count: usize,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct KboxlikeSocketShutdownState {
    pub(crate) read_closed: bool,
    pub(crate) write_closed: bool,
}

impl KboxlikeSocketShutdownState {
    pub(crate) fn from_shutdown_how(how: i32) -> Result<Self, FdError> {
        match how {
            libc::SHUT_RD => Ok(Self {
                read_closed: true,
                write_closed: false,
            }),
            libc::SHUT_WR => Ok(Self {
                read_closed: false,
                write_closed: true,
            }),
            libc::SHUT_RDWR => Ok(Self {
                read_closed: true,
                write_closed: true,
            }),
            _ => Err(FdError::TraceeInstallFailed("invalid socket shutdown mode")),
        }
    }

    pub(crate) fn merge(self, other: Self) -> Self {
        Self {
            read_closed: self.read_closed || other.read_closed,
            write_closed: self.write_closed || other.write_closed,
        }
    }

    pub(crate) fn is_empty(self) -> bool {
        !self.read_closed && !self.write_closed
    }
}

#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
struct ProcessFdTable {
    parent: Option<ProcessId>,
    current_exe: Option<String>,
    fds: BTreeMap<FdNumber, Descriptor>,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct KboxlikeFdSnapshot {
    next_pid: ProcessId,
    next_open_file: OpenFileId,
    processes: BTreeMap<ProcessId, ProcessFdTable>,
    open_files: BTreeMap<OpenFileId, OpenFileDescription>,
}

#[derive(Debug, PartialEq, Eq)]
pub(crate) enum FdError {
    BadProcess(ProcessId),
    BadFd(FdNumber),
    InvalidFd(FdNumber),
    NoAvailableFd,
    NoBackingFd(FdNumber),
    InvalidSnapshot(&'static str),
    ShadowRebuildFailed(&'static str),
    ShadowRebuildUnsupported(&'static str),
    TraceeInstallFailed(&'static str),
}

#[derive(Debug)]
pub(crate) struct KboxlikeFdSystem {
    next_pid: ProcessId,
    next_open_file: OpenFileId,
    processes: BTreeMap<ProcessId, ProcessFdTable>,
    open_files: BTreeMap<OpenFileId, OpenFileDescription>,
}

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

impl KboxlikeFdSystem {
    pub(crate) fn new() -> Self {
        Self {
            next_pid: 1,
            next_open_file: 1,
            processes: BTreeMap::new(),
            open_files: BTreeMap::new(),
        }
    }

    pub(crate) fn spawn_init(&mut self) -> ProcessId {
        self.spawn_init_inner(None)
    }

    pub(crate) fn spawn_init_with_exe(&mut self, exe: impl Into<String>) -> ProcessId {
        self.spawn_init_inner(Some(exe.into()))
    }

    fn spawn_init_inner(&mut self, current_exe: Option<String>) -> ProcessId {
        let pid = self.alloc_pid();
        self.processes.insert(
            pid,
            ProcessFdTable {
                current_exe,
                ..ProcessFdTable::default()
            },
        );
        pid
    }

    pub(crate) fn fork(&mut self, parent: ProcessId) -> Result<ProcessId, FdError> {
        let mut child_table = self
            .processes
            .get(&parent)
            .ok_or(FdError::BadProcess(parent))?
            .clone();
        for desc in child_table.fds.values() {
            self.open_file_mut(desc.open_file).ref_count += 1;
        }
        child_table.parent = Some(parent);
        let child = self.alloc_pid();
        self.processes.insert(child, child_table);
        Ok(child)
    }

    pub(crate) fn insert_shadow_fd(
        &mut self,
        pid: ProcessId,
        fd: FdNumber,
        lkl_fd: Option<i64>,
        shadow: ShadowObject,
        cloexec: bool,
    ) -> Result<(), FdError> {
        self.validate_fd(fd)?;
        self.replace_descriptor(
            pid,
            fd,
            lkl_fd,
            Some(shadow),
            Some(ShadowBacking::Ephemeral),
            cloexec,
        )
    }

    pub(crate) fn insert_shadow_fd_with_backing(
        &mut self,
        pid: ProcessId,
        fd: FdNumber,
        lkl_fd: Option<i64>,
        shadow: ShadowObject,
        backing: ShadowBacking,
        cloexec: bool,
    ) -> Result<(), FdError> {
        self.validate_fd(fd)?;
        self.replace_descriptor(pid, fd, lkl_fd, Some(shadow), Some(backing), cloexec)
    }

    pub(crate) fn insert_plain_fd(
        &mut self,
        pid: ProcessId,
        fd: FdNumber,
        lkl_fd: Option<i64>,
        cloexec: bool,
    ) -> Result<(), FdError> {
        self.validate_fd(fd)?;
        self.replace_descriptor(pid, fd, lkl_fd, None, None, cloexec)
    }

    pub(crate) fn dup(&mut self, pid: ProcessId, oldfd: FdNumber) -> Result<FdNumber, FdError> {
        self.dup_from_min(pid, oldfd, 0, false)
    }

    pub(crate) fn dup_cloexec(
        &mut self,
        pid: ProcessId,
        oldfd: FdNumber,
        minfd: FdNumber,
    ) -> Result<FdNumber, FdError> {
        self.dup_from_min(pid, oldfd, minfd, true)
    }

    pub(crate) fn dup_from_min(
        &mut self,
        pid: ProcessId,
        oldfd: FdNumber,
        minfd: FdNumber,
        cloexec: bool,
    ) -> Result<FdNumber, FdError> {
        let newfd = self.next_available_fd(pid, minfd)?;
        self.dup_to(pid, oldfd, newfd, cloexec)?;
        Ok(newfd)
    }

    pub(crate) fn dup_to(
        &mut self,
        pid: ProcessId,
        oldfd: FdNumber,
        newfd: FdNumber,
        cloexec: bool,
    ) -> Result<(), FdError> {
        self.validate_fd(oldfd)?;
        self.validate_fd(newfd)?;
        if oldfd == newfd {
            if self
                .processes
                .get(&pid)
                .ok_or(FdError::BadProcess(pid))?
                .fds
                .contains_key(&oldfd)
            {
                return Ok(());
            }
            return Err(FdError::BadFd(oldfd));
        }
        let old = self
            .processes
            .get(&pid)
            .ok_or(FdError::BadProcess(pid))?
            .fds
            .get(&oldfd)
            .cloned()
            .ok_or(FdError::BadFd(oldfd))?;
        self.close_if_present(pid, newfd)?;
        self.open_file_mut(old.open_file).ref_count += 1;
        self.process_mut(pid)?.fds.insert(
            newfd,
            Descriptor {
                open_file: old.open_file,
                cloexec,
            },
        );
        Ok(())
    }

    pub(crate) fn alias_fd_from(
        &mut self,
        source_pid: ProcessId,
        source_fd: FdNumber,
        target_pid: ProcessId,
        target_fd: FdNumber,
        cloexec: bool,
    ) -> Result<(), FdError> {
        self.validate_fd(source_fd)?;
        self.validate_fd(target_fd)?;
        if source_pid == target_pid && source_fd == target_fd {
            return self.set_cloexec(target_pid, target_fd, cloexec);
        }
        let source = self
            .processes
            .get(&source_pid)
            .ok_or(FdError::BadProcess(source_pid))?
            .fds
            .get(&source_fd)
            .cloned()
            .ok_or(FdError::BadFd(source_fd))?;
        self.close_if_present(target_pid, target_fd)?;
        self.open_file_mut(source.open_file).ref_count += 1;
        self.process_mut(target_pid)?.fds.insert(
            target_fd,
            Descriptor {
                open_file: source.open_file,
                cloexec,
            },
        );
        Ok(())
    }

    pub(crate) fn set_cloexec(
        &mut self,
        pid: ProcessId,
        fd: FdNumber,
        cloexec: bool,
    ) -> Result<(), FdError> {
        let desc = self
            .process_mut(pid)?
            .fds
            .get_mut(&fd)
            .ok_or(FdError::BadFd(fd))?;
        desc.cloexec = cloexec;
        Ok(())
    }

    pub(crate) fn close(&mut self, pid: ProcessId, fd: FdNumber) -> Result<(), FdError> {
        self.validate_fd(fd)?;
        let removed = self
            .process_mut(pid)?
            .fds
            .remove(&fd)
            .ok_or(FdError::BadFd(fd))?;
        self.release_open_file(removed.open_file);
        Ok(())
    }

    pub(crate) fn exec(&mut self, pid: ProcessId) -> Result<(), FdError> {
        self.exec_inner(pid, None)
    }

    pub(crate) fn exec_with_path(
        &mut self,
        pid: ProcessId,
        exe: impl Into<String>,
    ) -> Result<(), FdError> {
        self.exec_inner(pid, Some(exe.into()))
    }

    fn exec_inner(&mut self, pid: ProcessId, exe: Option<String>) -> Result<(), FdError> {
        let cloexec_fds: Vec<FdNumber> = self
            .processes
            .get(&pid)
            .ok_or(FdError::BadProcess(pid))?
            .fds
            .iter()
            .filter_map(|(fd, desc)| desc.cloexec.then_some(*fd))
            .collect();
        for fd in cloexec_fds {
            let removed = self
                .process_mut(pid)?
                .fds
                .remove(&fd)
                .expect("fd collected from same table");
            self.release_open_file(removed.open_file);
        }
        if let Some(exe) = exe {
            self.process_mut(pid)?.current_exe = Some(exe);
        }
        Ok(())
    }

    pub(crate) fn current_exe(&self, pid: ProcessId) -> Result<Option<&str>, FdError> {
        Ok(self
            .processes
            .get(&pid)
            .ok_or(FdError::BadProcess(pid))?
            .current_exe
            .as_deref())
    }

    pub(crate) fn parent(&self, pid: ProcessId) -> Result<Option<ProcessId>, FdError> {
        Ok(self
            .processes
            .get(&pid)
            .ok_or(FdError::BadProcess(pid))?
            .parent)
    }

    pub(crate) fn resolve_proc_exe(
        &self,
        caller: ProcessId,
        path: &str,
    ) -> Result<Option<String>, FdError> {
        let target = if path == "/proc/self/exe" || path == "/proc/thread-self/exe" {
            Some(caller)
        } else {
            path.strip_prefix("/proc/")
                .and_then(|rest| rest.strip_suffix("/exe"))
                .and_then(|pid| pid.parse::<ProcessId>().ok())
        };
        let Some(target) = target else {
            return Ok(None);
        };
        Ok(self
            .processes
            .get(&target)
            .ok_or(FdError::BadProcess(target))?
            .current_exe
            .clone())
    }

    pub(crate) fn exit_process(&mut self, pid: ProcessId) -> Result<(), FdError> {
        self.remove_process(pid)
    }

    pub(crate) fn kill_subtree(&mut self, root: ProcessId) -> Result<Vec<ProcessId>, FdError> {
        if !self.processes.contains_key(&root) {
            return Err(FdError::BadProcess(root));
        }
        let mut victims = Vec::new();
        self.collect_subtree(root, &mut victims);
        for pid in &victims {
            self.remove_process(*pid)?;
        }
        Ok(victims)
    }

    pub(crate) fn process_count(&self) -> usize {
        self.processes.len()
    }

    pub(crate) fn snapshot(&self) -> KboxlikeFdSnapshot {
        KboxlikeFdSnapshot {
            next_pid: self.next_pid,
            next_open_file: self.next_open_file,
            processes: self.processes.clone(),
            open_files: self.open_files.clone(),
        }
    }

    pub(crate) fn restore(snapshot: KboxlikeFdSnapshot) -> Result<Self, FdError> {
        validate_snapshot(&snapshot)?;
        Ok(Self {
            next_pid: snapshot.next_pid,
            next_open_file: snapshot.next_open_file,
            processes: snapshot.processes,
            open_files: snapshot.open_files,
        })
    }

    pub(crate) fn restore_rebuilding_shadows(
        mut snapshot: KboxlikeFdSnapshot,
        rebuilder: &mut impl ShadowRebuilder,
    ) -> Result<Self, FdError> {
        validate_snapshot(&snapshot)?;
        for open_file in snapshot.open_files.values_mut() {
            let Some(shadow) = open_file.shadow else {
                continue;
            };
            let backing = open_file
                .shadow_backing
                .as_ref()
                .expect("validated shadow backing");
            if matches!(backing, ShadowBacking::Ephemeral) {
                return Err(FdError::InvalidSnapshot(
                    "ephemeral shadow cannot be rebuilt after restore",
                ));
            }
            open_file.shadow = Some(rebuilder.rebuild_shadow(shadow.kind, backing)?);
        }
        Ok(Self {
            next_pid: snapshot.next_pid,
            next_open_file: snapshot.next_open_file,
            processes: snapshot.processes,
            open_files: snapshot.open_files,
        })
    }

    pub(crate) fn descriptor(
        &self,
        pid: ProcessId,
        fd: FdNumber,
    ) -> Result<DescriptorInfo, FdError> {
        let table = self.processes.get(&pid).ok_or(FdError::BadProcess(pid))?;
        let desc = table.fds.get(&fd).ok_or(FdError::BadFd(fd))?;
        let ofd = self
            .open_files
            .get(&desc.open_file)
            .expect("descriptor references live open file");
        Ok(DescriptorInfo {
            fd,
            lkl_fd: ofd.lkl_fd,
            shadow: ofd.shadow,
            cloexec: desc.cloexec,
            open_file: desc.open_file,
            ref_count: ofd.ref_count,
        })
    }

    pub(crate) fn shadow_backing(
        &self,
        pid: ProcessId,
        fd: FdNumber,
    ) -> Result<Option<&ShadowBacking>, FdError> {
        let table = self.processes.get(&pid).ok_or(FdError::BadProcess(pid))?;
        let desc = table.fds.get(&fd).ok_or(FdError::BadFd(fd))?;
        Ok(self
            .open_files
            .get(&desc.open_file)
            .expect("descriptor references live open file")
            .shadow_backing
            .as_ref())
    }

    pub(crate) fn set_socket_shutdown(
        &mut self,
        pid: ProcessId,
        fd: FdNumber,
        how: i32,
    ) -> Result<(), FdError> {
        self.validate_fd(fd)?;
        let shutdown = KboxlikeSocketShutdownState::from_shutdown_how(how)?;
        let open_file = self
            .processes
            .get(&pid)
            .ok_or(FdError::BadProcess(pid))?
            .fds
            .get(&fd)
            .cloned()
            .ok_or(FdError::BadFd(fd))?
            .open_file;
        let ofd = self.open_file_mut(open_file);
        ofd.socket_shutdown = Some(ofd.socket_shutdown.unwrap_or_default().merge(shutdown));
        Ok(())
    }

    pub(crate) fn socket_shutdown(
        &self,
        pid: ProcessId,
        fd: FdNumber,
    ) -> Result<Option<KboxlikeSocketShutdownState>, FdError> {
        self.validate_fd(fd)?;
        let table = self.processes.get(&pid).ok_or(FdError::BadProcess(pid))?;
        let desc = table.fds.get(&fd).ok_or(FdError::BadFd(fd))?;
        Ok(self
            .open_files
            .get(&desc.open_file)
            .expect("descriptor references live open file")
            .socket_shutdown)
    }

    pub(crate) fn route_fd_operation(
        &self,
        pid: ProcessId,
        fd: FdNumber,
        operation: FdOperation,
    ) -> Result<FdRoute, FdError> {
        let info = self.descriptor(pid, fd)?;
        match operation {
            // The host kernel performs mmap for ELF/shared-object text. Any
            // host-backed shadow must be injected/routed on the host side, or
            // the dynamic linker sees EBADF for /proc/self/fd/N.
            FdOperation::Mmap => self.route_host_if_shadow_else_lkl(&info),
            // These operations changed observable guest state in Chromium and
            // Postgres. If the fd is a same-fd shadow, routing them to LKL
            // desynchronizes the host memfd from the guest-visible file:
            // writev/pwritev* produced zero-byte Postgres config/WAL files, and
            // fcntl locks made Chromium's SQLite state look locked/corrupt.
            FdOperation::Write
            | FdOperation::Readv
            | FdOperation::Writev
            | FdOperation::Preadv
            | FdOperation::Pwritev
            | FdOperation::Pwritev2
            | FdOperation::FcntlLock => match info.shadow {
                Some(
                    shadow @ ShadowObject {
                        kind: ShadowKind::HostSameFd,
                        ..
                    },
                ) => Ok(FdRoute::HostShadow { shadow }),
                Some(
                    shadow @ ShadowObject {
                        kind: ShadowKind::HostPassthrough,
                        ..
                    },
                ) => Ok(FdRoute::HostPassthrough { shadow }),
                _ => self.route_lkl_or_shadow(&info),
            },
            // Plain reads and generic fd operations can stay on LKL unless the
            // fd is explicitly host-passthrough-only.
            FdOperation::Read | FdOperation::Generic => match info.shadow {
                Some(
                    shadow @ ShadowObject {
                        kind: ShadowKind::HostPassthrough,
                        ..
                    },
                ) => Ok(FdRoute::HostPassthrough { shadow }),
                _ => self.route_lkl_or_shadow(&info),
            },
        }
    }

    pub(crate) fn plan_shadow_install(
        &self,
        pid: ProcessId,
        source_fd: FdNumber,
        target_fd: FdNumber,
        cloexec: bool,
    ) -> Result<Option<ShadowInstallPlan>, FdError> {
        self.validate_fd(target_fd)?;
        let info = self.descriptor(pid, source_fd)?;
        Ok(info.shadow.map(|shadow| ShadowInstallPlan {
            source_fd,
            target_fd,
            shadow,
            cloexec,
        }))
    }

    pub(crate) fn process_shadow_install_plans(
        &self,
        pid: ProcessId,
    ) -> Result<Vec<ShadowInstallPlan>, FdError> {
        Ok(self
            .process_shadow_install_plans_with_backing(pid)?
            .into_iter()
            .map(|plan| plan.install)
            .collect())
    }

    pub(crate) fn process_shadow_install_plans_with_backing(
        &self,
        pid: ProcessId,
    ) -> Result<Vec<ShadowInstallPlanWithBacking>, FdError> {
        let table = self.processes.get(&pid).ok_or(FdError::BadProcess(pid))?;
        let mut plans = Vec::new();
        for (fd, desc) in &table.fds {
            let open_file = self
                .open_files
                .get(&desc.open_file)
                .expect("descriptor references live open file");
            if let Some(shadow) = open_file.shadow {
                plans.push(ShadowInstallPlanWithBacking {
                    install: ShadowInstallPlan {
                        source_fd: *fd,
                        target_fd: *fd,
                        shadow,
                        cloexec: desc.cloexec,
                    },
                    backing: open_file.shadow_backing.clone(),
                });
            }
        }
        Ok(plans)
    }

    pub(crate) fn plan_operation_shadow_install(
        &self,
        pid: ProcessId,
        fd: FdNumber,
        operation: FdOperation,
    ) -> Result<Option<ShadowInstallPlan>, FdError> {
        match self.route_fd_operation(pid, fd, operation)? {
            FdRoute::Lkl { .. } => Ok(None),
            FdRoute::HostShadow { shadow } | FdRoute::HostPassthrough { shadow } => {
                Ok(Some(ShadowInstallPlan {
                    source_fd: fd,
                    target_fd: fd,
                    shadow,
                    cloexec: false,
                }))
            }
        }
    }

    fn route_host_if_shadow_else_lkl(&self, info: &DescriptorInfo) -> Result<FdRoute, FdError> {
        match info.shadow {
            Some(
                shadow @ ShadowObject {
                    kind: ShadowKind::HostPassthrough,
                    ..
                },
            ) => Ok(FdRoute::HostPassthrough { shadow }),
            Some(shadow) => Ok(FdRoute::HostShadow { shadow }),
            None => self.route_lkl_or_shadow(info),
        }
    }

    fn route_lkl_or_shadow(&self, info: &DescriptorInfo) -> Result<FdRoute, FdError> {
        if let Some(fd) = info.lkl_fd {
            return Ok(FdRoute::Lkl { fd });
        }
        match info.shadow {
            Some(
                shadow @ ShadowObject {
                    kind: ShadowKind::HostPassthrough,
                    ..
                },
            ) => Ok(FdRoute::HostPassthrough { shadow }),
            Some(shadow) => Ok(FdRoute::HostShadow { shadow }),
            None => Err(FdError::NoBackingFd(info.fd)),
        }
    }

    fn alloc_pid(&mut self) -> ProcessId {
        let pid = self.next_pid;
        self.next_pid += 1;
        pid
    }

    fn alloc_open_file(
        &mut self,
        lkl_fd: Option<i64>,
        shadow: Option<ShadowObject>,
        shadow_backing: Option<ShadowBacking>,
    ) -> OpenFileId {
        let id = self.next_open_file;
        self.next_open_file += 1;
        self.open_files.insert(
            id,
            OpenFileDescription {
                lkl_fd,
                shadow,
                shadow_backing,
                socket_shutdown: None,
                ref_count: 1,
            },
        );
        id
    }

    fn replace_descriptor(
        &mut self,
        pid: ProcessId,
        fd: FdNumber,
        lkl_fd: Option<i64>,
        shadow: Option<ShadowObject>,
        shadow_backing: Option<ShadowBacking>,
        cloexec: bool,
    ) -> Result<(), FdError> {
        self.close_if_present(pid, fd)?;
        let open_file = self.alloc_open_file(lkl_fd, shadow, shadow_backing);
        self.process_mut(pid)?
            .fds
            .insert(fd, Descriptor { open_file, cloexec });
        Ok(())
    }

    fn close_if_present(&mut self, pid: ProcessId, fd: FdNumber) -> Result<(), FdError> {
        let removed = self.process_mut(pid)?.fds.remove(&fd);
        if let Some(desc) = removed {
            self.release_open_file(desc.open_file);
        }
        Ok(())
    }

    fn remove_process(&mut self, pid: ProcessId) -> Result<(), FdError> {
        let table = self
            .processes
            .remove(&pid)
            .ok_or(FdError::BadProcess(pid))?;
        for desc in table.fds.into_values() {
            self.release_open_file(desc.open_file);
        }
        Ok(())
    }

    fn collect_subtree(&self, root: ProcessId, out: &mut Vec<ProcessId>) {
        out.push(root);
        let children: Vec<ProcessId> = self
            .processes
            .iter()
            .filter_map(|(pid, table)| (table.parent == Some(root)).then_some(*pid))
            .collect();
        for child in children {
            self.collect_subtree(child, out);
        }
    }

    fn release_open_file(&mut self, open_file: OpenFileId) {
        let ofd = self.open_file_mut(open_file);
        ofd.ref_count -= 1;
        if ofd.ref_count == 0 {
            self.open_files.remove(&open_file);
        }
    }

    fn next_available_fd(&self, pid: ProcessId, minfd: FdNumber) -> Result<FdNumber, FdError> {
        self.validate_fd(minfd)?;
        let table = self.processes.get(&pid).ok_or(FdError::BadProcess(pid))?;
        for fd in minfd..=i32::MAX {
            if !table.fds.contains_key(&fd) {
                return Ok(fd);
            }
        }
        Err(FdError::NoAvailableFd)
    }

    fn process_mut(&mut self, pid: ProcessId) -> Result<&mut ProcessFdTable, FdError> {
        self.processes.get_mut(&pid).ok_or(FdError::BadProcess(pid))
    }

    fn open_file_mut(&mut self, id: OpenFileId) -> &mut OpenFileDescription {
        self.open_files
            .get_mut(&id)
            .expect("descriptor references live open file")
    }

    fn validate_fd(&self, fd: FdNumber) -> Result<(), FdError> {
        if fd < 0 {
            Err(FdError::InvalidFd(fd))
        } else {
            Ok(())
        }
    }
}

fn validate_snapshot(snapshot: &KboxlikeFdSnapshot) -> Result<(), FdError> {
    if snapshot.next_pid == 0 || snapshot.next_open_file == 0 {
        return Err(FdError::InvalidSnapshot("next ids must be non-zero"));
    }
    if snapshot
        .processes
        .keys()
        .any(|pid| *pid == 0 || *pid >= snapshot.next_pid)
    {
        return Err(FdError::InvalidSnapshot(
            "process id outside snapshot allocation range",
        ));
    }
    if snapshot
        .open_files
        .keys()
        .any(|id| *id == 0 || *id >= snapshot.next_open_file)
    {
        return Err(FdError::InvalidSnapshot(
            "open-file id outside snapshot allocation range",
        ));
    }

    let mut refs: BTreeMap<OpenFileId, usize> = BTreeMap::new();
    for table in snapshot.processes.values() {
        if let Some(parent) = table.parent {
            if !snapshot.processes.contains_key(&parent) {
                return Err(FdError::InvalidSnapshot(
                    "process parent references missing process",
                ));
            }
        }
        for (fd, desc) in &table.fds {
            if *fd < 0 {
                return Err(FdError::InvalidSnapshot("snapshot contains negative fd"));
            }
            if !snapshot.open_files.contains_key(&desc.open_file) {
                return Err(FdError::InvalidSnapshot(
                    "descriptor references missing open-file description",
                ));
            }
            *refs.entry(desc.open_file).or_insert(0) += 1;
        }
    }
    for (id, open_file) in &snapshot.open_files {
        if refs.get(id).copied().unwrap_or(0) != open_file.ref_count {
            return Err(FdError::InvalidSnapshot(
                "open-file ref_count does not match descriptor references",
            ));
        }
        match (&open_file.shadow, &open_file.shadow_backing) {
            (Some(_), Some(_)) => {}
            (Some(_), None) => {
                return Err(FdError::InvalidSnapshot(
                    "shadow open-file is missing reconstruction backing",
                ));
            }
            (None, Some(_)) => {
                return Err(FdError::InvalidSnapshot(
                    "non-shadow open-file has shadow reconstruction backing",
                ));
            }
            (None, None) => {}
        }
    }
    Ok(())
}

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

    fn shadow(supervisor_fd: i32) -> ShadowObject {
        ShadowObject {
            kind: ShadowKind::HostSameFd,
            supervisor_fd,
        }
    }

    fn local_shadow(supervisor_fd: i32) -> ShadowObject {
        ShadowObject {
            kind: ShadowKind::LocalOnly,
            supervisor_fd,
        }
    }

    fn host_passthrough(supervisor_fd: i32) -> ShadowObject {
        ShadowObject {
            kind: ShadowKind::HostPassthrough,
            supervisor_fd,
        }
    }

    fn guest_path(path: &str) -> ShadowBacking {
        ShadowBacking::GuestPath {
            path: path.to_string(),
            writable: false,
        }
    }

    #[derive(Default)]
    struct RecordingRebuilder {
        next_fd: i32,
        calls: Vec<(ShadowKind, ShadowBacking)>,
    }

    impl ShadowRebuilder for RecordingRebuilder {
        fn rebuild_shadow(
            &mut self,
            kind: ShadowKind,
            backing: &ShadowBacking,
        ) -> Result<ShadowObject, FdError> {
            let fd = if self.next_fd == 0 {
                9000
            } else {
                self.next_fd
            };
            self.next_fd = fd + 1;
            self.calls.push((kind, backing.clone()));
            Ok(ShadowObject {
                kind,
                supervisor_fd: fd,
            })
        }
    }

    #[test]
    fn raw_shadow_survives_chromium_style_dup_fork_close_dup2_exec() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init();
        for fd in (0..32).filter(|fd| *fd != 27 && *fd != 30) {
            fds.insert_plain_fd(browser, fd, None, false).unwrap();
        }
        fds.insert_shadow_fd(browser, 27, None, shadow(23), false)
            .unwrap();
        fds.insert_shadow_fd(browser, 30, None, shadow(30), false)
            .unwrap();
        fds.insert_shadow_fd(browser, 33033, Some(272), shadow(372), false)
            .unwrap();

        let network_pipe = fds.dup(browser, 27).unwrap();
        let v8_shared = fds.dup(browser, 30).unwrap();
        assert_eq!(network_pipe, 32);
        assert_eq!(v8_shared, 33);

        let helper = fds.fork(browser).unwrap();
        fds.close(browser, network_pipe).unwrap();
        fds.close(browser, v8_shared).unwrap();

        fds.dup_to(helper, network_pipe, 6, false).unwrap();
        fds.dup_to(helper, v8_shared, 10, false).unwrap();
        fds.dup_to(helper, 33033, 104, false).unwrap();
        fds.exec(helper).unwrap();

        assert_eq!(fds.descriptor(helper, 6).unwrap().shadow, Some(shadow(23)));
        assert_eq!(fds.descriptor(helper, 10).unwrap().shadow, Some(shadow(30)));
        assert_eq!(
            fds.descriptor(helper, 104).unwrap().shadow,
            Some(shadow(372))
        );
        assert!(fds.descriptor(browser, network_pipe).is_err());
        assert!(fds.descriptor(browser, v8_shared).is_err());
    }

    #[test]
    fn close_in_one_process_does_not_drop_inherited_open_file() {
        let mut fds = KboxlikeFdSystem::new();
        let parent = fds.spawn_init();
        fds.insert_shadow_fd(parent, 20, None, shadow(15), false)
            .unwrap();
        let child = fds.fork(parent).unwrap();

        let parent_info = fds.descriptor(parent, 20).unwrap();
        let child_info = fds.descriptor(child, 20).unwrap();
        assert_eq!(parent_info.open_file, child_info.open_file);
        assert_eq!(parent_info.ref_count, 2);

        fds.close(parent, 20).unwrap();

        let child_after = fds.descriptor(child, 20).unwrap();
        assert_eq!(child_after.shadow, Some(shadow(15)));
        assert_eq!(child_after.ref_count, 1);
    }

    #[test]
    fn exec_drops_only_cloexec_descriptors() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_plain_fd(pid, 3, Some(3), false).unwrap();
        fds.insert_plain_fd(pid, 4, Some(4), true).unwrap();
        fds.insert_shadow_fd(pid, 5, None, shadow(7), false)
            .unwrap();
        fds.set_cloexec(pid, 5, true).unwrap();

        fds.exec(pid).unwrap();

        assert!(fds.descriptor(pid, 3).is_ok());
        assert_eq!(fds.descriptor(pid, 4), Err(FdError::BadFd(4)));
        assert_eq!(fds.descriptor(pid, 5), Err(FdError::BadFd(5)));
    }

    #[test]
    fn dup_to_replaces_target_without_dropping_source() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_shadow_fd(pid, 8, None, shadow(11), false)
            .unwrap();
        fds.insert_shadow_fd(pid, 9, None, shadow(12), false)
            .unwrap();

        fds.dup_to(pid, 8, 9, false).unwrap();

        let source = fds.descriptor(pid, 8).unwrap();
        let target = fds.descriptor(pid, 9).unwrap();
        assert_eq!(source.open_file, target.open_file);
        assert_eq!(target.shadow, Some(shadow(11)));
        assert_eq!(target.ref_count, 2);
    }

    #[test]
    fn alias_fd_from_preserves_open_file_identity_across_processes() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init();
        let helper = fds.fork(browser).unwrap();
        fds.insert_shadow_fd_with_backing(
            browser,
            8,
            None,
            host_passthrough(88),
            ShadowBacking::HostPassthrough {
                label: "pipe:pipe123:0".to_string(),
            },
            false,
        )
        .unwrap();
        fds.insert_plain_fd(helper, 9, Some(9), false).unwrap();

        fds.alias_fd_from(browser, 8, helper, 9, true).unwrap();

        let source = fds.descriptor(browser, 8).unwrap();
        let target = fds.descriptor(helper, 9).unwrap();
        assert_eq!(source.open_file, target.open_file);
        assert_eq!(source.ref_count, 2);
        assert_eq!(target.shadow, Some(host_passthrough(88)));
        assert!(target.cloexec);
    }

    #[test]
    fn proc_self_exe_is_inherited_across_fork_and_updated_on_exec() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        let helper = fds.fork(browser).unwrap();

        assert_eq!(fds.parent(helper), Ok(Some(browser)));
        assert_eq!(
            fds.current_exe(helper).unwrap(),
            Some("/ms-playwright/chromium/chrome")
        );
        assert_eq!(
            fds.resolve_proc_exe(helper, "/proc/self/exe").unwrap(),
            Some("/ms-playwright/chromium/chrome".to_string())
        );
        assert_eq!(
            fds.resolve_proc_exe(helper, &format!("/proc/{browser}/exe"))
                .unwrap(),
            Some("/ms-playwright/chromium/chrome".to_string())
        );

        fds.exec_with_path(helper, "/usr/lib/chrome_crashpad_handler")
            .unwrap();

        assert_eq!(
            fds.current_exe(helper).unwrap(),
            Some("/usr/lib/chrome_crashpad_handler")
        );
        assert_eq!(
            fds.current_exe(browser).unwrap(),
            Some("/ms-playwright/chromium/chrome")
        );
    }

    #[test]
    fn proc_self_exe_exec_uses_model_identity_not_lkl_procfs() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        let gpu = fds.fork(browser).unwrap();

        let target = fds
            .resolve_proc_exe(gpu, "/proc/self/exe")
            .unwrap()
            .expect("proc self exe is known");
        fds.exec_with_path(gpu, target).unwrap();

        assert_eq!(
            fds.current_exe(gpu).unwrap(),
            Some("/ms-playwright/chromium/chrome")
        );
    }

    #[test]
    fn fcntl_dupfd_preserves_shadow_metadata_from_min_fd() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_plain_fd(pid, 100, Some(100), false).unwrap();
        fds.insert_shadow_fd(pid, 12, None, shadow(44), false)
            .unwrap();

        let duped = fds.dup_from_min(pid, 12, 100, true).unwrap();

        assert_eq!(duped, 101);
        let source = fds.descriptor(pid, 12).unwrap();
        let target = fds.descriptor(pid, duped).unwrap();
        assert_eq!(source.open_file, target.open_file);
        assert_eq!(target.shadow, Some(shadow(44)));
        assert!(target.cloexec);
        assert_eq!(target.ref_count, 2);
    }

    #[test]
    fn kill_subtree_reaps_descendants_and_releases_open_files() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/chrome");
        fds.insert_shadow_fd(browser, 20, None, shadow(20), false)
            .unwrap();
        let crashpad = fds.fork(browser).unwrap();
        let monitor = fds.fork(crashpad).unwrap();
        let sibling = fds.fork(browser).unwrap();

        assert_eq!(fds.descriptor(browser, 20).unwrap().ref_count, 4);

        let victims = fds.kill_subtree(crashpad).unwrap();

        assert_eq!(victims, [crashpad, monitor]);
        assert_eq!(fds.process_count(), 2);
        assert_eq!(fds.descriptor(browser, 20).unwrap().ref_count, 2);
        assert_eq!(fds.descriptor(sibling, 20).unwrap().ref_count, 2);
        assert_eq!(
            fds.current_exe(crashpad),
            Err(FdError::BadProcess(crashpad))
        );
        assert_eq!(fds.current_exe(monitor), Err(FdError::BadProcess(monitor)));
    }

    #[test]
    fn mmap_of_local_shadow_routes_to_host_for_dynamic_linker() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        let lib_shadow = local_shadow(55);
        fds.insert_shadow_fd(pid, 4, Some(44), lib_shadow, false)
            .unwrap();

        assert_eq!(
            fds.route_fd_operation(pid, 4, FdOperation::Mmap),
            Ok(FdRoute::HostShadow { shadow: lib_shadow })
        );
        assert_eq!(
            fds.route_fd_operation(pid, 4, FdOperation::Read),
            Ok(FdRoute::Lkl { fd: 44 })
        );
    }

    #[test]
    fn vector_io_on_same_fd_shadow_routes_to_host_for_postgres() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        let config_shadow = shadow(70);
        fds.insert_shadow_fd(pid, 8, Some(108), config_shadow, false)
            .unwrap();

        for op in [
            FdOperation::Writev,
            FdOperation::Pwritev,
            FdOperation::Pwritev2,
            FdOperation::Readv,
        ] {
            assert_eq!(
                fds.route_fd_operation(pid, 8, op),
                Ok(FdRoute::HostShadow {
                    shadow: config_shadow
                }),
                "{op:?} must use the host shadow"
            );
        }
    }

    #[test]
    fn fcntl_locks_on_same_fd_shadow_route_to_host_for_sqlite() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        let sqlite_shadow = shadow(90);
        fds.insert_shadow_fd(pid, 11, Some(211), sqlite_shadow, false)
            .unwrap();

        assert_eq!(
            fds.route_fd_operation(pid, 11, FdOperation::FcntlLock),
            Ok(FdRoute::HostShadow {
                shadow: sqlite_shadow
            })
        );
    }

    #[test]
    fn plain_lkl_fd_routes_to_lkl_and_unbacked_fd_errors() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_plain_fd(pid, 3, Some(3), false).unwrap();
        fds.insert_plain_fd(pid, 4, None, false).unwrap();

        assert_eq!(
            fds.route_fd_operation(pid, 3, FdOperation::Writev),
            Ok(FdRoute::Lkl { fd: 3 })
        );
        assert_eq!(
            fds.route_fd_operation(pid, 4, FdOperation::Generic),
            Err(FdError::NoBackingFd(4))
        );
    }

    #[test]
    fn host_passthrough_fd_always_routes_to_host() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        let socket = host_passthrough(120);
        fds.insert_shadow_fd(pid, 12, None, socket, false).unwrap();

        for op in [
            FdOperation::Read,
            FdOperation::Write,
            FdOperation::Mmap,
            FdOperation::Generic,
        ] {
            assert_eq!(
                fds.route_fd_operation(pid, 12, op),
                Ok(FdRoute::HostPassthrough { shadow: socket }),
                "{op:?} must use host passthrough"
            );
        }
    }

    #[test]
    fn snapshot_restore_preserves_chromium_process_fd_and_route_state() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        for fd in (0..32).filter(|fd| *fd != 27 && *fd != 30) {
            fds.insert_plain_fd(browser, fd, Some(fd as i64), false)
                .unwrap();
        }
        let network_shadow = shadow(21);
        let v8_shadow = shadow(372);
        fds.insert_shadow_fd(browser, 27, None, network_shadow, false)
            .unwrap();
        fds.insert_shadow_fd(browser, 33033, Some(272), v8_shadow, false)
            .unwrap();
        let network_pipe = fds.dup(browser, 27).unwrap();
        let helper = fds.fork(browser).unwrap();
        fds.close(browser, network_pipe).unwrap();
        fds.dup_to(helper, network_pipe, 6, false).unwrap();
        fds.dup_to(helper, 33033, 104, false).unwrap();
        fds.exec_with_path(helper, "/ms-playwright/chromium/chrome")
            .unwrap();

        let encoded = serde_json::to_vec(&fds.snapshot()).unwrap();
        let decoded: KboxlikeFdSnapshot = serde_json::from_slice(&encoded).unwrap();
        let restored = KboxlikeFdSystem::restore(decoded).unwrap();

        assert_eq!(
            restored.current_exe(helper).unwrap(),
            Some("/ms-playwright/chromium/chrome")
        );
        assert_eq!(restored.parent(helper), Ok(Some(browser)));
        assert_eq!(
            restored.resolve_proc_exe(helper, "/proc/self/exe").unwrap(),
            Some("/ms-playwright/chromium/chrome".to_string())
        );
        assert_eq!(
            restored.descriptor(helper, 6).unwrap().shadow,
            Some(network_shadow)
        );
        assert_eq!(
            restored.route_fd_operation(helper, 104, FdOperation::Mmap),
            Ok(FdRoute::HostShadow { shadow: v8_shadow })
        );
        assert!(restored.descriptor(browser, network_pipe).is_err());
    }

    #[test]
    fn guest_path_shadow_backing_survives_json_restore() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init_with_exe("/usr/local/bin/node");
        let lib_shadow = local_shadow(55);
        let backing = guest_path("/usr/lib/libstdc++.so.6");
        fds.insert_shadow_fd_with_backing(pid, 4, Some(44), lib_shadow, backing.clone(), false)
            .unwrap();

        let encoded = serde_json::to_vec(&fds.snapshot()).unwrap();
        let decoded: KboxlikeFdSnapshot = serde_json::from_slice(&encoded).unwrap();
        let restored = KboxlikeFdSystem::restore(decoded).unwrap();

        assert_eq!(restored.shadow_backing(pid, 4).unwrap(), Some(&backing));
        assert_eq!(
            restored.route_fd_operation(pid, 4, FdOperation::Mmap),
            Ok(FdRoute::HostShadow { shadow: lib_shadow })
        );
    }

    #[test]
    fn restore_rebuilding_shadows_replaces_stale_guest_path_handles() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init_with_exe("/usr/local/bin/node");
        let stale_shadow = local_shadow(55);
        let backing = guest_path("/usr/lib/libstdc++.so.6");
        fds.insert_shadow_fd_with_backing(pid, 4, Some(44), stale_shadow, backing.clone(), false)
            .unwrap();

        let encoded = serde_json::to_vec(&fds.snapshot()).unwrap();
        let decoded: KboxlikeFdSnapshot = serde_json::from_slice(&encoded).unwrap();
        let mut rebuilder = RecordingRebuilder::default();
        let restored =
            KboxlikeFdSystem::restore_rebuilding_shadows(decoded, &mut rebuilder).unwrap();

        let rebuilt_shadow = local_shadow(9000);
        assert_eq!(
            rebuilder.calls,
            vec![(ShadowKind::LocalOnly, backing.clone())]
        );
        assert_eq!(
            restored.descriptor(pid, 4).unwrap().shadow,
            Some(rebuilt_shadow)
        );
        assert_eq!(restored.shadow_backing(pid, 4).unwrap(), Some(&backing));
        assert_eq!(
            restored.route_fd_operation(pid, 4, FdOperation::Mmap),
            Ok(FdRoute::HostShadow {
                shadow: rebuilt_shadow
            })
        );
    }

    #[test]
    fn restore_rebuilding_shadows_replaces_shared_chromium_shadow_once() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        let stale_shadow = shadow(372);
        let backing =
            guest_path("/ms-playwright/chromium-1217/chrome-linux64/v8_context_snapshot.bin");
        fds.insert_shadow_fd_with_backing(
            browser,
            33033,
            Some(272),
            stale_shadow,
            backing.clone(),
            false,
        )
        .unwrap();
        let helper = fds.fork(browser).unwrap();
        fds.dup_to(helper, 33033, 104, false).unwrap();
        fds.close(helper, 33033).unwrap();

        let mut rebuilder = RecordingRebuilder::default();
        let restored =
            KboxlikeFdSystem::restore_rebuilding_shadows(fds.snapshot(), &mut rebuilder).unwrap();

        let rebuilt_shadow = shadow(9000);
        let browser_info = restored.descriptor(browser, 33033).unwrap();
        let helper_info = restored.descriptor(helper, 104).unwrap();
        assert_eq!(
            rebuilder.calls,
            vec![(ShadowKind::HostSameFd, backing.clone())]
        );
        assert_eq!(browser_info.open_file, helper_info.open_file);
        assert_eq!(browser_info.ref_count, 2);
        assert_eq!(browser_info.shadow, Some(rebuilt_shadow));
        assert_eq!(helper_info.shadow, Some(rebuilt_shadow));
    }

    #[test]
    fn restore_rebuilding_shadows_rebuilds_host_passthrough() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        let stale_socket = host_passthrough(120);
        let backing = ShadowBacking::HostPassthrough {
            label: "crashpad-socketpair".to_string(),
        };
        fds.insert_shadow_fd_with_backing(pid, 12, None, stale_socket, backing.clone(), false)
            .unwrap();

        let mut rebuilder = RecordingRebuilder::default();
        let restored =
            KboxlikeFdSystem::restore_rebuilding_shadows(fds.snapshot(), &mut rebuilder).unwrap();

        let rebuilt_socket = host_passthrough(9000);
        assert_eq!(
            rebuilder.calls,
            vec![(ShadowKind::HostPassthrough, backing)]
        );
        assert_eq!(
            restored.route_fd_operation(pid, 12, FdOperation::Read),
            Ok(FdRoute::HostPassthrough {
                shadow: rebuilt_socket
            })
        );
    }

    #[test]
    fn restore_rebuilding_shadows_rejects_ephemeral_shadow() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_shadow_fd(pid, 3, Some(3), shadow(9), false)
            .unwrap();
        let mut rebuilder = RecordingRebuilder::default();

        assert_eq!(
            KboxlikeFdSystem::restore_rebuilding_shadows(fds.snapshot(), &mut rebuilder)
                .unwrap_err(),
            FdError::InvalidSnapshot("ephemeral shadow cannot be rebuilt after restore")
        );
        assert!(rebuilder.calls.is_empty());
    }

    #[test]
    fn mmap_shadow_operation_plans_exact_fd_install() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        let v8_shadow = shadow(372);
        fds.insert_shadow_fd_with_backing(
            browser,
            33033,
            Some(272),
            v8_shadow,
            guest_path("/ms-playwright/chromium-1217/chrome-linux64/v8_context_snapshot.bin"),
            false,
        )
        .unwrap();

        assert_eq!(
            fds.plan_operation_shadow_install(browser, 33033, FdOperation::Mmap),
            Ok(Some(ShadowInstallPlan {
                source_fd: 33033,
                target_fd: 33033,
                shadow: v8_shadow,
                cloexec: false,
            }))
        );
    }

    #[test]
    fn chromium_helper_remap_plans_install_from_preserved_shadow_metadata() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        let v8_shadow = shadow(372);
        fds.insert_shadow_fd_with_backing(
            browser,
            33033,
            Some(272),
            v8_shadow,
            guest_path("/ms-playwright/chromium-1217/chrome-linux64/v8_context_snapshot.bin"),
            false,
        )
        .unwrap();
        let helper = fds.fork(browser).unwrap();
        fds.dup_to(helper, 33033, 104, false).unwrap();

        assert_eq!(
            fds.plan_shadow_install(helper, 104, 104, false),
            Ok(Some(ShadowInstallPlan {
                source_fd: 104,
                target_fd: 104,
                shadow: v8_shadow,
                cloexec: false,
            }))
        );
    }

    #[test]
    fn plain_lkl_operation_does_not_plan_shadow_install() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_plain_fd(pid, 3, Some(3), false).unwrap();

        assert_eq!(
            fds.plan_operation_shadow_install(pid, 3, FdOperation::Read),
            Ok(None)
        );
        assert_eq!(fds.plan_shadow_install(pid, 3, 8, false), Ok(None));
    }

    #[test]
    fn exact_shadow_install_rejects_negative_target_fd() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_shadow_fd(pid, 3, Some(3), shadow(9), false)
            .unwrap();

        assert_eq!(
            fds.plan_shadow_install(pid, 3, -1, false),
            Err(FdError::InvalidFd(-1))
        );
    }

    #[test]
    fn shadow_backing_follows_dup_fork_and_dup2_remap() {
        let mut fds = KboxlikeFdSystem::new();
        let browser = fds.spawn_init_with_exe("/ms-playwright/chromium/chrome");
        let v8_shadow = shadow(372);
        let backing =
            guest_path("/ms-playwright/chromium-1217/chrome-linux64/v8_context_snapshot.bin");
        fds.insert_shadow_fd_with_backing(
            browser,
            33033,
            Some(272),
            v8_shadow,
            backing.clone(),
            false,
        )
        .unwrap();

        let duplicate = fds.dup(browser, 33033).unwrap();
        let helper = fds.fork(browser).unwrap();
        fds.close(browser, duplicate).unwrap();
        fds.dup_to(helper, duplicate, 104, false).unwrap();

        assert_eq!(fds.shadow_backing(helper, 104).unwrap(), Some(&backing));
        assert_eq!(fds.descriptor(helper, 104).unwrap().shadow, Some(v8_shadow));
        assert!(fds.shadow_backing(browser, duplicate).is_err());
    }

    #[test]
    fn restore_rejects_shadow_without_backing() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_shadow_fd(pid, 3, Some(3), shadow(9), false)
            .unwrap();
        let mut snapshot = fds.snapshot();
        let open_file = snapshot
            .processes
            .get(&pid)
            .unwrap()
            .fds
            .get(&3)
            .unwrap()
            .open_file;
        snapshot
            .open_files
            .get_mut(&open_file)
            .unwrap()
            .shadow_backing = None;

        assert_eq!(
            KboxlikeFdSystem::restore(snapshot).unwrap_err(),
            FdError::InvalidSnapshot("shadow open-file is missing reconstruction backing")
        );
    }

    #[test]
    fn restore_rejects_backing_without_shadow() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_plain_fd(pid, 3, Some(3), false).unwrap();
        let mut snapshot = fds.snapshot();
        let open_file = snapshot
            .processes
            .get(&pid)
            .unwrap()
            .fds
            .get(&3)
            .unwrap()
            .open_file;
        snapshot
            .open_files
            .get_mut(&open_file)
            .unwrap()
            .shadow_backing = Some(guest_path("/not-a-shadow"));

        assert_eq!(
            KboxlikeFdSystem::restore(snapshot).unwrap_err(),
            FdError::InvalidSnapshot("non-shadow open-file has shadow reconstruction backing")
        );
    }

    #[test]
    fn restore_rejects_missing_open_file_reference() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_plain_fd(pid, 3, Some(3), false).unwrap();
        let mut snapshot = fds.snapshot();
        let open_file = snapshot
            .processes
            .get(&pid)
            .unwrap()
            .fds
            .get(&3)
            .unwrap()
            .open_file;
        snapshot.open_files.remove(&open_file);

        assert_eq!(
            KboxlikeFdSystem::restore(snapshot).unwrap_err(),
            FdError::InvalidSnapshot("descriptor references missing open-file description")
        );
    }

    #[test]
    fn restore_rejects_refcount_mismatch() {
        let mut fds = KboxlikeFdSystem::new();
        let pid = fds.spawn_init();
        fds.insert_plain_fd(pid, 3, Some(3), false).unwrap();
        let mut snapshot = fds.snapshot();
        let open_file = snapshot
            .processes
            .get(&pid)
            .unwrap()
            .fds
            .get(&3)
            .unwrap()
            .open_file;
        snapshot.open_files.get_mut(&open_file).unwrap().ref_count = 99;

        assert_eq!(
            KboxlikeFdSystem::restore(snapshot).unwrap_err(),
            FdError::InvalidSnapshot("open-file ref_count does not match descriptor references")
        );
    }
}
