#!/usr/bin/env python3
"""Run the Mastercard reference verifier over credentials minted by vi.appliedidentities.com.

Setup (python >= 3.10):
  pip install "git+https://github.com/agent-intent/verifiable-intent@<commit in vectors.json>"
  curl -O https://vi.appliedidentities.com/playground/xval/vectors.json
  curl -O https://vi.appliedidentities.com/playground/xval/verify.py
  python3 verify.py vectors.json

Exit 0 = the reference accepted every vector. Exit 1 = it rejected one (errors printed).

Clock: the credentials carry production lifetimes (L3 is 5 minutes; the reference
itself refuses any L3 longer than an hour), so by default this verifies at the
frozen mint clock recorded in the file. Pass --live-clock to use the real time; once
the credentials have aged past their lifetimes the reference rejects them as expired,
which is the correct outcome.

Root type: the reference's default expected L1 vct is Mastercard's card credential.
This script passes the issuer's own vct from the file, so a PASS proves wire-format
parity. Which root credential type a verifier should trust is a separate decision.

This file imports only the reference package. It contains none of the issuer's code.
"""
import json
import sys
import time

try:
    from verifiable_intent.crypto.sd_jwt import decode_sd_jwt
    from verifiable_intent.crypto.signing import jwk_to_public_key
    from verifiable_intent.verification.chain import verify_chain
except ImportError:
    sys.exit("verifiable_intent not installed: pip install "
             "\"git+https://github.com/agent-intent/verifiable-intent@<commit>\" (python >= 3.10)")

args = [a for a in sys.argv[1:] if not a.startswith("--")]
live_clock = "--live-clock" in sys.argv
path = args[0] if args else "vectors.json"

with open(path) as fh:
    doc = json.load(fh)
if doc.get("format") != "vi-xval-vectors/1":
    sys.exit(f"unexpected format: {doc.get('format')!r}")

print(f"issuer:          {doc['issuer']}")
print(f"minted at:       {doc['minted_at_iso']}  (vi-server {doc['vi_server_commit'][:7]})")
print(f"reference:       {doc['reference']['repo']} @ {doc['reference']['commit'][:7]}")
if live_clock:
    print("clock:           live (the credentials expire minutes after minting)")
else:
    print(f"clock:           frozen at mint time ({doc['minted_at']})")
    time.time = lambda _t=doc["minted_at"]: float(_t)   # the reference reads int(time.time())
print()

failed = 0
for mode, d in doc["vectors"].items():
    l1 = decode_sd_jwt(d["l1_serialized"])
    l2 = decode_sd_jwt(d["l2_serialized"])
    kwargs = dict(
        issuer_public_key=jwk_to_public_key(d["issuer_public_jwk"]),
        l1_serialized=d["l1_serialized"],
        l2_serialized=d["l2_serialized"],
        expected_l1_vct=d["l1_vct"],
    )
    l3a = decode_sd_jwt(d["l3a_serialized"]) if d.get("l3a_serialized") else None
    l3b = decode_sd_jwt(d["l3b_serialized"]) if d.get("l3b_serialized") else None
    for k in ("l2_payment_serialized", "l2_checkout_serialized"):
        if d.get(k):
            kwargs[k] = d[k]
    for k, v in d.get("expected", {}).items():   # audiences the minter bound in
        kwargs[f"expected_{k}"] = v
    res = verify_chain(l1, l2, l3_payment=l3a, l3_checkout=l3b, **kwargs)
    verdict = "PASS" if res.valid else "FAIL"
    print(f"== {mode} == {verdict}  ({len(res.checks_performed)} checks performed, {len(res.checks_skipped)} skipped)")
    for s in res.checks_skipped:
        print(f"   [skipped] {s}")
    for e in res.errors:
        print(f"   [ERROR] {e}")
    if not res.valid:
        failed += 1

print()
if failed:
    print(f"Verdict: the reference REJECTED {failed} vector(s)")
    sys.exit(1)
print("Verdict: the reference ACCEPTED every vector (wire-format parity at the pinned commit)")
