#!/usr/bin/env python3
"""usage_count.py — settlement-usage field for the $MUSEBOOK collision census
(eip155:4663). For each census contract: walk BACK from head in 50k-block
chunks, counting Transfer events (adaptive halving under the 10k-result cap),
until 1.5M blocks scanned or mint reached. Records transfer count, unique
senders/recipients, and last-active block. Output: usage_4663.json

Deciding evidence for "which is the real musebook" is USAGE — which contract
the rails actually quote, measured in transfers, not attestation.
"""
import json, sys, time, urllib.request

LOG_RPCS = ["https://rpc.mainnet.chain.robinhood.com", "https://rpc.ordofi.network"]
H = {"Content-Type": "application/json", "User-Agent": "ARION-usage/1.0"}
TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
IDENT = json.load(open("/workspace/deliverables/musebook-collision/ident_4663.json"))
CHUNK = 50_000
SCAN_CAP = 1_500_000   # max blocks scanned per contract
LOG_CAP = 25_000       # stop early once this many logs counted

_i = 0
def rpc(method, params, budget=25):
    global _i
    body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": method, "params": params}).encode()
    t0, delay, last = time.time(), 0.3, None
    while True:
        url = LOG_RPCS[_i % len(LOG_RPCS)]; _i += 1
        try:
            d = json.load(urllib.request.urlopen(urllib.request.Request(url, data=body, headers=H), timeout=30))
            if "result" in d:
                time.sleep(0.06)
                return d["result"]
            last = d.get("error")
            msg = json.dumps(last).lower()
            if "exceeds" in msg or "limit" in msg:
                raise RangeTooBig(msg[:120])
            if time.time() - t0 > budget:
                raise RuntimeError(f"budget: {last}")
            time.sleep(delay); delay = min(delay * 1.7, 2.5)
        except RangeTooBig:
            raise
        except Exception as e:
            last = e
            if time.time() - t0 > budget:
                raise RuntimeError(f"budget: {last}")
            time.sleep(delay); delay = min(delay * 1.7, 2.5)

class RangeTooBig(Exception):
    pass

def logs_in(addr, a, b):
    try:
        return rpc("eth_getLogs", [{"address": addr, "fromBlock": hex(a),
                   "toBlock": hex(b), "topics": [TRANSFER_TOPIC]}])
    except RangeTooBig:
        if b - a < 2000:
            # count as ">10k here" — split as far as we can cheaply
            m = (a + b) // 2
            return logs_in(addr, a, m) + logs_in(addr, m + 1, b)
        m = (a + b) // 2
        return logs_in(addr, a, m) + logs_in(addr, m + 1, b)

def scan(addr, mint, head):
    total, senders, recips = 0, set(), set()
    scanned = 0
    b = head
    last_active = None
    while b > mint and scanned < SCAN_CAP and total < LOG_CAP:
        a = max(mint, b - CHUNK + 1)
        for lg in logs_in(addr, a, b):
            total += 1
            t = lg.get("topics", [])
            if len(t) >= 3:
                senders.add(t[1][-40:]); recips.add(t[2][-40:])
            if last_active is None:
                last_active = int(lg["blockNumber"], 16)
        scanned += b - a + 1
        print(f"    {addr[:10]} {a}-{b} cum={total} senders={len(senders)} recips={len(recips)}", file=sys.stderr, flush=True)
        b = a - 1
    return {"contract": addr, "mint_block": mint, "head": head,
            "blocks_scanned": scanned, "window": f"{max(mint,b+1)}-{head}",
            "transfers_in_window": total, "unique_senders": len(senders),
            "unique_recipients": len(recips), "last_active_block": last_active,
            "truncated": scanned >= SCAN_CAP or total >= LOG_CAP}

def main():
    head = int(rpc("eth_blockNumber", []), 16)
    rows = []
    for c in IDENT.get("results", []):
        addr, mint = c["contract"], (c.get("first_mint") or {}).get("block")
        if not addr or not mint:
            continue
        t0 = time.time()
        try:
            row = scan(addr.lower(), int(mint), head)
        except Exception as e:
            row = {"contract": addr, "mint_block": mint, "head": head, "error": str(e)[:200]}
        row["elapsed_s"] = round(time.time() - t0, 1)
        rows.append(row)
        print(json.dumps(row), flush=True)
    json.dump({"head": head, "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
               "note": "windowed usage scan: back from head, <=1.5M blocks or <=25k logs per contract",
               "rows": rows}, open("/workspace/deliverables/musebook-collision/usage_4663.json", "w"), indent=1)
    print("WROTE usage_4663.json", flush=True)

if __name__ == "__main__":
    main()
