"""Sync and async mixin classes for OGImageResource."""
from __future__ import annotations

import logging
import time
from typing import Any

from ..._api.generated.ogimage.ogimage__api__ogimage.models import (
    OGImageGenerateRequest,
    OGImagePublicUrl,
    OGImageResponse,
    OGImageAsyncResponse,
    OGImageStatus,
    OGImagePreset,
    OGImagePresetRequest,
    PatchedOGImagePresetRequest,
    PaginatedOGImageResponseList,
    PaginatedOGImagePresetList,
)
from ..._api.generated.ogimage.ogimage__api__ogimage_managed.models import (
    OGImageManagedCreateRequest,
    OGImageManagedResponse,
    OGImageManagedRegenerateResponse,
    PatchedOGImageManagedPatchRequest,
)
from ...exceptions import api_error_handler, async_api_error_handler

logger = logging.getLogger(__name__)


class _OGImageMixin:
    """Sync mixin for OGImageResource methods."""

    @api_error_handler
    def generate(
        self,
        title: str,
        *,
        description: str = "",
        locale: str = "en",
        method: str = "direct",
        size: str = "1200x630",
        page_url: str = "",
        preset_slug: str | None = None,
        bg_color: str = "#1a1a2e",
        bg_color2: str = "#16213e",
        text_color: str = "#ffffff",
        accent_color: str = "#3b82f6",
        style: str = "dark",
        llm_model: str = "@balanced",
        image_model: str = "@balanced",
        style_hint: str = "minimal",
    ) -> OGImageResponse:
        """Sync generate OG image (direct method — fast, no LLM).

        Returns completed OGImage with CDN URL.
        """
        kwargs: dict[str, Any] = {
            "title": title,
            "description": description,
            "locale": locale,
            "method": method,
            "size": size,
            "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,
        }
        if page_url:
            kwargs["page_url"] = page_url
        if preset_slug is not None:
            kwargs["preset_slug"] = preset_slug

        req = OGImageGenerateRequest(**kwargs)  # type: ignore[arg-type]
        result = self._api.generate_create(req)
        logger.debug("OGImage generated: status=%s url=%s", result.status, result.image_url)
        return result

    @api_error_handler
    def generate_async(
        self,
        title: str,
        *,
        description: str = "",
        locale: str = "en",
        method: str = "llm",
        size: str = "1200x630",
        page_url: str = "",
        preset_slug: str | None = None,
        llm_model: str = "@balanced",
        image_model: str = "@balanced",
        style_hint: str = "minimal",
    ) -> OGImageAsyncResponse:
        """Async enqueue OG image generation — returns job_id immediately."""
        kwargs: dict[str, Any] = {
            "title": title,
            "description": description,
            "locale": locale,
            "method": method,
            "size": size,
            "llm_model": llm_model,
            "image_model": image_model,
            "style_hint": style_hint,
        }
        if page_url:
            kwargs["page_url"] = page_url
        if preset_slug is not None:
            kwargs["preset_slug"] = preset_slug

        req = OGImageGenerateRequest(**kwargs)  # type: ignore[arg-type]
        result = self._api.generate_async_create(req)
        logger.debug("OGImage enqueued: id=%s job_id=%s", result.id, result.job_id)
        return result

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

    @api_error_handler
    def get(self, ogimage_id: int) -> OGImageResponse:
        """Retrieve a specific OGImage by ID."""
        return self._api.retrieve(str(ogimage_id))

    @api_error_handler
    def list(
        self,
        *,
        page: int | None = None,
        page_size: int | None = None,
        ordering: str | None = None,
        search: str | None = None,
    ) -> PaginatedOGImageResponseList:
        """List OGImages for the authenticated API key (paginated)."""
        return self._api.list(page=page, page_size=page_size, ordering=ordering, search=search)

    @api_error_handler
    def list_presets(
        self,
        *,
        page: int | None = None,
        page_size: int | None = None,
        ordering: str | None = None,
    ) -> PaginatedOGImagePresetList:
        """List OGImage presets for the authenticated API key."""
        return self._api.presets_list(page=page, page_size=page_size, ordering=ordering)

    @api_error_handler
    def get_public_url(self, ogimage_id: int) -> OGImagePublicUrl:
        """Get public render URL and CDN URL for an OGImage."""
        return self._api.public_url_retrieve(str(ogimage_id))

    def wait_for_completion(
        self,
        ogimage_id: int,
        *,
        timeout: float = 120,
        poll_interval: float = 3,
    ) -> OGImageStatus:
        """Poll get_status() until status is completed or failed, or timeout.

        Returns the final OGImageStatus. Raises TimeoutError if not done within timeout.
        """
        deadline = time.monotonic() + timeout
        while True:
            status = self.get_status(ogimage_id)
            if status.status in ("completed", "failed"):
                return status
            if time.monotonic() >= deadline:
                raise TimeoutError(
                    f"OGImage {ogimage_id} not completed within {timeout}s "
                    f"(last status: {status.status})"
                )
            time.sleep(poll_interval)

    # ── Managed LLM images ────────────────────────────────────────────────────

    @api_error_handler
    def create_managed(
        self,
        slug: str,
        title: str,
        *,
        description: str = "",
        locale: str = "en",
        size: str = "1200x630",
        preset_slug: str | None = None,
        llm_model: str = "@balanced",
        image_model: str = "@balanced",
        style_hint: str = "minimal",
    ) -> OGImageManagedResponse:
        """Create a managed LLM OGImage with a permanent slug.

        Enqueues async generation. Use ``stable_url`` in ``<meta og:image>`` once.
        Poll ``get_managed(slug)`` until ``status == 'completed'``.
        """
        kwargs: dict[str, Any] = {
            "slug": slug,
            "title": title,
            "description": description,
            "locale": locale,
            "size": size,
            "llm_model": llm_model,
            "image_model": image_model,
            "style_hint": style_hint,
        }
        if preset_slug is not None:
            kwargs["preset_slug"] = preset_slug

        req = OGImageManagedCreateRequest(**kwargs)  # type: ignore[arg-type]
        result = self._managed_api.create(req)
        logger.debug("Managed OGImage created: slug=%s stable_url=%s", result.slug, result.stable_url)
        return result

    @api_error_handler
    def get_managed(self, slug: str) -> OGImageManagedResponse:
        """Get status and CDN URL of a managed OGImage."""
        return self._managed_api.retrieve(slug)

    @api_error_handler
    def update_managed(
        self,
        slug: str,
        *,
        title: str | None = None,
        description: str | None = None,
        locale: str | None = None,
        size: str | None = None,
    ) -> OGImageManagedResponse:
        """Update metadata of a managed OGImage. Does not trigger regeneration."""
        kwargs: dict[str, Any] = {}
        if title is not None:
            kwargs["title"] = title
        if description is not None:
            kwargs["description"] = description
        if locale is not None:
            kwargs["locale"] = locale
        if size is not None:
            kwargs["size"] = size

        req = PatchedOGImageManagedPatchRequest(**kwargs)  # type: ignore[arg-type]
        return self._managed_api.partial_update(slug, req)

    @api_error_handler
    def delete_managed(self, slug: str) -> None:
        """Delete a managed OGImage."""
        self._managed_api.destroy(slug)
        logger.debug("Managed OGImage deleted: slug=%s", slug)

    @api_error_handler
    def regenerate_managed(self, slug: str) -> OGImageManagedRegenerateResponse:
        """Re-enqueue generation for a managed OGImage."""
        result = self._managed_api.regenerate_create(slug)
        logger.debug("Managed OGImage re-enqueued: slug=%s job_id=%s", result.slug, result.job_id)
        return result

    @api_error_handler
    def list_managed(self) -> list[OGImageManagedResponse]:
        """List all managed OGImages for the authenticated API key."""
        result = self._managed_api.list()
        return result.results if hasattr(result, "results") else result  # type: ignore[return-value]


