#!/usr/bin/env python3
"""Build a bounded, machine-readable summary of an owner-provided Base log scan.

This does not assert HTTP/x402 route correlation and makes no RPC call.  It
only preserves exactly which raw-input bytes and sender filters produced the
on-chain-only summary, so later independent Base RPC verification can start
from explicit untrusted transaction hashes.
"""
from __future__ import annotations

import argparse
import hashlib
import json
import re
from collections import defaultdict
from pathlib import Path
from typing import Any

ADDRESS = re.compile(r"^0x[0-9a-fA-F]{40}$")
HASH = re.compile(r"^0x[0-9a-fA-F]{64}$")
TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"


def address_from_topic(topic: Any) -> str:
    if not isinstance(topic, str) or not re.fullmatch(r"0x[0-9a-fA-F]{64}", topic):
        raise ValueError("malformed indexed address topic")
    return "0x" + topic[-40:].lower()


def quantity(value: Any) -> int:
    if not isinstance(value, str) or not re.fullmatch(r"0x[0-9a-fA-F]+", value):
        raise ValueError("malformed amount")
    return int(value, 16)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", required=True, type=Path)
    parser.add_argument("--output", required=True, type=Path)
    parser.add_argument("--payee", required=True)
    parser.add_argument("--asset", required=True)
    parser.add_argument("--external-payer", action="append", required=True)
    args = parser.parse_args()
    payee = args.payee.lower()
    asset = args.asset.lower()
    external = {item.lower() for item in args.external_payer}
    if not ADDRESS.fullmatch(payee) or not ADDRESS.fullmatch(asset) or not all(ADDRESS.fullmatch(item) for item in external):
        raise SystemExit("payee, asset, and external payer values must be EVM addresses")

    source = args.input.read_bytes()
    rows: list[dict[str, Any]] = []
    for lineno, line in enumerate(source.decode("utf-8").splitlines(), 1):
        if not line.strip():
            continue
        item = json.loads(line)
        topics = item.get("topics")
        if (
            not isinstance(topics, list)
            or len(topics) < 3
            or item.get("address", "").lower() != asset
            or str(topics[0]).lower() != TRANSFER_TOPIC
            or address_from_topic(topics[2]) != payee
            or not HASH.fullmatch(str(item.get("transactionHash", "")))
            or item.get("removed") is not False
        ):
            raise SystemExit(f"input line {lineno} is not a canonical incoming Base USDC Transfer log")
        item["_payer"] = address_from_topic(topics[1])
        item["_amount"] = quantity(item["data"])
        rows.append(item)

    grouped: dict[str, list[dict[str, Any]]] = defaultdict(list)
    for row in rows:
        if row["_payer"] in external:
            grouped[row["_payer"]].append(row)
    payments = [
        {
            "payer": payer,
            "count": len(items),
            "amountAtomic": str(sum(item["_amount"] for item in items)),
            "transactions": [
                {
                    "transactionHash": item["transactionHash"].lower(),
                    "blockNumber": item["blockNumber"],
                    "blockHash": item["blockHash"].lower(),
                    "logIndex": item["logIndex"],
                    "amountAtomic": str(item["_amount"]),
                }
                for item in items
            ],
        }
        for payer, items in sorted(grouped.items())
    ]
    summary = {
        "$schema": "../schemas/x402-v2-onchain-payee-scan.schema.json",
        "schema": "vaultfire.x402.v2-onchain-payee-scan.v1",
        "scope": "owner-provided-raw-log-summary-onchain-only",
        "activation": "fail-closed-no-route-or-request-receipt-correlation",
        "source": {
            "fileName": args.input.name,
            "sha256": hashlib.sha256(source).hexdigest(),
            "lineCount": len(rows),
            "verification": "raw-input-structure-only; no live RPC query was made by this builder",
        },
        "paymentRail": {
            "network": "eip155:8453",
            "chainId": 8453,
            "asset": asset,
            "payee": payee,
            "eventTopic0": TRANSFER_TOPIC,
        },
        "externalPayerFilter": sorted(external),
        "externalPayments": payments,
        "externalPaymentCount": sum(item["count"] for item in payments),
        "externalAmountAtomic": str(sum(int(item["amountAtomic"]) for item in payments)),
        "notProven": [
            "V2 route or HTTP method",
            "request body, request identifier, or idempotency key",
            "x402 authorization identifier, nonce, signature, or validity window",
            "payment response delivery or receipt",
            "owner approval for a route to accept a payment",
            "current canonical-chain status or finality",
        ],
        "nextStep": "For each selected transaction, supply owner receipt correlation under x402-v2-live-evidence.schema.json and run verify-base-x402-receipt.py read-only.",
    }
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(summary, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    print(f"wrote {args.output}: {summary['externalPaymentCount']} filtered payments, {summary['externalAmountAtomic']} atomic units")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
