#!/usr/bin/env python3
# AgentPay x402 EIP-712 scaffold - Python (scaffold version 1.0)
# SCAFFOLD_ID: afcf6e08-86dc-4e38-9df7-497ae74deb99
#
# Single-file, dependency-minimized integration scaffold for the AgentPay
# facilitator (x402-agent-pay.com). The EIP-712 domain and types below are the
# EXACT structures /facilitator/verify and /facilitator/settle expect. Only the
# REPLACE_ME fields are dynamic.
#
#   pip install eth-account
#   python3 agentpay_x402_scaffold.py --self-test    # offline env validation
#   python3 agentpay_x402_scaffold.py                # sign + free-verify a payment
import json, os, sys, time, urllib.request

try:
    from eth_account import Account
    from eth_account.messages import encode_typed_data
except ImportError:
    print("pip install eth-account"); sys.exit(1)

SCAFFOLD_ID = "afcf6e08-86dc-4e38-9df7-497ae74deb99"
USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
AGENTPAY_TREASURY = "0x367F1b3D8Ca90D1e087481a9A40d585Bf3451a03"
FACILITATOR = "https://x402-agent-pay.com/facilitator"

EIP712_TYPES = {"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"TransferWithAuthorization":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"validAfter","type":"uint256"},{"name":"validBefore","type":"uint256"},{"name":"nonce","type":"bytes32"}]}
DOMAIN = {"name": "USD Coin", "version": "2", "chainId": 8453,
          "verifyingContract": USDC_BASE}

# ── known-good self-test vector (public anvil test key, zero funds) ──
_TEST_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
_TEST_ADDR = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
_TEST_SIG = "0xd704a46e365258817c513d5ad4b5860f3c39ae48688fd74de7117ff23c342bf60996778dcf7ca1a07efcf28748456a310a022a84c0adf4eac8f695e9d10e4fb31c"
_TEST_AUTH = {"from": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", "to": "0x367F1b3D8Ca90D1e087481a9A40d585Bf3451a03", "value": 5000, "validAfter": 1760000000, "validBefore": 1760172800, "nonce": "0xabababababababababababababababababababababababababababababababab"}

def build_typed(auth):
    return {"types": EIP712_TYPES, "domain": DOMAIN,
            "primaryType": "TransferWithAuthorization", "message": auth}

def sign_authorization(auth, private_key):
    signable = encode_typed_data(full_message=build_typed(auth))
    signed = Account.sign_message(signable, private_key)
    return signed.signature.hex()

def recover_authorization(auth, signature_hex):
    signable = encode_typed_data(full_message=build_typed(auth))
    return Account.recover_message(signable, signature=signature_hex)

def self_test():
    # 1. recover the embedded known-good signature -> must yield the known address
    got = recover_authorization(_TEST_AUTH, _TEST_SIG)
    assert got.lower() == _TEST_ADDR.lower(), ("hash mismatch: got %s want %s" % (got, _TEST_ADDR))
    # 2. round-trip: sign the same auth with the test key, recover it back
    sig = sign_authorization(_TEST_AUTH, _TEST_KEY)
    got2 = recover_authorization(_TEST_AUTH, sig)
    assert got2.lower() == _TEST_ADDR.lower(), ("round-trip mismatch: %s" % got2)
    print("SELF-TEST OK - your eth_account environment signs EIP-712 exactly like the facilitator expects.")
    return True

def make_payment(from_addr, to_addr, value_micro, pk, valid_seconds=3600):
    now = int(time.time())
    auth = {"from": from_addr, "to": to_addr, "value": int(value_micro),
            "validAfter": now, "validBefore": now + valid_seconds,
            "nonce": "0x" + os.urandom(32).hex()}
    sig = sign_authorization(auth, pk)
    envelope = {
        "x402Version": 2,
        "paymentPayload": {"scheme": "exact", "network": "eip155:8453",
                           "payload": {"authorization": auth, "signature": sig}},
        "paymentRequirements": {"scheme": "exact", "network": "eip155:8453",
                                 "asset": USDC_BASE, "payTo": to_addr,
                                 "amount": str(int(value_micro)),
                                 "maxTimeoutSeconds": 60,
                                 "extra": {"name": "USD Coin", "version": "2"}}}
    return envelope

# ══ REPLACE_ME — the only fields you own ══
PAYER_PRIVATE_KEY = os.environ.get("PK") or "REPLACE_ME"   # 0x... your wallet key
PAY_TO = AGENTPAY_TREASURY
AMOUNT_USDC_MICRO = os.environ.get("AMOUNT") or "REPLACE_ME"  # "5000" = $0.005

def main():
    if "--self-test" in sys.argv:
        sys.exit(0 if self_test() else 1)
    if "REPLACE_ME" in (PAYER_PRIVATE_KEY, AMOUNT_USDC_MICRO):
        print("Fill the REPLACE_ME fields (or set PK / AMOUNT env vars) first."); sys.exit(1)
    acct = Account.from_key(PAYER_PRIVATE_KEY)
    env = make_payment(acct.address, PAY_TO, int(AMOUNT_USDC_MICRO), PAYER_PRIVATE_KEY)
    req = urllib.request.Request(
        FACILITATOR + "/verify",
        data=json.dumps(env).encode(),
        headers={"Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (agentpay-scaffold)",
                 "X-Scaffold-Id": SCAFFOLD_ID})
    with urllib.request.urlopen(req, timeout=15) as r:
        out = json.loads(r.read())
    print(json.dumps(out, indent=2))
    print("Pass the same envelope as the X-Payment header (base64 of the JSON) to any paid endpoint.")

if __name__ == "__main__":
    main()
