"""Run a command in a bubblewrap jail: the workspace and the system toolchain, nothing else.

`codex sandbox` was the obvious reuse here and is not sufficient: it confines
writes but leaves every file on the machine readable, and leaves the session
D-Bus socket reachable, from which `systemd-run --user` starts a process the
jail never contained. Both were reproduced. bubblewrap confines reads, writes,
IPC and the network in one mechanism.
"""

from __future__ import annotations

import os
import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path

# Read-only for the sandboxed command: the system and its toolchains. A path
# absent here is invisible, so `~/.ssh` and `~/.codex` cannot be read at all.
SYSTEM_READABLE_ROOTS = (
    "/usr",
    "/bin",
    "/sbin",
    "/lib",
    "/lib32",
    "/lib64",
    "/opt",
    "/etc",
)
# The parent holds land tokens, account homes and API keys; the child gets only
# what a build needs. Anything absent from this list never reaches the command.
INHERITED_ENV = ("LANG", "LC_ALL", "TERM", "TZ")
CHILD_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

MODES = ("read-only", "workspace-write", "full-access")


def child_env(workspace: Path) -> dict[str, str]:
    """A fixed, minimal environment. PATH is set rather than inherited: the
    operator's PATH points into `~/.local/bin` and `~/.claude/bin`, which hold
    privileged helpers the sandboxed command has no business resolving."""
    env = {name: os.environ[name] for name in INHERITED_ENV if name in os.environ}
    env["PATH"] = CHILD_PATH
    env["HOME"] = str(workspace)
    env["USER"] = os.environ.get("USER", "user")
    env["SHELL"] = "/bin/bash"
    env["PWD"] = str(workspace)
    return env


@dataclass(frozen=True)
class Sandboxed:
    """A command and the environment it must be launched with. The two are one
    value because they are one guarantee: bwrap keeps its own init process inside
    the jail, and with `--unshare-pid` that process is pid 1, whose `/proc/1/environ`
    the command can read. Launching bwrap with the parent's environment therefore
    hands over every token in it, however clean the child's own environment is."""

    argv: list[str]
    env: dict[str, str]


def sandbox_command(
    command: str,
    *,
    workspace: Path,
    cwd: Path,
    mode: str,
    network_access: bool,
    readable_roots: tuple[str, ...] = (),
) -> Sandboxed:
    if mode not in MODES:
        raise ValueError(f"unknown sandbox mode '{mode}' (use: {', '.join(MODES)})")
    env = child_env(workspace)
    if mode == "full-access":
        return Sandboxed(["bash", "-c", command], env)
    bwrap = shutil.which("bwrap")
    if bwrap is None:
        raise ValueError("bubblewrap (bwrap) is not installed; gptbridge cannot sandbox commands")

    argv = [
        bwrap,
        "--unshare-user",
        "--unshare-pid",
        # Without it bwrap keeps a reaper as pid 1, and that process still holds
        # the environment bwrap was launched with, which the command reads
        # straight out of /proc/1/environ.
        "--as-pid-1",
        "--unshare-ipc",
        "--unshare-uts",
        "--unshare-cgroup-try",
        "--die-with-parent",
        "--new-session",
        "--proc", "/proc",
        "--dev", "/dev",
        "--tmpfs", "/tmp",
        "--tmpfs", "/run",
        "--tmpfs", "/var",
    ]
    if not network_access:
        argv += ["--unshare-net"]
    for root in (*SYSTEM_READABLE_ROOTS, *readable_roots):
        if Path(root).exists():
            argv += ["--ro-bind", root, root]
    # Mounted last so it wins over any readable root that contains it.
    argv += ["--ro-bind" if mode == "read-only" else "--bind", str(workspace), str(workspace)]
    argv += ["--chdir", str(cwd), "--clearenv"]
    for name, value in sorted(env.items()):
        argv += ["--setenv", name, value]
    return Sandboxed(argv + ["--", "/bin/bash", "-c", command], env)


def state_dir() -> Path:
    return Path(os.environ.get("GPTBRIDGE_HOME",
                               Path.home() / ".overdeck" / "gptbridge"))


def enforcement_holds(
    workspace: Path, *, mode: str, network_access: bool, readable_roots: tuple[str, ...] = ()
) -> tuple[bool, str]:
    """Prove the jail actually holds before serving. An unenforced sandbox is
    worse than a missing one, because the operator believes they have it.

    Each probe asserts a capability the remote model must not have. Effects are
    checked on the host, not by exit code: bwrap gives the command a private
    tmpfs root, so a write it believes succeeded may never have left the jail."""
    if mode == "full-access":
        return True, "full-access: no sandbox by request"

    probes = state_dir() / "probes"
    probes.mkdir(parents=True, exist_ok=True)
    canary = probes / "canary"
    canary.write_text("gptbridge-canary-secret\n", encoding="utf-8")
    target = probes / "written-by-the-sandbox"
    target.unlink(missing_ok=True)

    def run(command: str, *, extra_env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]:
        # Probes run against the jail as it will be served — network and extra
        # readable roots included — so a passing verdict describes the
        # configuration the model actually gets, not a stricter one.
        jail = sandbox_command(
            command,
            workspace=workspace,
            cwd=workspace,
            mode=mode,
            network_access=network_access,
            readable_roots=readable_roots,
        )
        return subprocess.run(
            jail.argv,
            env={**jail.env, **(extra_env or {})},
            cwd=workspace,
            capture_output=True,
            text=True,
            timeout=60,
        )

    try:
        if "gptbridge-canary-secret" in run(f"cat {canary}").stdout:
            return False, f"the sandbox let a command read {canary}, outside the workspace"
        run(f"printf breach > {target}")
        if target.exists():
            return False, f"the sandbox let a command write {target}, outside the workspace"
        leaked = run(
            "cat /proc/*/environ 2>/dev/null | tr '\\0' '\\n'",
            extra_env={"GPTBRIDGE_ENV_CANARY": "gptbridge-env-canary"},
        )
        if "gptbridge-env-canary" in leaked.stdout:
            return False, "a command read the launching process's environment through /proc"
        if run("test -S /run/user/$(id -u)/bus").returncode == 0:
            return False, "the session D-Bus socket is reachable, which allows an unconfined process"
        if run("XDG_RUNTIME_DIR=/run/user/$(id -u) systemd-run --user --pipe --wait /bin/true").returncode == 0:
            return False, "systemd-run started a process outside the sandbox"
        if not network_access and run("getent hosts api.openai.com").returncode == 0:
            return False, "the sandbox reached the network with network access disabled"
        if mode != "read-only":
            probe = workspace / ".gptbridge-write-probe"
            run(f"printf ok > {probe}")
            if not probe.exists():
                return False, "the sandbox blocked a write inside the workspace, so nothing would work"
            probe.unlink(missing_ok=True)
    except FileNotFoundError:
        return False, "bwrap is not on PATH; the sandbox cannot be enforced"
    except subprocess.TimeoutExpired:
        return False, "a sandbox probe timed out"
    finally:
        canary.unlink(missing_ok=True)
        target.unlink(missing_ok=True)

    return True, "sandbox enforced"
