"""OGImage generate/list/get/poll mixins."""

from __future__ import annotations

import asyncio
import logging
import time

from ..._api.generated.ogimage.ogimage__api__ogimage.models import (
    OGImageGenerateRequest,
    OGImageResponse,
    OGImageAsyncResponse,
    OGImageStatus,
    PaginatedOGImageResponseList,
)
from ..._api.generated.ogimage.enums import (
    OGImageGenerateRequestMethod,
    OGImageGenerateRequestSize,
    OGImageGenerateRequestStyle,
    OGImageResponseStatus,
)
from ...exceptions import api_error_handler, async_api_error_handler, APIError

logger = logging.getLogger(__name__)

_GENERATE_PARAMS = (
    "description", "locale", "page_url", "method", "preset_slug",
    "bg_color", "bg_color2", "text_color", "accent_color", "style",
    "llm_model", "image_model", "style_hint", "size",
)


def _build_generate_kwargs(title: str, **kw) -> dict:
    kwargs: dict = {"title": title}
    for k in _GENERATE_PARAMS:
        v = kw.get(k)
        if v is not None:
            kwargs[k] = v
    return kwargs


class _GenerateMixin:
    """Sync generate/list/get/poll methods."""

    @api_error_handler
    def generate(
        self,
        title: str,
        *,
        description: str | None = None,
        locale: str | None = None,
        page_url: str | None = None,
        method: OGImageGenerateRequestMethod | str | None = None,
        preset_slug: str | None = None,
        bg_color: str | None = None,
        bg_color2: str | None = None,
        text_color: str | None = None,
        accent_color: str | None = None,
        style: OGImageGenerateRequestStyle | str | None = None,
        llm_model: str | None = None,
        image_model: str | None = None,
        style_hint: str | None = None,
        size: OGImageGenerateRequestSize | str | None = None,
    ) -> OGImageResponse:
        """Generate an OG image synchronously (waits for completion).

        Args:
            title: Main title text (required, max 300 chars)
            description: Optional subtitle/description (max 600 chars)
            locale: Language locale (e.g. "en", "ko", "ja")
            page_url: Source page URL for metadata (max 2048 chars)
            method: Generation method ("direct" for PicTex, "llm" for AI)
            preset_slug: Named preset to use (overrides style defaults)
            bg_color: Background color hex (e.g. "#1a1a2e")
            bg_color2: Gradient end color hex
            text_color: Text color hex
            accent_color: Accent/highlight color hex
            style: Style preset ("dark" or "light")
            llm_model: LLM model override for AI-generated images
            image_model: Image model override
            style_hint: Style hint for AI generation
            size: Output size ("1200x630", "1200x600", "800x418", "1200x1200")

        Returns:
            OGImageResponse with image_url, status, cost_usd, etc.
        """
        logger.debug("OGImage generate: title=%s, style=%s", title[:50], style)
        kwargs = _build_generate_kwargs(
            title, description=description, locale=locale, page_url=page_url,
            method=method, preset_slug=preset_slug, bg_color=bg_color,
            bg_color2=bg_color2, text_color=text_color, accent_color=accent_color,
            style=style, llm_model=llm_model, image_model=image_model,
            style_hint=style_hint, size=size,
        )
        result = self._api.generate_create(OGImageGenerateRequest(**kwargs))
        logger.info("OGImage generated: id=%s, cost=$%s", result.id, result.cost_usd or "0")
        return result

    @api_error_handler
    def generate_async(
        self,
        title: str,
        *,
        description: str | None = None,
        locale: str | None = None,
        page_url: str | None = None,
        method: OGImageGenerateRequestMethod | str | None = None,
        preset_slug: str | None = None,
        bg_color: str | None = None,
        bg_color2: str | None = None,
        text_color: str | None = None,
        accent_color: str | None = None,
        style: OGImageGenerateRequestStyle | str | None = None,
        llm_model: str | None = None,
        image_model: str | None = None,
        style_hint: str | None = None,
        size: OGImageGenerateRequestSize | str | None = None,
    ) -> OGImageAsyncResponse:
        """Start async OG image generation (returns immediately).

        Returns:
            OGImageAsyncResponse with id and job_id for polling.

        Example:
            job = client.ogimage.generate_async(title="My Post")
            result = client.ogimage.wait_for_completion(job.id)
        """
        logger.debug("OGImage generate async: title=%s", title[:50])
        kwargs = _build_generate_kwargs(
            title, description=description, locale=locale, page_url=page_url,
            method=method, preset_slug=preset_slug, bg_color=bg_color,
            bg_color2=bg_color2, text_color=text_color, accent_color=accent_color,
            style=style, llm_model=llm_model, image_model=image_model,
            style_hint=style_hint, size=size,
        )
        result = self._api.generate_async_create(OGImageGenerateRequest(**kwargs))
        logger.info("OGImage queued: id=%s, job_id=%s", result.id, result.job_id)
        return result

    @api_error_handler
    def get_status(self, ogimage_id: int | str) -> OGImageStatus:
        """Get status of an OG image job."""
        return self._api.status_retrieve(str(ogimage_id))

    @api_error_handler
    def get(self, ogimage_id: int | str) -> OGImageResponse:
        """Get full details of an OGImage record."""
        return self._api.retrieve(str(ogimage_id))

    @api_error_handler
    def list(
        self,
        *,
        ordering: str | None = None,
        search: str | None = None,
        page: int | None = None,
        page_size: int | None = None,
    ) -> PaginatedOGImageResponseList:
        """List OG image generation history."""
        return self._api.list(
            ordering=ordering, search=search, page=page, page_size=page_size,
        )

    def wait_for_completion(
        self,
        ogimage_id: int | str,
        *,
        timeout: float = 300.0,
        poll_interval: float = 2.0,
    ) -> OGImageResponse:
        """Wait for async OG image generation to complete.

        Args:
            ogimage_id: ID of the OGImage record to wait for
            timeout: Maximum time to wait in seconds (default: 300)
            poll_interval: Time between status checks in seconds (default: 2)

        Returns:
            Completed OGImageResponse with image_url

        Raises:
            TimeoutError: If generation doesn't complete within timeout
            APIError: If generation fails
        """
        start_time = time.monotonic()
        while True:
            elapsed = time.monotonic() - start_time
            if elapsed > timeout:
                raise TimeoutError(f"OGImage {ogimage_id} did not complete within {timeout}s")
            status = self.get_status(ogimage_id)
            logger.debug("OGImage %s status: %s (elapsed: %.1fs)", ogimage_id, status.status, elapsed)
            if status.status == OGImageResponseStatus.COMPLETED:
                return self.get(ogimage_id)
            elif status.status == OGImageResponseStatus.FAILED:
                raise APIError(getattr(status, "error_message", None) or "OGImage generation failed")
            time.sleep(poll_interval)


