#!/usr/bin/env python3
"""wdy_verify.py — independently verify a WDY provenance manifest.

STDLIB ONLY. Anyone can run this; it never contacts wdy.org.

Usage:
  python wdy_verify.py MANIFEST.json ORIGINAL_FILE --pub wdy-key.pub

Checks:
  1. file size + SHA-256 recomputed from ORIGINAL_FILE match the manifest
  2. ai_generated_ratio + live_action_ratio == 1.0
  3. ai/hybrid works carry visible_label = true
  4. Ed25519 signature verifies over the canonical manifest bytes
     (signature object excluded), public key from --pub file
Exit code 0 = all pass; 1 = any check failed (reasons printed).
"""
import base64
import hashlib
import json
import sys
import os

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from wdy_ed25519 import canonical_bytes, ed25519_verify  # noqa: E402


def _load_pub(path):
    """Key file format: one 'key_id=base64pub' per line (lines starting with # ignored)."""
    keys = {}
    for ln in open(path, encoding="utf-8"):
        ln = ln.strip()
        if not ln or ln.startswith("#"):
            continue
        kid, _, pub = ln.partition("=")
        keys[kid.strip()] = base64.b64decode(pub.strip())
    return keys


def main():
    args = sys.argv[1:]
    pubpath = None
    rest = []
    i = 0
    while i < len(args):
        if args[i] == "--pub":
            if i + 1 >= len(args):
                print("error: --pub requires a path"); sys.exit(2)
            pubpath = args[i + 1]; i += 2
        else:
            rest.append(args[i]); i += 1
    if len(rest) != 2 or pubpath is None:
        print(__doc__)
        sys.exit(2)
    mpath, fpath = rest
    m = json.load(open(mpath, encoding="utf-8"))
    sig = m.pop("signature", None)
    ok, fails = True, []

    data = open(fpath, "rb").read()
    sha = hashlib.sha256(data).hexdigest()
    if m["file"]["size_bytes"] != len(data):
        ok = False
        fails.append(f"size mismatch: manifest={m['file']['size_bytes']} actual={len(data)}")
    if m["file"]["sha256"] != sha:
        ok = False
        fails.append("SHA-256 MISMATCH — file is not byte-identical to the recorded one")

    prov = m["provenance"]
    if round(prov["ai_generated_ratio"] + prov["live_action_ratio"], 6) != 1.0:
        ok = False
        fails.append("ai_generated_ratio + live_action_ratio != 1.0")
    disc = m["ai_disclosure"]
    if prov["production_mode"] in ("ai", "hybrid") and not disc.get("visible_label"):
        ok = False
        fails.append("ai/hybrid work without visible_label=true — record is invalid by policy")
    if prov["production_mode"] not in ("ai", "live", "hybrid", "authored"):
        ok = False
        fails.append(f"unknown production_mode: {prov['production_mode']}")
    if sig is None:
        ok = False
        fails.append("manifest has no signature object")
    else:
        keys = _load_pub(pubpath)
        pub = keys.get(sig.get("key_id"))
        if pub is None:
            ok = False
            fails.append(f"unknown key_id: {sig.get('key_id')} (not in {pubpath})")
        elif not ed25519_verify(pub, canonical_bytes(m), base64.b64decode(sig["value"])):
            ok = False
            fails.append("Ed25519 SIGNATURE INVALID — manifest was altered after signing")

    print(f"record        : {m.get('record_id')}")
    print(f"title         : {m.get('title')}")
    print(f"sha256        : {sha}")
    print(f"mode          : {prov['production_mode']} "
          f"(ai {prov['ai_generated_ratio']:.0%} / live {prov['live_action_ratio']:.0%})")
    for f in fails:
        print("FAIL:", f)
    if ok:
        print("RESULT: PASS — file matches record; signature valid; disclosure complete.")
    else:
        print("RESULT: FAIL — do not trust this record.")
    sys.exit(0 if ok else 1)


if __name__ == "__main__":
    main()
