"""Sync and async HTTP clients for the murmr API."""

from __future__ import annotations

from types import TracebackType
from typing import Any

import httpx

from murmr._errors import MurmrError

DEFAULT_BASE_URL = "https://api.murmr.dev"
DEFAULT_TIMEOUT = 300.0


class AsyncMurmrClient:
    """Async client for the murmr TTS API.

    Usage::

        async with AsyncMurmrClient(api_key="murmr_sk_...") as client:
            wav = await client.voices.design(input="Hello", voice_description="A calm voice")
    """

    def __init__(
        self,
        *,
        api_key: str,
        base_url: str = DEFAULT_BASE_URL,
        timeout: float = DEFAULT_TIMEOUT,
    ) -> None:
        if not api_key:
            raise MurmrError("api_key is required")

        self._api_key = api_key
        self._base_url = base_url.rstrip("/")
        self._timeout = timeout
        self._http = httpx.AsyncClient(timeout=timeout)

        # Lazy imports to avoid circular dependency
        from murmr.resources.jobs import AsyncJobs
        from murmr.resources.speech import AsyncSpeech
        from murmr.resources.voices import AsyncVoices

        self.speech = AsyncSpeech(self)
        self.voices = AsyncVoices(self)
        self.jobs = AsyncJobs(self)

    async def __aenter__(self) -> AsyncMurmrClient:
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        await self.close()

    async def close(self) -> None:
        """Close the underlying HTTP client."""
        await self._http.aclose()

    async def request(
        self,
        method: str,
        path: str,
        *,
        json: dict[str, Any] | None = None,
        headers: dict[str, str] | None = None,
        stream: bool = False,
    ) -> httpx.Response:
        """Make an authenticated API request. Internal use by resource classes."""
        url = f"{self._base_url}{path}"
        request_headers = {"Authorization": f"Bearer {self._api_key}"}
        if headers:
            request_headers.update(headers)

        if stream:
            request_headers["Accept"] = "text/event-stream"
            req = self._http.build_request(
                method, url, json=json, headers=request_headers
            )
            response = await self._http.send(req, stream=True)
        else:
            response = await self._http.request(
                method, url, json=json, headers=request_headers
            )

        if response.status_code >= 400:
            await _raise_for_status(response)

        return response


class MurmrClient:
    """Sync client for the murmr TTS API.

    Usage::

        with MurmrClient(api_key="murmr_sk_...") as client:
            wav = client.voices.design(input="Hello", voice_description="A calm voice")
    """

    def __init__(
        self,
        *,
        api_key: str,
        base_url: str = DEFAULT_BASE_URL,
        timeout: float = DEFAULT_TIMEOUT,
    ) -> None:
        if not api_key:
            raise MurmrError("api_key is required")

        self._api_key = api_key
        self._base_url = base_url.rstrip("/")
        self._timeout = timeout
        self._http = httpx.Client(timeout=timeout)

        from murmr.resources.jobs import SyncJobs
        from murmr.resources.speech import SyncSpeech
        from murmr.resources.voices import SyncVoices

        self.speech = SyncSpeech(self)
        self.voices = SyncVoices(self)
        self.jobs = SyncJobs(self)

    def __enter__(self) -> MurmrClient:
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        self.close()

    def close(self) -> None:
        """Close the underlying HTTP client."""
        self._http.close()

    def request(
        self,
        method: str,
        path: str,
        *,
        json: dict[str, Any] | None = None,
        headers: dict[str, str] | None = None,
        stream: bool = False,
    ) -> httpx.Response:
        """Make an authenticated API request. Internal use by resource classes."""
        url = f"{self._base_url}{path}"
        request_headers = {"Authorization": f"Bearer {self._api_key}"}
        if headers:
            request_headers.update(headers)

        if stream:
            request_headers["Accept"] = "text/event-stream"
            req = self._http.build_request(
                method, url, json=json, headers=request_headers
            )
            response = self._http.send(req, stream=True)
        else:
            response = self._http.request(
                method, url, json=json, headers=request_headers
            )

        if response.status_code >= 400:
            _raise_for_status_sync(response)

        return response


async def _raise_for_status(response: httpx.Response) -> None:
    """Parse error body and raise MurmrError for async responses."""
    body = await response.aread()
    _parse_and_raise(response, body)


def _raise_for_status_sync(response: httpx.Response) -> None:
    """Parse error body and raise MurmrError for sync responses."""
    body = response.read()
    _parse_and_raise(response, body)


def _parse_and_raise(response: httpx.Response, body: bytes) -> None:
    """Parse OpenAI-format error body and raise MurmrError."""
    import json as json_mod

    message = f"Request failed with status {response.status_code}"
    error_type: str | None = None
    error_code: str | None = None
    concurrent_limit: int | None = None
    concurrent_active: int | None = None

    try:
        error_body = json_mod.loads(body)
        error_field = error_body.get("error")
        if isinstance(error_field, dict):
            message = error_field.get("message", message)
            error_type = error_field.get("type")
            error_code = error_field.get("code")
        elif isinstance(error_field, str):
            message = error_field
    except json_mod.JSONDecodeError:
        pass

    # Concurrent info from headers
    limit_header = response.headers.get("X-Concurrent-Limit")
    active_header = response.headers.get("X-Concurrent-Active")
    if limit_header:
        try:
            concurrent_limit = int(limit_header)
        except ValueError:
            pass
    if active_header:
        try:
            concurrent_active = int(active_header)
        except ValueError:
            pass

    raise MurmrError(
        message,
        status=response.status_code,
        code=error_code,
        type=error_type,
        concurrent_limit=concurrent_limit,
        concurrent_active=concurrent_active,
    )
