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

from __future__ import annotations

from typing import Any
from urllib.parse import quote

import httpx

from rendshot._version import __version__
from rendshot.errors import RendshotError
from rendshot.types import (
    ImageMetadata,
    ImageResult,
    RenderImageOptions,
    ScreenshotUrlOptions,
    UsageResult,
)

_DEFAULT_BASE_URL = "https://api.rendshot.ai"
_TIMEOUT = httpx.Timeout(60.0, connect=10.0)


def _base_headers(api_key: str) -> dict[str, str]:
    return {
        "Authorization": f"Bearer {api_key}",
        "User-Agent": f"rendshot-python/{__version__}",
    }


def _handle_response(response: httpx.Response) -> Any:
    try:
        data = response.json()
    except (ValueError, httpx.DecodingError):
        if not response.is_success:
            raise RendshotError("UNKNOWN_ERROR", f"API error: {response.status_code}", response.status_code)
        raise ValueError(f"Unexpected non-JSON response (status {response.status_code})")

    if not response.is_success:
        err = data.get("error", {}) if isinstance(data, dict) else {}
        raise RendshotError(
            code=err.get("code", "UNKNOWN_ERROR"),
            message=err.get("message", f"API error: {response.status_code}"),
            status=err.get("status", response.status_code),
        )
    return data


class RendshotClient:
    """Synchronous RendShot API client.

    Usage::

        from rendshot import RendshotClient, RenderImageOptions

        client = RendshotClient(api_key="rs_live_...")
        result = client.render_image(RenderImageOptions(html="<h1>Hello</h1>"))
        print(result.url)
    """

    def __init__(self, api_key: str, *, base_url: str = _DEFAULT_BASE_URL) -> None:
        self._base_url = base_url.rstrip("/")
        self._client = httpx.Client(
            headers=_base_headers(api_key),
            timeout=_TIMEOUT,
        )

    def render_image(self, options: RenderImageOptions | dict) -> ImageResult:
        """Render HTML/CSS to an image."""
        body = options.to_dict() if isinstance(options, RenderImageOptions) else options
        data = self._post("/v1/image", body)
        return ImageResult.from_dict(data)

    def screenshot_url(self, options: ScreenshotUrlOptions | dict) -> ImageResult:
        """Take a screenshot of a URL."""
        body = options.to_dict() if isinstance(options, ScreenshotUrlOptions) else options
        data = self._post("/v1/screenshot", body)
        return ImageResult.from_dict(data)

    def get_usage(self) -> UsageResult:
        """Get current month's usage."""
        data = self._get("/v1/usage")
        return UsageResult.from_dict(data)

    def get_image(self, image_id: str) -> ImageMetadata:
        """Get metadata for a specific image."""
        data = self._get(f"/v1/image/{quote(image_id, safe='')}")
        return ImageMetadata.from_dict(data)

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

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

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

    def _post(self, path: str, body: dict) -> Any:
        resp = self._client.post(f"{self._base_url}{path}", json=body)
        return _handle_response(resp)

    def _get(self, path: str) -> Any:
        resp = self._client.get(f"{self._base_url}{path}")
        return _handle_response(resp)


class AsyncRendshotClient:
    """Async RendShot API client.

    Usage::

        from rendshot import AsyncRendshotClient, RenderImageOptions

        async with AsyncRendshotClient(api_key="rs_live_...") as client:
            result = await client.render_image(RenderImageOptions(html="<h1>Hello</h1>"))
            print(result.url)
    """

    def __init__(self, api_key: str, *, base_url: str = _DEFAULT_BASE_URL) -> None:
        self._base_url = base_url.rstrip("/")
        self._client = httpx.AsyncClient(
            headers=_base_headers(api_key),
            timeout=_TIMEOUT,
        )

    async def render_image(self, options: RenderImageOptions | dict) -> ImageResult:
        """Render HTML/CSS to an image."""
        body = options.to_dict() if isinstance(options, RenderImageOptions) else options
        data = await self._post("/v1/image", body)
        return ImageResult.from_dict(data)

    async def screenshot_url(self, options: ScreenshotUrlOptions | dict) -> ImageResult:
        """Take a screenshot of a URL."""
        body = options.to_dict() if isinstance(options, ScreenshotUrlOptions) else options
        data = await self._post("/v1/screenshot", body)
        return ImageResult.from_dict(data)

    async def get_usage(self) -> UsageResult:
        """Get current month's usage."""
        data = await self._get("/v1/usage")
        return UsageResult.from_dict(data)

    async def get_image(self, image_id: str) -> ImageMetadata:
        """Get metadata for a specific image."""
        data = await self._get(f"/v1/image/{quote(image_id, safe='')}")
        return ImageMetadata.from_dict(data)

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

    async def __aenter__(self) -> "AsyncRendshotClient":
        return self

    async def __aexit__(self, *args: Any) -> None:
        await self.close()

    async def _post(self, path: str, body: dict) -> Any:
        resp = await self._client.post(f"{self._base_url}{path}", json=body)
        return _handle_response(resp)

    async def _get(self, path: str) -> Any:
        resp = await self._client.get(f"{self._base_url}{path}")
        return _handle_response(resp)
