"""Writes provenance/checksums.sha256 (sha256sum format: "<hex>  <relative path>") over the
artifact directories plan/, sources/, data/, code/, results/, manuscript/, review/ and the
top-level CLAUDE.md. Excluded: results/reproduction/ (rewritten by every run of reproduce.sh),
compiled binaries, __pycache__, .claude/ directories and the LaTeX build by-products that
tectonic rewrites on each build (main.aux/.log/.out/.bbl/.blg). Large DRAT proofs are included
(their digests also appear in the result records).

usage: python3 code/tools/make_checksums.py [--verify]
"""
import hashlib
import os
import sys

ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
DIRS = ["plan", "sources", "data", "code", "results", "manuscript", "review"]
EXCLUDE_PREFIX = ["results/reproduction/", "results/revision/run_fortcover_checks.pid", "results/revision/verify_full.pid",
                  "results/revision/finalize.log", "results/revision/finalize_summary.json"]
EXCLUDE_NAMES = {"brute_force", "ff_brute", "beta_brute", "drat-trim", "ref_ff", ".DS_Store"}
EXCLUDE_SUFFIX = (".pyc", ".aux", ".log", ".out", ".blg", ".bbl", ".fls", ".fdb_latexmk", ".pid")
OUT = os.path.join(ROOT, "provenance", "checksums.sha256")


def sha256(path):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(1 << 24), b""):
            h.update(chunk)
    return h.hexdigest()


def listing():
    files = ["CLAUDE.md"]
    for d in DIRS:
        for dp, dns, fns in os.walk(os.path.join(ROOT, d)):
            dns[:] = sorted(x for x in dns if x not in ("__pycache__", ".claude"))
            for fn in sorted(fns):
                rel = os.path.relpath(os.path.join(dp, fn), ROOT)
                if any(rel.startswith(p) for p in EXCLUDE_PREFIX) or fn in EXCLUDE_NAMES or rel.endswith(EXCLUDE_SUFFIX):
                    continue
                if os.path.islink(os.path.join(dp, fn)):
                    continue
                files.append(rel)
    return files


def main():
    if "--verify" in sys.argv:
        bad = 0
        n = 0
        for line in open(OUT):
            h, rel = line.rstrip("\n").split("  ", 1)
            n += 1
            if not os.path.exists(os.path.join(ROOT, rel)) or sha256(os.path.join(ROOT, rel)) != h:
                bad += 1
                print("MISMATCH", rel)
        print("checked %d files, %d mismatches" % (n, bad))
        sys.exit(1 if bad else 0)
    files = listing()
    with open(OUT, "w") as f:
        for rel in files:
            f.write("%s  %s\n" % (sha256(os.path.join(ROOT, rel)), rel))
    print("wrote %s with %d entries" % (os.path.relpath(OUT, ROOT), len(files)))


if __name__ == "__main__":
    main()
