"""Veo 3 video generation resource."""
from __future__ import annotations

import asyncio
import logging
from typing import TYPE_CHECKING

from ..types.veo import (
    VeoModel,
    VeoAspectRatio,
    VeoStatus,
    VeoGenerateParams,
    VeoGenerateResponse,
    VeoStatusResponse,
)
from .._errors import GenerationTimeoutError

if TYPE_CHECKING:
    from ..client import RunBlob

logger = logging.getLogger("runblob.veo")

_POLL_INITIAL_DELAY = 3.0
_POLL_MAX_DELAY = 15.0
_POLL_BACKOFF_FACTOR = 1.5
_POLL_DEFAULT_TIMEOUT = 600.0


class Veo:
    """Veo 3 video generation.

    Available via ``client.veo``.
    """

    _client: RunBlob

    def __init__(self, client: RunBlob) -> None:
        self._client = client

    async def generate(
        self,
        *,
        prompt: str,
        model: VeoModel | str = VeoModel.FAST,
        aspect_ratio: VeoAspectRatio | str = VeoAspectRatio.LANDSCAPE,
        seed: int | None = None,
        watermark_text: str | None = None,
        bytes_image: str | None = None,
        start_frame: str | None = None,
        end_frame: str | None = None,
        reference_images: list[str] | None = None,
        callback_url: str | None = None,
    ) -> VeoGenerateResponse:
        """Generate a video with Veo 3.

        Supports text-to-video, image-to-video, frame loop (start+end frame),
        and reference image modes.

        Args:
            prompt: Text description (10-4000 chars, English only).
            model: Veo model — "veo_3_fast", "veo_3_fast_hd", "veo_3_fast_4k",
                   "veo_3_quality", "veo_3_quality_hd", "veo_3_quality_4k".
            aspect_ratio: "16:9" (landscape) or "9:16" (portrait).
            seed: Reproducibility seed (0-1000000000).
            watermark_text: Custom watermark (3-14 chars).
            bytes_image: Base64-encoded image for image-to-video.
            start_frame: Base64 start frame for frame loop mode.
            end_frame: Base64 end frame for frame loop mode.
            reference_images: List of 1-3 base64 reference images (fast models only).
            callback_url: Webhook URL for completion notification.

        Returns:
            VeoGenerateResponse with generation_id, status, seed, price.
        """
        model_enum = VeoModel(model) if isinstance(model, str) else model
        ar_enum = VeoAspectRatio(aspect_ratio) if isinstance(aspect_ratio, str) else aspect_ratio

        params = VeoGenerateParams(
            prompt=prompt,
            model=model_enum,
            aspect_ratio=ar_enum,
            seed=seed,
            watermark_text=watermark_text,
            bytes_image=bytes_image,
            start_frame=start_frame,
            end_frame=end_frame,
            reference_images=reference_images,
            callback_url=callback_url,
        )

        return await self._client.request(
            "POST",
            "/v1/veo/generate",
            response_model=VeoGenerateResponse,
            body=params.to_api_body(),
        )

    async def get_status(self, generation_id: str) -> VeoStatusResponse:
        """Get video generation status.

        Args:
            generation_id: The ID returned from generate().

        Returns:
            VeoStatusResponse with status, video_url on completion.
        """
        return await self._client.request(
            "GET",
            f"/v1/veo/generations/{generation_id}",
            response_model=VeoStatusResponse,
        )

    async def list(
        self,
        *,
        limit: int = 50,
        offset: int = 0,
    ) -> list[VeoStatusResponse]:
        """List recent generations.

        Args:
            limit: Max results, 1-100 (default 50).
            offset: Skip N results for pagination (default 0).

        Returns:
            List of VeoStatusResponse objects.
        """
        response = await self._client.request_raw(
            "GET",
            "/v1/veo/generations",
            params={"limit": limit, "offset": offset},
        )
        items = response.json()
        return [VeoStatusResponse.model_validate(item) for item in items]

    async def wait_for_result(
        self,
        generation_id: str,
        *,
        timeout: float = _POLL_DEFAULT_TIMEOUT,
        poll_interval: float = _POLL_INITIAL_DELAY,
        max_poll_interval: float = _POLL_MAX_DELAY,
        backoff_factor: float = _POLL_BACKOFF_FACTOR,
    ) -> VeoStatusResponse:
        """Poll until video generation completes or fails.

        Args:
            generation_id: The ID to poll.
            timeout: Max seconds to wait (default 600).
            poll_interval: Initial delay between polls (default 3s).
            max_poll_interval: Max delay between polls (default 15s).
            backoff_factor: Multiply interval each iteration (default 1.5).

        Returns:
            VeoStatusResponse with final status.

        Raises:
            GenerationTimeoutError: If timeout exceeded.
        """
        deadline = asyncio.get_event_loop().time() + timeout
        delay = poll_interval

        while True:
            status = await self.get_status(generation_id)

            if status.is_terminal:
                logger.info("Veo %s finished: %s", generation_id, status.status.value)
                return status

            remaining = deadline - asyncio.get_event_loop().time()
            if remaining <= 0:
                raise GenerationTimeoutError(generation_id, timeout)

            actual_delay = min(delay, remaining)
            logger.debug("Veo %s status=%s, polling in %.1fs", generation_id, status.status.value, actual_delay)
            await asyncio.sleep(actual_delay)
            delay = min(delay * backoff_factor, max_poll_interval)

    async def generate_and_wait(
        self,
        *,
        prompt: str,
        model: VeoModel | str = VeoModel.FAST,
        aspect_ratio: VeoAspectRatio | str = VeoAspectRatio.LANDSCAPE,
        seed: int | None = None,
        watermark_text: str | None = None,
        bytes_image: str | None = None,
        callback_url: str | None = None,
        timeout: float = _POLL_DEFAULT_TIMEOUT,
    ) -> VeoStatusResponse:
        """Generate video and wait for result.

        Returns:
            VeoStatusResponse with final status and video_url.
        """
        task = await self.generate(
            prompt=prompt, model=model, aspect_ratio=aspect_ratio,
            seed=seed, watermark_text=watermark_text,
            bytes_image=bytes_image, callback_url=callback_url,
        )
        logger.info("Created Veo %s (price=%s), waiting...", task.generation_id, task.price)
        return await self.wait_for_result(task.generation_id, timeout=timeout)

    async def generate_batch(
        self,
        prompts: list[str],
        *,
        model: VeoModel | str = VeoModel.FAST,
        aspect_ratio: VeoAspectRatio | str = VeoAspectRatio.LANDSCAPE,
        concurrency: int = 5,
    ) -> list[VeoGenerateResponse]:
        """Submit multiple video generations concurrently.

        Args:
            prompts: List of text prompts.
            model: Model for all generations.
            aspect_ratio: Aspect ratio for all.
            concurrency: Max concurrent requests (default 5).

        Returns:
            List of VeoGenerateResponse in same order as prompts.
        """
        sem = asyncio.Semaphore(concurrency)

        async def _gen(prompt: str) -> VeoGenerateResponse:
            async with sem:
                return await self.generate(
                    prompt=prompt, model=model, aspect_ratio=aspect_ratio,
                )

        return list(await asyncio.gather(*[_gen(p) for p in prompts]))
