from __future__ import annotations

import base64
import json
from typing import Any

import httpx

from .config import A402Config


class FacilitatorError(Exception):
    def __init__(self, status: int, body: Any) -> None:
        self.status = status
        self.body = body
        super().__init__(f"facilitator {status}: {body}")


def _client(cfg: A402Config) -> httpx.Client:
    return httpx.Client(timeout=cfg.timeout)


def issue_challenge(
    cfg: A402Config,
    *,
    price_usd: float,
    resource: str,
    description: str,
) -> dict[str, Any]:
    url = cfg.facilitator_url.rstrip("/") + "/facilitator/challenge"
    with _client(cfg) as c:
        resp = c.post(
            url,
            headers={"X-API-KEY": cfg.api_key},
            json={"priceUsd": price_usd, "resource": resource, "description": description},
        )
    if resp.status_code != 200:
        raise FacilitatorError(resp.status_code, resp.text)
    return resp.json()


def settle(cfg: A402Config, payment_payload: dict[str, Any]) -> tuple[int, dict[str, Any]]:
    """Call /facilitator/settle. Returns (status_code, body_dict)."""
    url = cfg.facilitator_url.rstrip("/") + "/facilitator/settle"
    # Always force the seller's key, overwriting any value a paying client tried
    # to inject — the seller owns the key, the client never sets it.
    payload = {**payment_payload, "apiKeyToken": cfg.api_key}

    with _client(cfg) as c:
        resp = c.post(url, json=payload)

    try:
        body = resp.json()
    except Exception:
        body = {"error": resp.text}

    return resp.status_code, body


def decode_x_payment(header_value: str) -> dict[str, Any]:
    try:
        raw = base64.b64decode(header_value)
        return json.loads(raw)
    except Exception:
        # Facilitator also accepts raw JSON (see decodeXPayment in x402.go)
        return json.loads(header_value)


def encode_response(obj: dict[str, Any]) -> str:
    return base64.b64encode(json.dumps(obj).encode()).decode()
