from __future__ import annotations

from typing import TYPE_CHECKING, List, Optional

from .._resource import AsyncAPIResource, SyncAPIResource
from .._types import NOT_GIVEN, FileInput, NotGiven, is_given
from .._utils import strip_not_given
from ..types.create_response import CreateResponse
from ..types.generation import Generation

if TYPE_CHECKING:
    from .files import AsyncFiles, Files


class Video(SyncAPIResource):
    @property
    def _files(self) -> Files:
        return self._client.files  # type: ignore[return-value]

    def _build_body(self, **kwargs: object) -> tuple[dict, list[str]]:
        temp: list[str] = []
        first_frame = kwargs.pop("first_frame")
        last_frame = kwargs.pop("last_frame")
        video = kwargs.pop("video")
        audio = kwargs.pop("audio")
        reference_images = kwargs.pop("reference_images")
        motion_video = kwargs.pop("motion_video")
        body: dict = strip_not_given(kwargs)  # type: ignore[arg-type]
        if is_given(first_frame):
            body["first_frame"] = self._files.resolve(first_frame, _temp=temp)  # type: ignore[arg-type]
        if is_given(last_frame):
            body["last_frame"] = self._files.resolve(last_frame, _temp=temp)  # type: ignore[arg-type]
        if is_given(video):
            body["video"] = self._files.resolve(video, _temp=temp)  # type: ignore[arg-type]
        if is_given(audio):
            body["audio"] = self._files.resolve(audio, _temp=temp)  # type: ignore[arg-type]
        if is_given(reference_images):
            body["reference_images"] = self._files.resolve_many(reference_images, _temp=temp)  # type: ignore[arg-type]
        if is_given(motion_video):
            body["motion_video"] = self._files.resolve(motion_video, _temp=temp)  # type: ignore[arg-type]
        return body, temp

    def submit(
        self,
        *,
        model: str,
        prompt: str | NotGiven = NOT_GIVEN,
        size: str | NotGiven = NOT_GIVEN,
        duration: int | NotGiven = NOT_GIVEN,
        quality: str | NotGiven = NOT_GIVEN,
        first_frame: FileInput | NotGiven = NOT_GIVEN,
        last_frame: FileInput | NotGiven = NOT_GIVEN,
        video: FileInput | NotGiven = NOT_GIVEN,
        audio: FileInput | NotGiven = NOT_GIVEN,
        reference_images: List[FileInput] | NotGiven = NOT_GIVEN,
        motion_video: FileInput | NotGiven = NOT_GIVEN,
        resolution: str | NotGiven = NOT_GIVEN,
        aspect_ratio: str | NotGiven = NOT_GIVEN,
        generate_audio: bool | NotGiven = NOT_GIVEN,
        negative_prompt: str | NotGiven = NOT_GIVEN,
        **extra_body: object,
    ) -> CreateResponse:
        body, _ = self._build_body(
            model=model, prompt=prompt, size=size, duration=duration, quality=quality,
            first_frame=first_frame, last_frame=last_frame, video=video,
            audio=audio, reference_images=reference_images, motion_video=motion_video,
            resolution=resolution, aspect_ratio=aspect_ratio,
            generate_audio=generate_audio, negative_prompt=negative_prompt,
            **extra_body,
        )
        return self._post("/generatevideo", cast_to=CreateResponse, body=body)

    def generate(
        self,
        *,
        model: str,
        prompt: str | NotGiven = NOT_GIVEN,
        size: str | NotGiven = NOT_GIVEN,
        duration: int | NotGiven = NOT_GIVEN,
        quality: str | NotGiven = NOT_GIVEN,
        first_frame: FileInput | NotGiven = NOT_GIVEN,
        last_frame: FileInput | NotGiven = NOT_GIVEN,
        video: FileInput | NotGiven = NOT_GIVEN,
        audio: FileInput | NotGiven = NOT_GIVEN,
        reference_images: List[FileInput] | NotGiven = NOT_GIVEN,
        motion_video: FileInput | NotGiven = NOT_GIVEN,
        resolution: str | NotGiven = NOT_GIVEN,
        aspect_ratio: str | NotGiven = NOT_GIVEN,
        generate_audio: bool | NotGiven = NOT_GIVEN,
        negative_prompt: str | NotGiven = NOT_GIVEN,
        poll_interval: float = 5.0,
        timeout: float = 900.0,
        **extra_body: object,
    ) -> Generation:
        body, temp_ids = self._build_body(
            model=model, prompt=prompt, size=size, duration=duration, quality=quality,
            first_frame=first_frame, last_frame=last_frame, video=video,
            audio=audio, reference_images=reference_images, motion_video=motion_video,
            resolution=resolution, aspect_ratio=aspect_ratio,
            generate_audio=generate_audio, negative_prompt=negative_prompt,
            **extra_body,
        )
        try:
            resp = self._post("/generatevideo", cast_to=CreateResponse, body=body)
            return self._client.generations.wait(  # type: ignore[union-attr]
                resp.request_id,
                poll_interval=poll_interval,
                timeout=timeout,
            )
        finally:
            if temp_ids:
                self._files.cleanup(temp_ids)


