"""Low-level HTTP transport — wraps httpx and maps status codes to exceptions."""

from __future__ import annotations

import time
from typing import Any, Dict, Optional

import httpx

from fireplace_gg.exceptions import (
    AuthenticationError,
    BadRequestError,
    FireplaceError,
    ForbiddenError,
    RateLimitError,
    ServerError,
)

_BASE_URL = "https://data.fireplace.gg/api/v1/external"
_DEFAULT_TIMEOUT = 30.0


class Transport:
    """Synchronous HTTP transport for the Fireplace REST API."""

    def __init__(
        self,
        api_key: str,
        api_secret: str,
        *,
        base_url: str = _BASE_URL,
        timeout: float = _DEFAULT_TIMEOUT,
        trading_token: Optional[str] = None,
    ) -> None:
        self._base_url = base_url.rstrip("/")
        self._api_key = api_key
        self._api_secret = api_secret
        self._trading_token = trading_token
        self._client = httpx.Client(
            base_url=self._base_url,
            headers={
                "X-API-Key": api_key,
                "X-API-Secret": api_secret,
            },
            timeout=timeout,
        )
        self.rate_limit: Optional[int] = None
        self.rate_remaining: Optional[int] = None
        self.rate_reset: Optional[float] = None

    def _trading_headers(self) -> Dict[str, str]:
        """Return auth headers for the Trading API (Bearer token)."""
        if not self._trading_token:
            raise AuthenticationError(
                "A trading_token is required for Trading API endpoints. "
                "Pass it to Fireplace() or set FIREPLACE_TRADING_TOKEN.",
                status_code=401,
            )
        return {
            "Authorization": f"Bearer {self._trading_token}",
            "Content-Type": "application/json",
        }

    def _parse_response(self, resp: httpx.Response) -> Any:
        self.rate_limit = _int_header(resp, "X-RateLimit-Limit")
        self.rate_remaining = _int_header(resp, "X-RateLimit-Remaining")
        self.rate_reset = _float_header(resp, "X-RateLimit-Reset")
        if resp.status_code == 200:
            body = resp.json()
            if isinstance(body, dict) and body.get("success") is True:
                return body.get("data", body)
            return body
        _raise_for_status(resp)

    def get(
        self,
        path: str,
        params: Optional[Dict[str, Any]] = None,
        *,
        headers: Optional[Dict[str, str]] = None,
    ) -> Any:
        """Send a GET request and return the ``data`` payload."""
        if params:
            params = {k: v for k, v in params.items() if v is not None}
        resp = self._client.get(path, params=params, headers=headers)
        return self._parse_response(resp)

    def post(
        self,
        path: str,
        payload: Optional[Dict[str, Any]] = None,
        *,
        headers: Optional[Dict[str, str]] = None,
    ) -> Any:
        """Send a POST request with a JSON body and return the ``data`` payload."""
        resp = self._client.post(path, json=payload, headers=headers)
        return self._parse_response(resp)

    def close(self) -> None:
        self._client.close()

    def __enter__(self) -> "Transport":
        return self

    def __exit__(self, *exc: Any) -> None:
        self.close()


def _int_header(resp: httpx.Response, name: str) -> Optional[int]:
    val = resp.headers.get(name)
    if val is not None:
        try:
            return int(val)
        except ValueError:
            pass
    return None


def _float_header(resp: httpx.Response, name: str) -> Optional[float]:
    val = resp.headers.get(name)
    if val is not None:
        try:
            return float(val)
        except ValueError:
            pass
    return None


def _raise_for_status(resp: httpx.Response) -> None:
    body: Any = None
    try:
        body = resp.json()
    except Exception:
        body = resp.text
    message = ""
    if isinstance(body, dict):
        message = body.get("message", "") or ""
    if not message:
        message = f"HTTP {resp.status_code}"
    code = resp.status_code
    if code == 400:
        raise BadRequestError(message, status_code=code, response_body=body)
    if code == 401:
        raise AuthenticationError(message, status_code=code, response_body=body)
    if code == 403:
        raise ForbiddenError(message, status_code=code, response_body=body)
    if code == 429:
        retry_after: Optional[float] = None
        reset_ts = _float_header(resp, "X-RateLimit-Reset")
        if reset_ts is not None:
            retry_after = max(0.0, reset_ts - time.time())
        raise RateLimitError(message, status_code=code, response_body=body, retry_after=retry_after)
    if 500 <= code < 600:
        raise ServerError(message, status_code=code, response_body=body)
    raise FireplaceError(message, status_code=code, response_body=body)
