"""
Ontario Protocol — list-service example (Python).

Goal: help a service provider rehearse the x402 marketplace listing flow BEFORE spending money.

This script:
- Validates a list-service payload (free; no payment; no storage).
- Rehearses the full HTTP 402 + retry flow against Ontario's SANDBOX endpoint (no spend; no storage).

Important: the sandbox PAYMENT-SIGNATURE payload in this file is intentionally fake and is NOT a
production x402 payment signature. Do not reuse it for real settlement.
"""

from __future__ import annotations

import argparse
import base64
import json
import sys
from typing import Any, Dict, Optional, Tuple

import requests


DEFAULT_BASE = "https://ontarioprotocol.com"


def _b64json(payload: Dict[str, Any]) -> str:
    raw = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")
    return base64.b64encode(raw).decode("ascii")


def _decode_payment_required(header_value: str) -> Optional[Dict[str, Any]]:
    if not header_value:
        return None
    try:
        decoded = base64.b64decode(header_value.strip()).decode("utf-8")
        return json.loads(decoded)
    except Exception:
        return None


def _post_json(
    url: str,
    payload: Dict[str, Any],
    headers: Optional[Dict[str, str]] = None,
    timeout: int = 30,
) -> Tuple[int, Dict[str, str], Any]:
    h = {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "User-Agent": "OntarioListService-Python/1.0",
    }
    if headers:
        h.update(headers)
    res = requests.post(url, json=payload, headers=h, timeout=timeout)
    content_type = (res.headers.get("Content-Type") or "").lower()
    if "application/json" in content_type:
        body: Any = res.json()
    else:
        body = res.text
    return res.status_code, dict(res.headers), body


def validate_payload(base_url: str, listing_payload: Dict[str, Any]) -> Dict[str, Any]:
    url = f"{base_url.rstrip('/')}/api/x402/list-service/validate"
    status, _, body = _post_json(url, listing_payload)
    if status != 200:
        raise RuntimeError(f"validate failed: HTTP {status}: {body}")
    if not isinstance(body, dict) or not body.get("ok"):
        raise RuntimeError(f"validate failed: {body}")
    return body


def sandbox_rehearsal(base_url: str, listing_payload: Dict[str, Any]) -> Dict[str, Any]:
    """
    Two-step rehearsal:
      1) POST without PAYMENT-SIGNATURE → expect 402 + PAYMENT-REQUIRED
      2) POST again with a *fake* sandbox payment payload encoded in PAYMENT-SIGNATURE → expect 200
    """
    sandbox_url = f"{base_url.rstrip('/')}/sandbox/api/x402/list-service"

    status, headers, body = _post_json(sandbox_url, listing_payload)
    if status != 402:
        raise RuntimeError(f"expected 402 challenge; got HTTP {status}: {body}")

    required_header = headers.get("PAYMENT-REQUIRED") or headers.get("X-Payment-Required") or ""
    requirements = _decode_payment_required(required_header) or {}

    # Minimal sandbox-only payment payload: sandbox facilitator treats any non-empty signature as valid.
    # This is *not* a real x402 payment signature.
    payment_payload = {
        "x402Version": 1,
        "scheme": requirements.get("scheme") or "exact",
        "network": requirements.get("network") or "base-sandbox",
        "payload": {
            "signature": "sandbox-valid",
            "authorization": {
                "from": "0xSandboxAgent",
                "to": requirements.get("payTo") or "0x0000000000000000000000000000000000000420",
                "value": requirements.get("maxAmountRequired") or "10000",
                "nonce": "ontario-list-service-sandbox",
            },
        },
    }
    sig_header = _b64json(payment_payload)

    status2, _, body2 = _post_json(
        sandbox_url,
        listing_payload,
        headers={"PAYMENT-SIGNATURE": sig_header},
    )
    if status2 != 200:
        raise RuntimeError(f"sandbox retry failed: HTTP {status2}: {body2}")
    if not isinstance(body2, dict):
        raise RuntimeError(f"unexpected response: {body2}")
    return body2


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Ontario Protocol list-service sandbox rehearsal (no spend)."
    )
    parser.add_argument(
        "--base-url",
        default=DEFAULT_BASE,
        help="Ontario base URL (default: https://ontarioprotocol.com)",
    )
    parser.add_argument("--name", default="Example Service")
    parser.add_argument("--description", default="Paid endpoint for agents")
    parser.add_argument("--category", default="data")
    parser.add_argument("--endpoint", default="https://example.com/api/paid")
    parser.add_argument("--method", default="POST")
    parser.add_argument("--price-usdc", default="0.01")
    parser.add_argument("--network", default="base")
    parser.add_argument("--owner-url", default="https://example.com")
    parser.add_argument("--owner-contact", default="ops@example.com")
    args = parser.parse_args()

    listing_payload = {
        "name": args.name,
        "description": args.description,
        "category": args.category,
        "endpoint": args.endpoint,
        "method": args.method,
        "price_usdc": args.price_usdc,
        "network": args.network,
        "owner_url": args.owner_url,
        "owner_contact": args.owner_contact,
        "tags": ["x402", "ai-agent"],
    }

    print("1) Validate payload (free)")
    validation = validate_payload(args.base_url, listing_payload)
    normalized = validation.get("normalized") or {}
    print(json.dumps({"ok": True, "normalized": normalized}, indent=2))

    print("\n2) Sandbox rehearsal (HTTP 402 -> retry with simulated PAYMENT-SIGNATURE)")
    out = sandbox_rehearsal(args.base_url, normalized or listing_payload)
    print(json.dumps(out, indent=2))

    print(
        "\nNext: production submission requires a REAL x402 client and payment signature.\n"
        "See https://ontarioprotocol.com/docs/x402-service-listing-quickstart"
    )
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except KeyboardInterrupt:
        sys.stderr.write("Interrupted.\n")
        raise SystemExit(130)
