"""Orrery x402 Python client — minimal, runnable, dependency-free.

Usage (no install — single file):

    python orrery_client.py decision will-bitcoin-hit-150k-by-june-30-2026
    python orrery_client.py snapshot will-bitcoin-hit-150k-by-june-30-2026
    python orrery_client.py why will-bitcoin-hit-150k-by-june-30-2026
    python orrery_client.py brief
    python orrery_client.py attention --limit 5

Every response is the standard x402 envelope:

    {
      "data": <endpoint-specific payload>,
      "meta": {
        "schema_version": "orrery.envelope.v1",
        "endpoint": "/api/x402/v1/...",
        "fetched_at": "2026-05-06T12:34:56Z",
        "snapshot_id": "snap_2026-05-06T12-34Z",
        "cache_seconds": 60,
        "payment_status": "preview" | "settled" | "missing" | "rate_limited",
        "usdc_per_call": 0.005,
        "sources": ["Polymarket Gamma", ...],
        "not_trade_advice": true
      }
    }

Paid endpoints accept ORRERY_API_KEY first, spending monthly API credits.
If no valid key/credits are available, they return HTTP 402. Set
ORRERY_X_PAYMENT_HEADER to your x402 payment proof and the server returns
`payment_status: "settled"` after the proof is verified.

Stability contract:
- Assert `meta["schema_version"] == "orrery.envelope.v1"`. Bump means
  breaking change to the envelope; check docs.
- Inner `data.schema_version` (when present) versions the payload
  shape independently — see /docs/agents.
"""

from __future__ import annotations

import argparse
import json
import os
import sys
import urllib.request
from typing import Any

ORRERY_BASE = os.environ.get("ORRERY_BASE", "https://orrery.me")
ORRERY_API_KEY = os.environ.get("ORRERY_API_KEY")
EXPECTED_ENVELOPE_VERSION = "orrery.envelope.v1"


class OrreryClient:
    """Single-file x402 client. No deps beyond stdlib `urllib`."""

    def __init__(
        self,
        base: str = ORRERY_BASE,
        api_key: str | None = None,
        x_payment: str | None = None,
    ):
        self.base = base.rstrip("/")
        self.api_key = api_key or ORRERY_API_KEY
        self.x_payment = x_payment or os.environ.get("ORRERY_X_PAYMENT_HEADER")

    def _get(self, path: str, query: dict[str, Any] | None = None) -> dict[str, Any]:
        if query:
            params = "&".join(f"{k}={v}" for k, v in query.items() if v is not None)
            path = f"{path}?{params}"
        url = f"{self.base}{path}"
        headers = {"Accept": "application/json"}
        if self.api_key:
            headers["X-Orrery-API-Key"] = self.api_key
        if self.x_payment:
            headers["X-PAYMENT"] = self.x_payment
        req = urllib.request.Request(url, headers=headers)
        with urllib.request.urlopen(req, timeout=10) as resp:
            body = resp.read().decode("utf-8")
        env = json.loads(body)
        # Stability assert — bail loudly on schema drift.
        meta = env.get("meta", {})
        actual = meta.get("schema_version")
        if actual != EXPECTED_ENVELOPE_VERSION:
            print(
                f"WARN  envelope schema_version mismatch: expected "
                f"{EXPECTED_ENVELOPE_VERSION!r}, got {actual!r}",
                file=sys.stderr,
            )
        return env

    # ---- Decision API (paid endpoints; require x402 payment proof in prod) ----

    def decision_market(self, slug: str) -> dict[str, Any]:
        return self._get(f"/api/x402/v1/decision/market/{slug}")

    def snapshot(self, slug: str) -> dict[str, Any]:
        return self._get(f"/api/x402/v1/markets/{slug}/snapshot")

    def why(self, slug: str) -> dict[str, Any]:
        return self._get(f"/api/x402/v1/markets/{slug}/why")

    def resolution_risk(self, slug: str) -> dict[str, Any]:
        return self._get(f"/api/x402/v1/markets/{slug}/resolution-risk")

    def attention(self, limit: int = 10) -> dict[str, Any]:
        return self._get("/api/x402/v1/decision/attention", {"limit": limit})

    def brief(self) -> dict[str, Any]:
        return self._get("/api/x402/v1/brief/today")

    def signals(self) -> dict[str, Any]:
        return self._get("/api/x402/v1/signals")


def main() -> int:
    ap = argparse.ArgumentParser(description="Orrery x402 client (runnable demo)")
    ap.add_argument(
        "command",
        choices=[
            "decision", "snapshot", "why", "resolution-risk",
            "attention", "brief", "signals",
        ],
    )
    ap.add_argument("slug", nargs="?", help="market slug (for market-scoped commands)")
    ap.add_argument("--limit", type=int, default=10)
    ap.add_argument("--base", default=ORRERY_BASE)
    args = ap.parse_args()

    client = OrreryClient(base=args.base)

    needs_slug = args.command in {"decision", "snapshot", "why", "resolution-risk"}
    if needs_slug and not args.slug:
        ap.error(f"command '{args.command}' requires a market slug")

    try:
        if args.command == "decision":
            assert args.slug
            res = client.decision_market(args.slug)
        elif args.command == "snapshot":
            assert args.slug
            res = client.snapshot(args.slug)
        elif args.command == "why":
            assert args.slug
            res = client.why(args.slug)
        elif args.command == "resolution-risk":
            assert args.slug
            res = client.resolution_risk(args.slug)
        elif args.command == "attention":
            res = client.attention(limit=args.limit)
        elif args.command == "brief":
            res = client.brief()
        elif args.command == "signals":
            res = client.signals()
        else:
            ap.error(f"unknown command: {args.command}")
            return 2
    except Exception as err:  # noqa: BLE001 — demo, surface every failure
        print(f"ERROR: {err}", file=sys.stderr)
        return 1

    # Pretty-print so the demo is useful out of the box.
    print(json.dumps(res, indent=2))
    return 0


if __name__ == "__main__":
    sys.exit(main())
