from __future__ import annotations

from typing import Any

import httpx

from .auth import ClientCredentialsAuth
from .config import (
    DEFAULT_API_URL,
    DEFAULT_AUTH0_AUDIENCE,
    DEFAULT_AUTH0_DOMAIN,
    load_config,
)
from .errors import ApiError, NotFoundError
from .models import Model, User
from .transport import HttpxSyncTransport, Transport


def _normalize_base_url(base_url: str) -> str:
    return base_url.rstrip("/")


class Client:
    def __init__(
        self,
        *,
        base_url: str | None = None,
        auth0_domain: str | None = None,
        auth0_audience: str | None = None,
        client_id: str,
        client_secret: str,
        timeout: float = 30.0,
        transport: Transport | None = None,
    ) -> None:
        config = load_config()

        resolved_base_url = base_url or config.base_url or DEFAULT_API_URL
        resolved_domain = auth0_domain or config.auth0_domain or DEFAULT_AUTH0_DOMAIN
        resolved_audience = (
            auth0_audience or config.auth0_audience or DEFAULT_AUTH0_AUDIENCE
        )

        self._base_url = _normalize_base_url(resolved_base_url)
        self._auth = ClientCredentialsAuth(
            auth0_domain=resolved_domain,
            audience=resolved_audience,
            client_id=client_id,
            client_secret=client_secret,
        )

        self._transport = transport or HttpxSyncTransport(
            client=httpx.Client(timeout=timeout)
        )

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

    def __enter__(self) -> Client:
        return self

    def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
        self.close()

    def get_models(self, *, time_horizon: str | None = None) -> list[Model]:
        params: dict[str, Any] = {}
        if time_horizon is not None:
            params["timeHorizon"] = time_horizon

        data = self._request_json("GET", "/model", params=params)
        return [Model.model_validate(item) for item in data]

    def get_model(self, model_id: str) -> Model:
        data = self._request_json("GET", f"/model/{model_id}")
        return Model.model_validate(data)

    def get_current_user(self) -> User:
        data = self._request_json("GET", "/user/current")
        return User.model_validate(data)

    def _request_json(
        self,
        method: str,
        path: str,
        *,
        params: dict[str, Any] | None = None,
    ) -> Any:
        url = f"{self._base_url}{path}"
        token = self._auth.get_access_token(self._transport)

        response = self._transport.request(
            method,
            url,
            headers={
                "authorization": f"Bearer {token}",
                "accept": "application/json",
            },
            params=params,
        )

        if response.status_code == 404:
            raise NotFoundError("resource not found")

        if response.status_code >= 400:
            body: Any | None
            try:
                body = response.json()
            except Exception:
                body = response.text

            raise ApiError(
                status_code=response.status_code,
                message="request failed",
                body=body,
            )

        try:
            return response.json()
        except ValueError as e:
            raise ApiError(
                status_code=response.status_code,
                message=f"invalid JSON response: {e}",
                body=response.text,
            ) from e
