"""HTTP client for ImagineAPI."""

from types import TracebackType
from typing import Self

import httpx

from .config import settings
from .models import ImageData, ImageListResponse, ImageRequest, ImageResponse


class ImagineAPIError(Exception):
    """Error from ImagineAPI.

    Attributes:
        status_code: HTTP status code if available.
        response_body: Raw response body for debugging.
    """

    def __init__(
        self,
        message: str,
        status_code: int | None = None,
        response_body: str | None = None,
    ) -> None:
        super().__init__(message)
        self.status_code = status_code
        self.response_body = response_body

    @classmethod
    def from_response(cls, action: str, response: httpx.Response) -> "ImagineAPIError":
        """Create error from HTTP response."""
        return cls(
            message=f"Failed to {action}: HTTP {response.status_code}",
            status_code=response.status_code,
            response_body=response.text,
        )


class ImagineAPIClient:
    """Async HTTP client for ImagineAPI.

    Example:
        async with ImagineAPIClient() as client:
            image = await client.create_image(ImageRequest(prompt="test"))
    """

    def __init__(self, base_url: str | None = None, token: str | None = None) -> None:
        self.base_url = (base_url or settings.url).rstrip("/")
        self.token = token or settings.token
        if not self.token:
            raise ValueError(
                "ImagineAPI token is required. "
                "Set IMAGINEAPI_TOKEN environment variable or pass token parameter."
            )
        self._client: httpx.AsyncClient | None = None

    async def __aenter__(self) -> Self:
        """Enter async context manager."""
        _ = self.client
        return self

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

    @property
    def client(self) -> httpx.AsyncClient:
        """Get or create the HTTP client."""
        if self._client is None:
            self._client = httpx.AsyncClient(
                base_url=self.base_url,
                headers={
                    "Authorization": f"Bearer {self.token}",
                    "Content-Type": "application/json",
                },
                timeout=30.0,
            )
        return self._client

    async def close(self) -> None:
        """Close the HTTP client."""
        if self._client is not None:
            await self._client.aclose()
            self._client = None

    def _check_success(self, response: httpx.Response, action: str) -> None:
        """Check if response is successful (2xx status code)."""
        if not response.is_success:
            raise ImagineAPIError.from_response(action, response)

    async def create_image(self, request: ImageRequest) -> ImageData:
        """Create a new image generation request."""
        response = await self.client.post(
            "/items/images",
            json=request.model_dump(exclude_none=True),
        )
        self._check_success(response, "create image")
        return ImageResponse.model_validate(response.json()).data

    async def get_image(self, image_id: str) -> ImageData:
        """Get image status by ID."""
        response = await self.client.get(f"/items/images/{image_id}")
        if response.status_code == 404:
            raise ImagineAPIError(
                message=f"Image not found: {image_id}",
                status_code=404,
            )
        self._check_success(response, "get image")
        return ImageResponse.model_validate(response.json()).data

    async def list_images(
        self,
        limit: int = 25,
        offset: int = 0,
        status: str | None = None,
    ) -> ImageListResponse:
        """List images with optional filtering."""
        params: dict[str, str | int] = {"limit": limit, "offset": offset}
        if status:
            params["filter[status][_eq]"] = status
        response = await self.client.get("/items/images", params=params)
        self._check_success(response, "list images")
        return ImageListResponse.model_validate(response.json())

    async def download_image(self, url: str) -> bytes:
        """Download image from URL."""
        async with httpx.AsyncClient(timeout=60.0) as download_client:
            response = await download_client.get(url)
            if not response.is_success:
                raise ImagineAPIError(
                    message=f"Failed to download image from {url}: HTTP {response.status_code}",
                    status_code=response.status_code,
                )
            return response.content
