"""Small dependency-free Python client for the public IBANgen API."""
from __future__ import annotations

import json
import urllib.error
import urllib.request


class IBANgenError(RuntimeError):
    """Raised for non-2xx API responses; ``status`` and ``payload`` are stable."""

    def __init__(self, status: int, payload: dict):
        self.status = status
        self.payload = payload
        super().__init__(f"IBANgen API error {status}: {payload.get('error', 'unknown_error')}")


class IBANgenClient:
    def __init__(self, api_key: str, base_url: str = "https://ibangen.com/api/v1"):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key

    def _request(self, path: str, payload: dict):
        body = json.dumps(payload).encode("utf-8")
        request = urllib.request.Request(
            self.base_url + path,
            data=body,
            headers={"X-API-Key": self.api_key, "Content-Type": "application/json"},
            method="POST",
        )
        return self._send(request)

    def _get(self, path: str):
        request = urllib.request.Request(
            self.base_url + path,
            headers={"X-API-Key": self.api_key},
            method="GET",
        )
        return self._send(request)

    def _send(self, request):
        try:
            with urllib.request.urlopen(request, timeout=30) as response:
                status, raw = response.status, response.read()
        except urllib.error.HTTPError as exc:
            status, raw = exc.code, exc.read()
        payload = json.loads(raw.decode("utf-8")) if raw else {}
        if status >= 400:
            raise IBANgenError(status, payload)
        return payload

    def generate(self, country: str, quantity: int = 1, seed: str | None = None, targeting: dict | None = None):
        payload = {"country": country, "quantity": quantity}
        if seed is not None:
            payload["seed"] = seed
        if targeting:
            payload["targeting"] = targeting
        return self._request("/generate", payload)

    def bulk_dataset(
        self,
        country: str,
        quantity: int = 10,
        seed: str | None = None,
        *,
        include_bank_details: bool = True,
        include_profiles: bool = False,
    ):
        """Build a paid, reproducible QA dataset as JSON."""
        payload = {
            "country": country,
            "quantity": quantity,
            "output": "json",
            "include_bank_details": include_bank_details,
            "include_profiles": include_profiles,
        }
        if seed is not None:
            payload["seed"] = seed
        return self._request("/bulk-dataset", payload)

    def validate(self, ibans):
        return self._request("/validate", {"ibans": list(ibans)})

    def fixture_manifest_spec(self):
        """Read the public manifest contract without spending API credits."""
        request = urllib.request.Request(self.base_url + "/fixture-manifest/spec", method="GET")
        return self._send(request)

    def compile_fixture_manifest(self, fixtures, *, fixture_id=None, seed=None,
                                  registry_snapshot=None, sources=None,
                                  schemas=None, expected_outcomes=None):
        payload = {"fixtures": list(fixtures)}
        optional = {
            "fixture_id": fixture_id,
            "seed": seed,
            "registry_snapshot": registry_snapshot,
            "sources": sources,
            "schemas": schemas,
            "expected_outcomes": expected_outcomes,
        }
        payload.update({key: value for key, value in optional.items() if value is not None})
        return self._request("/fixture-manifest/compile", payload)

    def payment_scenarios_catalog(self):
        """Read the supported synthetic payment scenario catalog."""
        request = urllib.request.Request(self.base_url + "/payment-scenarios/catalog", method="GET")
        return self._send(request)

    def compile_payment_scenario(self, **scenario):
        return self._request("/payment-scenarios/compile", scenario)
