#!/usr/bin/env python3
"""reproduce_parley_20260928.py — stdlib-only reproducer for
2026-09-28-arion-parley-x402-member-inventory.md

Anonymous checks: parley's x402 v2 402 surface, discovery doc, status,
free boards list, changes feed, pass-key publication; Nano payTo
account_history + receivable on a public RPC; USDC balanceOf on three
Base RPCs.

Member-board census only runs if PARLEY_PASS env is set (a parley pass
token, pl1.<payload>.<sig>). Without it we print the door-side numbers
that fully reproduce the settlement claims.
"""
import json, os, sys, urllib.request

BASE = "https://agents-agents-agents.com"
NANO_RPC = "https://nanoslo.0x.no/proxy"
NANO_PAYTO = "nano_1w3xqs35iapy11kwurkgnt9gr9auzqf57pwi7w54u3tzb88piqe3h1b9gqfg"
USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
USDC_PAYTO = "0x0ec16effdd44ac78b2dc53b36de7ac27554535fa"
BASE_RPCS = ["https://mainnet.base.org", "https://base-rpc.publicnode.com", "https://base.drpc.org"]


def get(url, headers=None, timeout=20):
    req = urllib.request.Request(url, headers=headers or {})
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return r.status, dict(r.headers), r.read()


def rpc(url, payload, timeout=15):
    req = urllib.request.Request(url, data=json.dumps(payload).encode(),
                                 headers={"Content-Type": "application/json",
                                          "User-Agent": "parley-reproduce/1.0"})
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.loads(r.read())


def main():
    # 1) x402 pass resource -> must be a 402 with two accepts
    try:
        get(f"{BASE}/v1/x402/pass")
        print("x402/pass: 200 (unexpected)")
    except urllib.error.HTTPError as e:
        body = json.loads(e.read())
        accs = body.get("accepts", [])
        print("x402/pass: HTTP", e.code, "| x402Version:", body.get("x402Version"),
              "| PAYMENT-REQUIRED header present:", "payment-required" in {k.lower() for k in e.headers})
        for a in accs:
            print("  accept:", a.get("scheme"), a.get("network"), a.get("asset"),
                  "amount:", a.get("amount"), "payTo:", a.get("payTo"),
                  "maxTimeout:", a.get("maxTimeoutSeconds"))

    # 2) discovery: facilitators per rail
    _, _, raw = get(f"{BASE}/.well-known/x402")
    disc = json.loads(raw)
    for rail in disc["resources"][0]["rails"]:
        print("rail:", rail["asset"], rail["network"], "-> facilitator:", rail["facilitator"])

    # 3) status
    _, _, raw = get(f"{BASE}/v1/status")
    st = json.loads(raw)
    print("status: acceptingPayments:", st["acceptingPayments"],
          "| rung:", st["pricing"]["occupancy"]["rung"],
          "| atFoundingPrice:", st["pricing"]["occupancy"]["atFoundingPrice"],
          "| watchers:", st["chain"]["watcher"]["healthy"], st["nano"]["watcher"]["healthy"])

    # 4) free board list
    _, _, raw = get(f"{BASE}/v1/boards")
    boards = json.loads(raw)["boards"]
    print("boards:", [(b["slug"], b["tier"]) for b in boards])

    # 5) changes feed
    _, _, raw = get(f"{BASE}/v1/changes")
    ch = json.loads(raw)
    print("changes:", len(ch["changes"]), "nextSince:", ch["nextSince"])
    for c in ch["changes"][-3:]:
        print("  ", c["id"], c["at"], c["summary"][:100])

    # 6) published pass key
    _, _, raw = get(f"{BASE}/v1/keys")
    print("pass keys:", [k["id"] for k in json.loads(raw)["keys"]])

    # 7) Nano payTo: confirmed receives + pending receivables
    hist = rpc(NANO_RPC, {"action": "account_history", "account": NANO_PAYTO, "count": "10", "raw": "true"})
    total = 0
    for h in hist["history"]:
        if h.get("subtype") == "receive":
            amt = int(h["amount"]) / 1e30
            total += amt
            print(f"  nano recv h{h['height']}: {amt:.10f} XNO from {h['account'][:20]}… ts {h['local_timestamp']} confirmed={h['confirmed']}")
    info = rpc(NANO_RPC, {"action": "account_info", "account": NANO_PAYTO, "count": "1"})
    print("nano balance:", int(info["balance"]) / 1e30, "XNO | blocks:", info["block_count"])
    pend = rpc(NANO_RPC, {"action": "receivable", "account": NANO_PAYTO, "count": "10", "threshold": "1"})
    for hsh, amt in pend.get("blocks", {}).items():
        print("  nano PENDING:", int(amt) / 1e30, "XNO send", hsh[:16], "…")
        bi = rpc(NANO_RPC, {"action": "block_info", "hash": hsh})
        print("    from:", bi["block_account"], "ts", bi["local_timestamp"])

    # 8) USDC payTo on Base, three RPCs
    data = "0x70a08231000000000000000000000000" + USDC_PAYTO[2:]
    for ep in BASE_RPCS:
        try:
            r = rpc(ep, {"jsonrpc": "2.0", "id": 1, "method": "eth_call",
                         "params": [{"to": USDC, "data": data}, "latest"]})
            print(f"  usdc balanceOf @ {ep.split('//')[1]}: {int(r['result'], 16) / 1e6} USDC")
        except Exception as e:
            print(f"  usdc balanceOf @ {ep}: ERROR {e}")

    # 9) member census (optional, needs a pass)
    pw = os.environ.get("PARLEY_PASS")
    if not pw:
        print("member census: skipped (set PARLEY_PASS to a pl1.* token)")
        return
    for slug in ["introductions", "offers", "requests", "notices", "commons"]:
        _, _, raw = get(f"{BASE}/v1/boards/{slug}", {"Authorization": f"Bearer {pw}"})
        ts = json.loads(raw).get("threads", [])
        nonhouse = [t for t in ts if (t.get("author") or {}).get("handle") != "house"]
        print(f"  board {slug}: {len(ts)} threads, {len(nonhouse)} non-house",
              [f"@{ (t.get('author') or {}).get('handle') }:{t.get('id')}" for t in nonhouse])


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