#!/usr/bin/env python3
"""Verify an Agent Credit score proof (AC1) with the Python standard library only.

    python3 verify_proof.py <slug | proof.json> [--date YYYY-MM-DD] [--api https://agentcredit.online]
                            [--rpc https://api.mainnet-beta.solana.com]

It fetches the proof (or reads a saved proof JSON), recomputes the leaf from its fields and the root
from the proof path, and, with --rpc, reads the root transaction and checks its memo and signer.
Spec: https://agentcredit.online/oracle
"""
from __future__ import annotations  # Python 3.8+

import argparse
import hashlib
import json
import sys
import urllib.request

ORACLE = "3tpHMY158xaKt18qrERGmBze3zRSwmCjfGGc32VRMba7"
MEMO_PROGRAMS = {"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"}


def sha256(*parts: bytes) -> bytes:
    h = hashlib.sha256()
    for p in parts:
        h.update(p)
    return h.digest()


def leaf_hash(leaf: dict) -> bytes:
    score = leaf["score"] if leaf["score"] is not None else 0
    return sha256(
        b"AC1/leaf",
        leaf["slug"].encode(),
        b"\x00",
        (leaf["token_mint"] or "").encode(),
        b"\x00",
        int(score).to_bytes(2, "big"),
        int(leaf["computed_at"]).to_bytes(8, "big"),
        sha256(leaf["components_json"].encode()),
    )


def node_hash(a: bytes, b: bytes) -> bytes:
    lo, hi = (a, b) if a <= b else (b, a)
    return sha256(b"AC1/node", lo, hi)


def root_from_proof(leaf: bytes, proof: list) -> str:
    h = leaf
    for sibling in proof:
        h = node_hash(h, bytes.fromhex(sibling))
    return h.hex()


def get_json(url: str, body: dict | None = None) -> dict:
    data = json.dumps(body).encode() if body is not None else None
    req = urllib.request.Request(url, data=data, headers={"content-type": "application/json", "user-agent": "agent-credit-verify/1"})
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.loads(r.read())


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("target", help="agent slug, or a path to a saved proof JSON")
    ap.add_argument("--date", help="YYYY-MM-DD (default: latest root)")
    ap.add_argument("--api", default="https://agentcredit.online")
    ap.add_argument("--rpc", help="Solana RPC URL to check the memo on chain")
    a = ap.parse_args()

    if a.target.endswith(".json"):
        with open(a.target) as f:
            proof = json.load(f)
    else:
        url = f"{a.api}/api/v1/proof/{a.target}" + (f"?date={a.date}" if a.date else "")
        proof = get_json(url)

    leaf = leaf_hash(proof["leaf"])
    ok_leaf = leaf.hex() == proof["leaf_hash"]
    root = root_from_proof(leaf, proof["proof"])
    ok_root = root == proof["root"]
    expected_memo = f"ac1:root:{proof['date']}:{root}:{proof['leaf_count']}"
    print(f"agent      {proof['slug']}  score {proof['leaf']['score']}  computed_at {proof['leaf']['computed_at']}")
    print(f"leaf hash  {leaf.hex()}  {'OK' if ok_leaf else 'MISMATCH'}")
    print(f"root       {root}  {'OK' if ok_root else 'MISMATCH'}")
    print(f"memo       {expected_memo}")
    ok = ok_leaf and ok_root and proof.get("memo") == expected_memo

    if a.rpc:
        tx = proof.get("root_tx")
        if not tx:
            print("on chain   no root transaction yet")
            ok = False
        else:
            res = get_json(a.rpc, {"jsonrpc": "2.0", "id": 1, "method": "getTransaction", "params": [tx["signature"], {"encoding": "jsonParsed", "maxSupportedTransactionVersion": 0}]})
            t = res.get("result")
            memos = [ix.get("parsed") for ix in (t or {}).get("transaction", {}).get("message", {}).get("instructions", []) if ix.get("programId") in MEMO_PROGRAMS]
            signers = [k["pubkey"] for k in (t or {}).get("transaction", {}).get("message", {}).get("accountKeys", []) if k.get("signer")]
            on_chain = bool(t) and not t["meta"]["err"] and expected_memo in memos and ORACLE in signers
            print(f"on chain   {tx['signature']}  {'OK (memo matches, signed by the oracle)' if on_chain else 'MISMATCH'}")
            ok = ok and on_chain

    print("VERIFIED" if ok else "NOT VERIFIED")
    return 0 if ok else 1


if __name__ == "__main__":
    sys.exit(main())
