"""
MatchPrior API — zero-dependency Python client (standard library only).

    from matchprior import MatchPrior, MatchPriorError
    mp = MatchPrior(api_key="mp_live_…")
    page = mp.predictions(sport="football", limit=5)
    for p in mp.iterate("/v1/predictions", sport="tennis"):
        print(p["pick"], p["fairOdds"])

Docs: https://matchprior.com/docs.html  ·  OpenAPI: https://matchprior.com/openapi.yaml
"""
from __future__ import annotations

import json
import urllib.parse
import urllib.request
import urllib.error
from typing import Any, Dict, Iterator, Optional

__all__ = ["MatchPrior", "MatchPriorError"]

_DEFAULT_BASE = "https://api.matchprior.com"


class MatchPriorError(Exception):
    """A non-2xx API response. status == 429 means you are over your monthly quota
    (see `retry_after`, in seconds)."""

    def __init__(self, status: int, body: Any, retry_after: Optional[int] = None):
        self.status = status
        self.body = body
        self.retry_after = retry_after
        msg = f"MatchPrior API error {status}"
        if retry_after:
            msg += f" (retry after {retry_after}s)"
        super().__init__(msg)


class MatchPrior:
    def __init__(self, api_key: str, base_url: str = _DEFAULT_BASE, timeout: float = 30.0):
        if not api_key:
            raise ValueError("api_key is required")
        self._key = api_key
        self._base = base_url.rstrip("/")
        self._timeout = timeout

    def _get(self, path: str, **params: Any) -> Any:
        query = {k: v for k, v in params.items() if v is not None}
        url = self._base + path
        if query:
            url += "?" + urllib.parse.urlencode(query)
        req = urllib.request.Request(url, headers={"X-API-Key": self._key, "Accept": "application/json"})
        try:
            with urllib.request.urlopen(req, timeout=self._timeout) as resp:
                return json.loads(resp.read().decode("utf-8"))
        except urllib.error.HTTPError as e:
            raw = e.read().decode("utf-8", "replace")
            try:
                body = json.loads(raw)
            except ValueError:
                body = raw
            ra = e.headers.get("Retry-After")
            raise MatchPriorError(e.code, body, int(ra) if (ra and ra.isdigit()) else None) from None

    # --- endpoints ---
    def health(self) -> Dict[str, Any]:
        return self._get("/v1/health")

    def predictions(self, sport=None, league=None, date=None, limit=None, since=None) -> Dict[str, Any]:
        return self._get("/v1/predictions", sport=sport, league=league, date=date, limit=limit, since=since)

    def prediction(self, fixture_id: str) -> Dict[str, Any]:
        return self._get("/v1/predictions/" + urllib.parse.quote(fixture_id, safe=""))

    def fixtures(self, sport=None, league=None, date=None, limit=None, since=None) -> Dict[str, Any]:
        return self._get("/v1/fixtures", sport=sport, league=league, date=date, limit=limit, since=since)

    def ratings(self, sport=None, league=None, limit=None, since=None) -> Dict[str, Any]:
        """Dev tier and above."""
        return self._get("/v1/ratings", sport=sport, league=league, limit=limit, since=since)

    def accuracy(self, sport=None) -> Dict[str, Any]:
        return self._get("/v1/accuracy", sport=sport)

    def backtest(self, sport=None) -> Dict[str, Any]:
        """Pro tier and above."""
        return self._get("/v1/backtest", sport=sport)

    def iterate(self, path: str, **params: Any) -> Iterator[Dict[str, Any]]:
        """Yield every item across all pages of a paginated endpoint
        (/v1/predictions, /v1/fixtures, /v1/ratings)."""
        since = params.pop("since", None)
        while True:
            page = self._get(path, since=since, **params)
            for item in page.get("data", []):
                yield item
            since = page.get("page", {}).get("nextCursor")
            if not since:
                break
