#!/usr/bin/env python3
"""Verify a released Wizer.Bet PRIME artifact. Standard library only; no network, no database, no secrets.

    python verify_prime.py prime-2026-09-03.json
    python verify_prime.py prime-2026-09-03.json --prev prime-2026-09-02.json
    python verify_prime.py prime-2026-09-03.json --expect d6359269febb3b13bc184d3fdd8524f9cd3ee99dea010d33bdbc747e34133b70

What it checks:
  1. The hash inside the file reproduces from the file's own rows:
         body = json.dumps({schema_version, date, source_board_hash, tickets}, sort_keys=True,
                           separators=(",", ":"), ensure_ascii=False)
         hash = sha256(prev_hash + "\\n" + body)
     Only the ticket definition is hashed: kind, state, reason, the legs (selection, probability,
     prices, fixture) and the frozen same-book price. Results are not in the hash and never will be,
     so grading a ticket cannot change it, and neither can anyone.
  2. With --expect, that the recomputed hash equals the fingerprint that was published before kick-off
     (shown on https://wizer.bet/prime/verify on the day).
  3. With --prev, that this file's prev_hash equals the previous day's hash, so the chain is intact.

Exit code 0 when every check passes, 1 otherwise.
"""
import argparse, hashlib, json, sys

KINDS = ("PRIME_3", "PRIME_4")


def canonical_body(artifact):
    tickets = sorted(artifact["tickets"], key=lambda t: KINDS.index(t["kind"]))
    body = {
        "schema_version": artifact.get("schema_version", 1),
        "date": artifact["date"],
        "source_board_hash": artifact["source_board_hash"],
        "tickets": [
            {
                "kind": t["kind"],
                "state": t["state"],
                "reason": t.get("reason"),
                "legs": [dict(l, leg=i + 1) for i, l in enumerate(t.get("legs") or [])],
                "price": t.get("price") or {"state": "NOT_APPLICABLE", "bookmaker": None,
                                            "leg_prices": [], "combined": None, "observed_at": None},
            }
            for t in tickets
        ],
    }
    return json.dumps(body, sort_keys=True, separators=(",", ":"), ensure_ascii=False)


def recompute(artifact):
    return hashlib.sha256((artifact["prev_hash"] + "\n" + canonical_body(artifact)).encode("utf-8")).hexdigest()


def main():
    ap = argparse.ArgumentParser(description="Verify a Wizer.Bet PRIME artifact.")
    ap.add_argument("artifact", help="the released prime.json")
    ap.add_argument("--prev", help="the previous day's released prime.json, to check the chain link")
    ap.add_argument("--expect", help="the hash published before kick-off, to check the commitment")
    a = ap.parse_args()

    art = json.load(open(a.artifact, encoding="utf-8"))
    computed = recompute(art)
    ok = True
    print("date               %s" % art["date"])
    print("hash in file       %s" % art["hash"])
    print("recomputed         %s" % computed)
    if computed == art["hash"]:
        print("  OK   the file reproduces its own hash")
    else:
        ok = False
        print("  FAIL the file's rows do not reproduce the hash it carries")
    if a.expect:
        if a.expect.lower() == computed:
            print("  OK   equals the hash published before kick-off")
        else:
            ok = False
            print("  FAIL differs from the hash published before kick-off (%s)" % a.expect)
    if a.prev:
        prev = json.load(open(a.prev, encoding="utf-8"))
        print("prev_hash          %s" % art["prev_hash"])
        print("previous file hash %s" % prev["hash"])
        if art["prev_hash"] == prev["hash"]:
            print("  OK   chains to the previous day")
        else:
            ok = False
            print("  FAIL prev_hash does not equal the previous day's hash")
        if recompute(prev) != prev["hash"]:
            ok = False
            print("  FAIL the previous file does not reproduce its own hash")
    print("source Board hash  %s" % art["source_board_hash"])
    for t in art["tickets"]:
        legs = t.get("legs") or []
        price = t.get("price") or {}
        print("%s %s%s" % (t["kind"], t["state"], (" (%s)" % t["reason"]) if t.get("reason") else ""))
        for l in legs:
            print("   leg %d  %s v %s  %s  p=%s" % (l["leg"], l["home"], l["away"], l["tip"], l["p"]))
        if price.get("state") == "PRICED":
            print("   price %s at %s (observed %s)" % (price["combined"], price["bookmaker"], price["observed_at"]))
    print("RESULT: %s" % ("VERIFIED" if ok else "NOT VERIFIED"))
    sys.exit(0 if ok else 1)


if __name__ == "__main__":
    main()
