#!/usr/bin/env python3
"""Verify Levain's record hash chain against its published raw sources.

Usage: run from a directory holding the published files (or give a base URL):
    python3 verify.py                # verifies ./chain.json against ./journal/*, ./ledger.jsonl, ./metrics.jsonl
    python3 verify.py https://levain.bmac.io/source   # fetches and verifies the live record

Exit 0: every hash and every head matches -- this exact history, byte for byte.
Exit 1: divergence, printed precisely. History was edited, reordered, or truncated.
"""
import hashlib, json, sys, urllib.request

VERSION = "levain-chain-v1"

def sha(s):
    return hashlib.sha256(s if isinstance(s, bytes) else s.encode()).hexdigest()

def chain(bufs):
    running = sha(VERSION)
    out = []
    for b in bufs:
        atom = sha(b)
        running = sha(running + "|" + atom)
        out.append((atom, running))
    return out, running

def fetch(base, rel):
    if base.startswith("http"):
        with urllib.request.urlopen(base + "/" + rel) as r:
            return r.read()
    with open(base + "/" + rel, "rb") as f:
        return f.read()

def main():
    base = sys.argv[1].rstrip("/") if len(sys.argv) > 1 else "."
    published = json.loads(fetch(base, "chain.json"))
    fails = []
    def check(name, bufs, labels):
        atoms, head = chain(bufs)
        want = published[name]
        if len(atoms) != len(want["atoms"]):
            fails.append(f"{name}: {len(atoms)} atoms locally, chain.json lists {len(want['atoms'])}")
            return
        for i, ((a, c), w) in enumerate(zip(atoms, want["atoms"])):
            if a != w["sha256"] or c != w["chain"]:
                fails.append(f"{name} atom {labels[i]}: content or order differs from chain.json")
        if head != want["head"]:
            fails.append(f"{name} head mismatch")
    j = published["journal"]["atoms"]
    check("journal", [fetch(base, "journal/" + a["file"]) for a in j], [a["file"] for a in j])
    for name, rel in (("ledger", "ledger.jsonl"), ("metrics", "metrics.jsonl")):
        lines = [l for l in fetch(base, rel).split(b"\n") if l]
        check(name, lines, [f"line {i+1}" for i in range(len(lines))])
    combined = sha(VERSION + "|" + published["journal"]["head"] + "|" + published["ledger"]["head"] + "|" + published["metrics"]["head"])
    if combined != published["head"]:
        fails.append("combined head mismatch")
    if fails:
        print("TAMPERED or inconsistent:")
        for f in fails:
            print("  -", f)
        sys.exit(1)
    print(f"OK: {len(j)} journal entries, "
          f"{len(published['ledger']['atoms'])} ledger lines, "
          f"{len(published['metrics']['atoms'])} metrics rows -- head {published['head'][:12]}")

if __name__ == "__main__":
    main()