class _AsyncOGImageMixin:
    """Async mixin for AsyncOGImageResource methods."""

    @async_api_error_handler
    async def generate(
        self,
        title: str,
        *,
        description: str = "",
        locale: str = "en",
        method: str = "direct",
        size: str = "1200x630",
        page_url: str = "",
        preset_slug: str | None = None,
        bg_color: str = "#1a1a2e",
        bg_color2: str = "#16213e",
        text_color: str = "#ffffff",
        accent_color: str = "#3b82f6",
        style: str = "dark",
        llm_model: str = "@balanced",
        image_model: str = "@balanced",
        style_hint: str = "minimal",
    ) -> OGImageResponse:
        """Async sync generate OG image (direct method — fast, no LLM)."""
        kwargs: dict[str, Any] = {
            "title": title,
            "description": description,
            "locale": locale,
            "method": method,
            "size": size,
            "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,
        }
        if page_url:
            kwargs["page_url"] = page_url
        if preset_slug is not None:
            kwargs["preset_slug"] = preset_slug

        req = OGImageGenerateRequest(**kwargs)  # type: ignore[arg-type]
        return await self._api.generate_create(req)

    @async_api_error_handler
    async def generate_async(
        self,
        title: str,
        *,
        description: str = "",
        locale: str = "en",
        method: str = "llm",
        size: str = "1200x630",
        page_url: str = "",
        preset_slug: str | None = None,
        llm_model: str = "@balanced",
        image_model: str = "@balanced",
        style_hint: str = "minimal",
    ) -> OGImageAsyncResponse:
        """Async enqueue OG image generation."""
        kwargs: dict[str, Any] = {
            "title": title,
            "description": description,
            "locale": locale,
            "method": method,
            "size": size,
            "llm_model": llm_model,
            "image_model": image_model,
            "style_hint": style_hint,
        }
        if page_url:
            kwargs["page_url"] = page_url
        if preset_slug is not None:
            kwargs["preset_slug"] = preset_slug

        req = OGImageGenerateRequest(**kwargs)  # type: ignore[arg-type]
        return await self._api.generate_async_create(req)

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

    @async_api_error_handler
    async def get(self, ogimage_id: int) -> OGImageResponse:
        """Retrieve a specific OGImage by ID."""
        return await self._api.retrieve(str(ogimage_id))

    @async_api_error_handler
    async def list(
        self,
        *,
        page: int | None = None,
        page_size: int | None = None,
        ordering: str | None = None,
        search: str | None = None,
    ) -> PaginatedOGImageResponseList:
        """List OGImages for the authenticated API key (paginated)."""
        return await self._api.list(page=page, page_size=page_size, ordering=ordering, search=search)

    @async_api_error_handler
    async def list_presets(
        self,
        *,
        page: int | None = None,
        page_size: int | None = None,
        ordering: str | None = None,
    ) -> PaginatedOGImagePresetList:
        """List OGImage presets for the authenticated API key."""
        return await self._api.presets_list(page=page, page_size=page_size, ordering=ordering)

    @async_api_error_handler
    async def get_public_url(self, ogimage_id: int) -> OGImagePublicUrl:
        """Get public render URL and CDN URL for an OGImage."""
        return await self._api.public_url_retrieve(str(ogimage_id))

    async def wait_for_completion(
        self,
        ogimage_id: int,
        *,
        timeout: float = 120,
        poll_interval: float = 3,
    ) -> OGImageStatus:
        """Poll get_status() until completed or failed, or timeout."""
        import asyncio
        deadline = time.monotonic() + timeout
        while True:
            status = await self.get_status(ogimage_id)
            if status.status in ("completed", "failed"):
                return status
            if time.monotonic() >= deadline:
                raise TimeoutError(
                    f"OGImage {ogimage_id} not completed within {timeout}s "
                    f"(last status: {status.status})"
                )
            await asyncio.sleep(poll_interval)

    @async_api_error_handler
    async def create_managed(
        self,
        slug: str,
        title: str,
        *,
        description: str = "",
        locale: str = "en",
        size: str = "1200x630",
        preset_slug: str | None = None,
        llm_model: str = "@balanced",
        image_model: str = "@balanced",
        style_hint: str = "minimal",
    ) -> OGImageManagedResponse:
        """Create a managed LLM OGImage with a permanent slug."""
        kwargs: dict[str, Any] = {
            "slug": slug,
            "title": title,
            "description": description,
            "locale": locale,
            "size": size,
            "llm_model": llm_model,
            "image_model": image_model,
            "style_hint": style_hint,
        }
        if preset_slug is not None:
            kwargs["preset_slug"] = preset_slug

        req = OGImageManagedCreateRequest(**kwargs)  # type: ignore[arg-type]
        return await self._managed_api.create(req)

    @async_api_error_handler
    async def get_managed(self, slug: str) -> OGImageManagedResponse:
        """Get status and CDN URL of a managed OGImage."""
        return await self._managed_api.retrieve(slug)

    @async_api_error_handler
    async def update_managed(
        self,
        slug: str,
        *,
        title: str | None = None,
        description: str | None = None,
        locale: str | None = None,
        size: str | None = None,
    ) -> OGImageManagedResponse:
        """Update metadata of a managed OGImage."""
        kwargs: dict[str, Any] = {}
        if title is not None:
            kwargs["title"] = title
        if description is not None:
            kwargs["description"] = description
        if locale is not None:
            kwargs["locale"] = locale
        if size is not None:
            kwargs["size"] = size

        req = PatchedOGImageManagedPatchRequest(**kwargs)  # type: ignore[arg-type]
        return await self._managed_api.partial_update(slug, req)

    @async_api_error_handler
    async def delete_managed(self, slug: str) -> None:
        """Delete a managed OGImage."""
        await self._managed_api.destroy(slug)

    @async_api_error_handler
    async def regenerate_managed(self, slug: str) -> OGImageManagedRegenerateResponse:
        """Re-enqueue generation for a managed OGImage."""
        return await self._managed_api.regenerate_create(slug)

    @async_api_error_handler
    async def list_managed(self) -> list[OGImageManagedResponse]:
        """List all managed OGImages for the authenticated API key."""
        result = await self._managed_api.list()
        return result.results if hasattr(result, "results") else result  # type: ignore[return-value]
