"""x402 v1 "exact" scheme rules: pure functions, no I/O."""
from __future__ import annotations

import base64
import json
import re
from decimal import Decimal, InvalidOperation
from typing import Any

USDC = {
    "base": {"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "name": "USD Coin", "version": "2"},
    "base-sepolia": {"address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", "name": "USDC", "version": "2"},
}
NETWORKS = tuple(USDC)
_ADDRESS = re.compile(r"^0x[0-9a-fA-F]{40}$")
_PRICE = re.compile(r"^\d+(\.\d{1,6})?$")


def to_atomic_usdc(price: str) -> str:
    """'0.01' USDC to atomic units (6 decimals): '10000'."""
    if not isinstance(price, str):
        raise TypeError('price must be a decimal string such as "0.01"')
    p = price.strip().lstrip("$")
    if not _PRICE.match(p):
        raise ValueError(f'price must be a positive decimal with at most 6 decimals, got "{price}"')
    try:
        atomic = int(Decimal(p) * 1_000_000)
    except InvalidOperation as e:  # pragma: no cover
        raise ValueError("bad price") from e
    if atomic <= 0:
        raise ValueError("price must be greater than 0")
    return str(atomic)


def is_address(a: Any) -> bool:
    return isinstance(a, str) and bool(_ADDRESS.match(a))


def build_requirements(*, price: str, pay_to: str, network: str, resource: str, description: str,
                       mime_type: str = "application/json", max_timeout_seconds: int = 60) -> dict:
    usdc = USDC[network]
    return {
        "scheme": "exact",
        "network": network,
        "maxAmountRequired": to_atomic_usdc(price),
        "resource": resource,
        "description": description,
        "mimeType": mime_type,
        "payTo": pay_to,
        "maxTimeoutSeconds": max_timeout_seconds,
        "asset": usdc["address"],
        "extra": {"name": usdc["name"], "version": usdc["version"]},
    }


def encode_header(value: Any) -> str:
    return base64.b64encode(json.dumps(value, separators=(",", ":")).encode()).decode()


def decode_header(header: str) -> Any:
    return json.loads(base64.b64decode(header.strip(), validate=True).decode())


def read_payment(header: str, req: dict) -> tuple[dict | None, str | None]:
    """Decodes X-PAYMENT and checks scheme and network. Returns (payment, None) or (None, reason)."""
    try:
        p = decode_header(header)
    except Exception:
        return None, "X-PAYMENT is not base64 encoded JSON"
    if not isinstance(p, dict) or not isinstance(p.get("payload"), dict):
        return None, "X-PAYMENT has no payload"
    if p.get("x402Version") != 1:
        return None, f"unsupported x402Version {p.get('x402Version')}"
    if p.get("scheme") != req["scheme"]:
        return None, f"unsupported scheme {p.get('scheme')}"
    if p.get("network") != req["network"]:
        return None, f"wrong network {p.get('network')}, expected {req['network']}"
    return p, None
