"""Top-level Tetrate Agent Router SDK client (Connect-JSON over httpx)."""

from __future__ import annotations

import os
from dataclasses import dataclass

import httpx

from . import errors
from ._transport import RetryPolicy, Transport, TransportConfig
from . import _services as svc
from . import _services_gen as svc_gen

_DEFAULT_BASE_URL = "https://api.tetrate.ai"
_USER_AGENT = "tetrate-agentrouter-sdk-py/0.1.0"

_STANDARD_API_KEY_ENV = "AGENTROUTER_API_KEY"
_STANDARD_BASE_URL_ENV = "AGENTROUTER_BASE_URL"
_STANDARD_SESSION_TOKEN_ENV = "AGENTROUTER_SESSION_TOKEN"


@dataclass
class Endpoints:
    """Per-service base URLs. Empty fields fall back to the client base URL.

    Local dev binds each service to its own port; production behind one
    facade leaves these empty and uses ``base_url``.
    """

    identity: str = ""
    tenancy: str = ""
    catalog: str = ""
    aikeys: str = ""
    inference: str = ""
    clients: str = ""
    telemetry: str = ""
    mcp: str = ""


class Client:
    """SDK entry point. Construct once and reuse; close when done (or use a
    ``with`` block).

    Auth: ``api_key`` sets ``Authorization: Bearer <key>`` (the inference
    ext-authz facade requires the Bearer form; management services accept it
    and also read ``X-Api-Key``) plus ``X-Api-Key``; ``session_token`` sets
    ``Authorization: Bearer ...``. Mutually exclusive.
    """

    def __init__(
        self,
        *,
        base_url: str | None = None,
        endpoints: Endpoints | None = None,
        api_key: str | None = None,
        session_token: str | None = None,
        retry: RetryPolicy | None = None,
        timeout_s: float = 30.0,
        user_agent: str | None = None,
        httpx_client: httpx.Client | None = None,
    ):
        if api_key and session_token:
            raise errors.AgentrouterError(
                errors.INVALID_ARGUMENT,
                "api_key and session_token are mutually exclusive",
            )
        # Base URL precedence: explicit arg, then AGENTROUTER_BASE_URL env, then
        # the public production URL.
        self._base_url = (
            base_url or os.environ.get(_STANDARD_BASE_URL_ENV) or _DEFAULT_BASE_URL
        ).rstrip("/")
        self._endpoints = endpoints or Endpoints()
        self._retry = retry or RetryPolicy()
        self._timeout = timeout_s
        self._ua = f"{_USER_AGENT}{' ' + user_agent if user_agent else ''}"

        headers: dict[str, str] = {"User-Agent": self._ua}
        if api_key:
            headers["Authorization"] = f"Bearer {api_key}"
            headers["X-Api-Key"] = api_key
        elif session_token:
            headers["Authorization"] = f"Bearer {session_token}"
        self._headers = headers

        self._owns_client = httpx_client is None
        self._client = httpx_client or httpx.Client()

        self._transports: dict[str, Transport] = {}

        # Service handles.
        self.me = svc.MeHandle(self._transport_for("identity"))
        self.identity = svc.IdentityHandle(self._transport_for("identity"))
        self.customers = svc.CustomersHandle(self._transport_for("tenancy"))
        self.tenancy = self.customers
        self.projects = svc.ProjectsHandle(self._transport_for("tenancy"))
        self.catalog = svc.CatalogHandle(self._transport_for("catalog"))
        self.aikeys = svc.AIKeysHandle(self._transport_for("aikeys"))
        self.inference = svc.InferenceHandle(self._transport_for("inference"))
        self.clients = svc.ClientsHandle(self._transport_for("clients"))
        self.telemetry = svc.TelemetryHandle(self._transport_for("telemetry"))
        self.mcp = svc.MCPHandle(
            svc.MCPProfilesHandle(self._transport_for("mcp")),
            svc.MCPCatalogHandle(self._transport_for("mcp")),
        )

        # Proto-driven handles for services with no hand-written wrapper above.
        # The block below is owned by tools/sdkgen; edit the generator, not here.
        # BEGIN sdkgen generated handles -- managed by tools/sdkgen, do not edit by hand
        self.admin = svc_gen.AdminGenHandle(self._transport_for(""))
        self.configurator = svc_gen.ConfiguratorGenHandle(self._transport_for(""))
        self.secret = svc_gen.SecretGenHandle(self._transport_for(""))
        self.api_keys = svc_gen.ApiKeysGenHandle(self._transport_for(""))
        self.customer_request_logs = svc_gen.CustomerRequestLogsGenHandle(self._transport_for(""))
        self.request_logs = svc_gen.RequestLogsGenHandle(self._transport_for(""))
        self.auth = svc_gen.AuthGenHandle(self._transport_for(""))
        self.tokens = svc_gen.TokensGenHandle(self._transport_for(""))
        # END sdkgen generated handles

    @classmethod
    def from_env(cls, env_var: str = _STANDARD_API_KEY_ENV, **kwargs) -> "Client":
        """Construct a Client with the API key read from ``env_var``."""
        return cls(api_key=os.environ.get(env_var), **kwargs)

    @classmethod
    def from_standard_env(cls, **kwargs) -> "Client":
        """Construct a Client with the API key from the standard
        AGENTROUTER_API_KEY environment variable."""
        return cls.from_env(_STANDARD_API_KEY_ENV, **kwargs)

    def _transport_for(self, endpoint_key: str) -> Transport:
        url = getattr(self._endpoints, endpoint_key, "") or self._base_url
        t = self._transports.get(url)
        if t is None:
            cfg = TransportConfig(
                base_url=url.rstrip("/"),
                headers=self._headers,
                timeout_s=self._timeout,
                retry=self._retry,
            )
            t = Transport(cfg, self._client)
            self._transports[url] = t
        return t

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

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

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