#!/usr/bin/env python3
"""Build a deterministic source and reproducibility archive."""

from __future__ import annotations

import hashlib
from pathlib import Path
import shutil
import tempfile
import zipfile


HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
OUTPUT = HERE / "build" / "interior-barriers-source.zip"
ROUTE_REL = HERE.relative_to(ROOT)

ROUTE_FILES = (
    "README.md",
    "author-block.tex",
    "manuscript.tex",
    "SOURCE_DOSSIERS.md",
    "HOSTILE_AUDIT.md",
    "REFEREE_AUDIT.md",
    "dossiers.json",
    "source_manifest.json",
    "verify.py",
    "verify_manuscript.py",
    "build_package.py",
)
ROOT_FILES = (
    "canon/obstructions/O-0176-uniform-interior-certificates-cannot-prove-rh.md",
    "literature/2026-07-19-uniform-interior-certificate-barrier-primary-sources.md",
    "lean/README.md",
    "lean/RHResearch.lean",
    "lean/lake-manifest.json",
    "lean/lakefile.toml",
    "lean/lean-toolchain",
)
ROOT_TREES = (
    "canon/witnesses/O-0176",
    "lean/RHResearch",
)


def sha256(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for block in iter(lambda: handle.read(1024 * 1024), b""):
            digest.update(block)
    return digest.hexdigest()


def copy_file(source: Path, destination: Path) -> None:
    destination.parent.mkdir(parents=True, exist_ok=True)
    shutil.copy2(source, destination)


def copy_tree(source: Path, destination: Path) -> None:
    for path in sorted(source.rglob("*")):
        if not path.is_file() or "__pycache__" in path.parts:
            continue
        copy_file(path, destination / path.relative_to(source))


def write_manifest(bundle_root: Path) -> None:
    entries = []
    for path in sorted(bundle_root.rglob("*")):
        if path.is_file() and path.name != "SHA256SUMS":
            entries.append(f"{sha256(path)}  {path.relative_to(bundle_root)}")
    (bundle_root / "SHA256SUMS").write_text(
        "\n".join(entries) + "\n",
        encoding="ascii",
    )


def write_zip(bundle_root: Path) -> None:
    OUTPUT.parent.mkdir(parents=True, exist_ok=True)
    with zipfile.ZipFile(
        OUTPUT,
        "w",
        compression=zipfile.ZIP_DEFLATED,
        compresslevel=9,
    ) as archive:
        for path in sorted(bundle_root.rglob("*")):
            if not path.is_file():
                continue
            info = zipfile.ZipInfo(
                str(Path("RH") / path.relative_to(bundle_root)),
                date_time=(2026, 7, 19, 12, 0, 0),
            )
            info.compress_type = zipfile.ZIP_DEFLATED
            info.external_attr = 0o100644 << 16
            archive.writestr(info, path.read_bytes())


def main() -> None:
    with tempfile.TemporaryDirectory(prefix="o0176-package-") as temp:
        bundle_root = Path(temp) / "RH"
        for name in ROUTE_FILES:
            copy_file(HERE / name, bundle_root / ROUTE_REL / name)
        copy_tree(HERE / "sources", bundle_root / ROUTE_REL / "sources")
        for name in ROOT_FILES:
            copy_file(ROOT / name, bundle_root / name)
        for name in ROOT_TREES:
            copy_tree(ROOT / name, bundle_root / name)
        write_manifest(bundle_root)
        write_zip(bundle_root)

    print(f"built {OUTPUT} ({sha256(OUTPUT)})")


if __name__ == "__main__":
    main()