class AsyncVideo(AsyncAPIResource):
    @property
    def _files(self) -> AsyncFiles:
        return self._client.files  # type: ignore[return-value]

    async def _build_body(self, **kwargs: object) -> tuple[dict, list[str]]:
        temp: list[str] = []
        first_frame = kwargs.pop("first_frame")
        last_frame = kwargs.pop("last_frame")
        video = kwargs.pop("video")
        audio = kwargs.pop("audio")
        reference_images = kwargs.pop("reference_images")
        motion_video = kwargs.pop("motion_video")
        body: dict = strip_not_given(kwargs)  # type: ignore[arg-type]
        if is_given(first_frame):
            body["first_frame"] = await self._files.resolve(first_frame, _temp=temp)  # type: ignore[arg-type]
        if is_given(last_frame):
            body["last_frame"] = await self._files.resolve(last_frame, _temp=temp)  # type: ignore[arg-type]
        if is_given(video):
            body["video"] = await self._files.resolve(video, _temp=temp)  # type: ignore[arg-type]
        if is_given(audio):
            body["audio"] = await self._files.resolve(audio, _temp=temp)  # type: ignore[arg-type]
        if is_given(reference_images):
            body["reference_images"] = await self._files.resolve_many(reference_images, _temp=temp)  # type: ignore[arg-type]
        if is_given(motion_video):
            body["motion_video"] = await self._files.resolve(motion_video, _temp=temp)  # type: ignore[arg-type]
        return body, temp

    async def submit(
        self,
        *,
        model: str,
        prompt: str | NotGiven = NOT_GIVEN,
        size: str | NotGiven = NOT_GIVEN,
        duration: int | NotGiven = NOT_GIVEN,
        quality: str | NotGiven = NOT_GIVEN,
        first_frame: FileInput | NotGiven = NOT_GIVEN,
        last_frame: FileInput | NotGiven = NOT_GIVEN,
        video: FileInput | NotGiven = NOT_GIVEN,
        audio: FileInput | NotGiven = NOT_GIVEN,
        reference_images: List[FileInput] | NotGiven = NOT_GIVEN,
        motion_video: FileInput | NotGiven = NOT_GIVEN,
        resolution: str | NotGiven = NOT_GIVEN,
        aspect_ratio: str | NotGiven = NOT_GIVEN,
        generate_audio: bool | NotGiven = NOT_GIVEN,
        negative_prompt: str | NotGiven = NOT_GIVEN,
        **extra_body: object,
    ) -> CreateResponse:
        body, _ = await self._build_body(
            model=model, prompt=prompt, size=size, duration=duration, quality=quality,
            first_frame=first_frame, last_frame=last_frame, video=video,
            audio=audio, reference_images=reference_images, motion_video=motion_video,
            resolution=resolution, aspect_ratio=aspect_ratio,
            generate_audio=generate_audio, negative_prompt=negative_prompt,
            **extra_body,
        )
        return await self._post("/generatevideo", cast_to=CreateResponse, body=body)

    async def generate(
        self,
        *,
        model: str,
        prompt: str | NotGiven = NOT_GIVEN,
        size: str | NotGiven = NOT_GIVEN,
        duration: int | NotGiven = NOT_GIVEN,
        quality: str | NotGiven = NOT_GIVEN,
        first_frame: FileInput | NotGiven = NOT_GIVEN,
        last_frame: FileInput | NotGiven = NOT_GIVEN,
        video: FileInput | NotGiven = NOT_GIVEN,
        audio: FileInput | NotGiven = NOT_GIVEN,
        reference_images: List[FileInput] | NotGiven = NOT_GIVEN,
        motion_video: FileInput | NotGiven = NOT_GIVEN,
        resolution: str | NotGiven = NOT_GIVEN,
        aspect_ratio: str | NotGiven = NOT_GIVEN,
        generate_audio: bool | NotGiven = NOT_GIVEN,
        negative_prompt: str | NotGiven = NOT_GIVEN,
        poll_interval: float = 5.0,
        timeout: float = 900.0,
        **extra_body: object,
    ) -> Generation:
        body, temp_ids = await self._build_body(
            model=model, prompt=prompt, size=size, duration=duration, quality=quality,
            first_frame=first_frame, last_frame=last_frame, video=video,
            audio=audio, reference_images=reference_images, motion_video=motion_video,
            resolution=resolution, aspect_ratio=aspect_ratio,
            generate_audio=generate_audio, negative_prompt=negative_prompt,
            **extra_body,
        )
        try:
            resp = await self._post("/generatevideo", cast_to=CreateResponse, body=body)
            return await self._client.generations.wait(  # type: ignore[union-attr]
                resp.request_id,
                poll_interval=poll_interval,
                timeout=timeout,
            )
        finally:
            if temp_ids:
                await self._files.cleanup(temp_ids)
