#!/usr/bin/env python3
"""
afg_attest_client.py — minimal client helper for AFG keyless attestations.

Drop this in, call attest(...), done. No API key: your signature over the
payment details IS the credential. The signing wallet MUST be the actual
on-chain counterparty (payer = USDC Transfer 'from', payee = 'to') or the
server rejects the attestation.

Dependencies: eth-account, requests  (pip install eth-account requests)

Supported chains: Base mainnet (8453), Base Sepolia (84532).

NOTE: attesting is live and keyless, but attestations do NOT yet affect any
trust score (the scoring flag is off). Useful attestations are about your
COUNTERPARTIES — self-attestation (attester == subject) is dropped by the
score guard.
"""
import json
import requests
from eth_account import Account
from eth_account.messages import encode_defunct

AFG_ATTEST_URL = "https://afg.ai/v2/api/attest"
SUPPORTED_CHAINS = (8453, 84532)


def build_payload(tx_hash, payer, payee, amount, role, timestamp):
    """The canonical signed message. MUST match the server's
    afg_attest.canonical_payload byte-for-byte: sorted keys, tight
    separators, lowercased addresses, amount as string."""
    return json.dumps({
        "tx_hash": tx_hash.lower(),
        "payer": payer.lower(),
        "payee": payee.lower(),
        "amount": str(amount),
        "attester_role": role,
        "timestamp": timestamp,
    }, sort_keys=True, separators=(",", ":"))


def attest(private_key, tx_hash, chain_id, payer, payee, amount, token, role,
           timestamp=None, url=AFG_ATTEST_URL, timeout=90):
    """Sign and submit an attestation. Returns the verifier's JSON response
    ({"verification_status": "verified"|"rejected", "reason": ...})."""
    if chain_id not in SUPPORTED_CHAINS:
        raise ValueError(f"unsupported chain_id {chain_id}; use {SUPPORTED_CHAINS}")
    if role not in ("payer", "payee"):
        raise ValueError("role must be 'payer' or 'payee'")
    if timestamp is None:
        import time
        timestamp = int(time.time())

    msg = build_payload(tx_hash, payer, payee, amount, role, timestamp)
    sig = Account.sign_message(encode_defunct(text=msg),
                               private_key=private_key).signature.hex()
    if not sig.startswith("0x"):
        sig = "0x" + sig

    body = {
        "tx_hash": tx_hash, "chain_id": chain_id,
        "payer": payer, "payee": payee, "amount": str(amount),
        "token": token, "role": role, "timestamp": timestamp,
        "signature": sig,
    }
    r = requests.post(url, json=body, timeout=timeout)
    return r.json()


def _selftest():
    """Sign a sample with a throwaway key locally and confirm the recovered
    address equals the signer. This is the client/server payload-match guard:
    if the client payload ever drifts from the server's, this breaks."""
    acct = Account.create()
    msg = build_payload("0x" + "ab" * 32, acct.address,
                        "0x000000000000000000000000000000000000dead",
                        1000000, "payer", 1700000000)
    sig = Account.sign_message(encode_defunct(text=msg),
                               private_key=acct.key).signature
    recovered = Account.recover_message(encode_defunct(text=msg), signature=sig)
    assert recovered.lower() == acct.address.lower(), "payload/sig mismatch"
    print(f"selftest OK: recovered {recovered} == signer {acct.address}")
    return True


if __name__ == "__main__":
    _selftest()