class _AsyncGenerateMixin:
    """Async generate/list/get/poll methods."""

    @async_api_error_handler
    async def generate(
        self,
        title: str,
        *,
        description: str | None = None,
        locale: str | None = None,
        page_url: str | None = None,
        method: OGImageGenerateRequestMethod | str | None = None,
        preset_slug: str | None = None,
        bg_color: str | None = None,
        bg_color2: str | None = None,
        text_color: str | None = None,
        accent_color: str | None = None,
        style: OGImageGenerateRequestStyle | str | None = None,
        llm_model: str | None = None,
        image_model: str | None = None,
        style_hint: str | None = None,
        size: OGImageGenerateRequestSize | str | None = None,
    ) -> OGImageResponse:
        """Generate an OG image (async, waits for completion)."""
        logger.debug("OGImage generate: title=%s, style=%s", title[:50], style)
        kwargs = _build_generate_kwargs(
            title, description=description, locale=locale, page_url=page_url,
            method=method, preset_slug=preset_slug, bg_color=bg_color,
            bg_color2=bg_color2, text_color=text_color, accent_color=accent_color,
            style=style, llm_model=llm_model, image_model=image_model,
            style_hint=style_hint, size=size,
        )
        result = await self._api.generate_create(OGImageGenerateRequest(**kwargs))
        logger.info("OGImage generated: id=%s, cost=$%s", result.id, result.cost_usd or "0")
        return result

    @async_api_error_handler
    async def generate_async(
        self,
        title: str,
        *,
        description: str | None = None,
        locale: str | None = None,
        page_url: str | None = None,
        method: OGImageGenerateRequestMethod | str | None = None,
        preset_slug: str | None = None,
        bg_color: str | None = None,
        bg_color2: str | None = None,
        text_color: str | None = None,
        accent_color: str | None = None,
        style: OGImageGenerateRequestStyle | str | None = None,
        llm_model: str | None = None,
        image_model: str | None = None,
        style_hint: str | None = None,
        size: OGImageGenerateRequestSize | str | None = None,
    ) -> OGImageAsyncResponse:
        """Start async OG image generation (returns immediately with job_id)."""
        kwargs = _build_generate_kwargs(
            title, description=description, locale=locale, page_url=page_url,
            method=method, preset_slug=preset_slug, bg_color=bg_color,
            bg_color2=bg_color2, text_color=text_color, accent_color=accent_color,
            style=style, llm_model=llm_model, image_model=image_model,
            style_hint=style_hint, size=size,
        )
        result = await self._api.generate_async_create(OGImageGenerateRequest(**kwargs))
        logger.info("OGImage queued: id=%s, job_id=%s", result.id, result.job_id)
        return result

    @async_api_error_handler
    async def get_status(self, ogimage_id: int | str) -> OGImageStatus:
        """Get status of an OG image job."""
        return await self._api.status_retrieve(str(ogimage_id))

    @async_api_error_handler
    async def get(self, ogimage_id: int | str) -> OGImageResponse:
        """Get full details of an OGImage record."""
        return await self._api.retrieve(str(ogimage_id))

    @async_api_error_handler
    async def list(
        self,
        *,
        ordering: str | None = None,
        search: str | None = None,
        page: int | None = None,
        page_size: int | None = None,
    ) -> PaginatedOGImageResponseList:
        """List OG image generation history."""
        return await self._api.list(
            ordering=ordering, search=search, page=page, page_size=page_size,
        )

    async def wait_for_completion(
        self,
        ogimage_id: int | str,
        *,
        timeout: float = 300.0,
        poll_interval: float = 2.0,
    ) -> OGImageResponse:
        """Wait for async OG image generation to complete.

        Raises:
            TimeoutError: If generation doesn't complete within timeout
            APIError: If generation fails
        """
        start_time = time.monotonic()
        while True:
            elapsed = time.monotonic() - start_time
            if elapsed > timeout:
                raise TimeoutError(f"OGImage {ogimage_id} did not complete within {timeout}s")
            status = await self.get_status(ogimage_id)
            logger.debug("OGImage %s status: %s (elapsed: %.1fs)", ogimage_id, status.status, elapsed)
            if status.status == OGImageResponseStatus.COMPLETED:
                return await self.get(ogimage_id)
            elif status.status == OGImageResponseStatus.FAILED:
                raise APIError(getattr(status, "error_message", None) or "OGImage generation failed")
            await asyncio.sleep(poll_interval)
