from typing import Literal

from pydantic import BaseModel, Field, field_validator


# Only the workflows this build ships. The backend serves more.
ComfyCloudWorkflow = Literal[
    "z-image-turbo/text-to-image",
    "flux-2/text-to-image",
    "minimax-h3/text-to-video",
    "minimax-h3/first-last-frame-to-video",
    "minimax-h3/reference-to-video",
    "minimax-h3/image-to-video",
    "mage-flow/text-to-image",
    "mage-flow-turbo/text-to-image",
    "minimax-music-3/text-to-audio",
]


# Only the inputs the shipped nodes send. The manifests accept more.
class ComfyCloudWorkflowInputs(BaseModel):
    prompt: str | None = Field(None)
    image_url: str | None = Field(None)
    assets: dict[str, "ComfyCloudAssetInput"] | None = Field(None)
    audio_url: str | None = Field(None)
    first_frame_url: str | None = Field(None)
    last_frame_url: str | None = Field(None)
    instruction: str | None = Field(None)
    prompt_enhance: bool | None = Field(None)
    enhance_prompt: bool | None = Field(None)
    negative_prompt: str | None = Field(None)
    aspect_ratio: str | None = Field(None)
    duration_seconds: float | None = Field(None)
    quality_mode: str | None = Field(None)
    seed: int | None = Field(None, ge=0, le=0xFFFFFFFFFFFFFFFF)
    scale: str | None = Field(None)
    width: int | None = Field(None, ge=256, le=2048)
    height: int | None = Field(None, ge=256, le=2048)
    resolution: str | None = Field(None)
    rendering_speed: str | None = Field(None)
    color_correction: str | None = Field(None)
    turbo: bool | None = Field(None)
    style_lora: bool | None = Field(None)
    model: str | None = Field(None)
    lora: str | None = Field(None)
    steps: int | None = Field(None, ge=1)
    turbo_steps: int | None = Field(None, ge=1)
    fast_steps: int | None = Field(None, ge=1)
    cfg: float | None = Field(None, ge=0)
    guidance: float | None = Field(None, ge=0)
    shift: float | None = Field(None, ge=0)
    turbo_strength: float | None = Field(None, ge=0)
    style_strength: float | None = Field(None, ge=0)
    megapixels: float | None = Field(None, gt=0)
    size_multiple: int | None = Field(None, ge=1)
    sampler: str | None = Field(None)
    scheduler: str | None = Field(None)
    denoise: float | None = Field(None, ge=0, le=1)
    text_encoder: str | None = Field(None)
    ref_image_size: str | None = Field(None)
    lyrics: str | None = Field(None)
    max_duration: float | None = Field(None, gt=0)
    caption_cfg: float | None = Field(None, ge=0)
    top_k: int | None = Field(None, ge=1)
    tiled_decode: bool | None = Field(None)
    tile_size: int | None = Field(None, ge=1)
    tile_overlap: int | None = Field(None, ge=0)
    audio_quality: str | None = Field(None)


class ComfyCloudAssetInput(BaseModel):
    type: Literal["IMAGE", "VIDEO", "AUDIO"] = Field(...)
    url: str = Field(...)


class ComfyCloudGenerateRequest(BaseModel):
    workflow: ComfyCloudWorkflow = Field(...)
    inputs: ComfyCloudWorkflowInputs = Field(...)


class ComfyCloudGenerateResponse(BaseModel):
    task_id: str = Field(..., min_length=1)
    status: str = Field(...)
    polling_url: str | None = Field(None)
    cancel_url: str | None = Field(None)

    @field_validator("task_id")
    @classmethod
    def task_id_must_not_be_blank(cls, value: str) -> str:
        if not value.strip():
            raise ValueError("task_id must not be blank")
        return value


class ComfyCloudStatusResponse(BaseModel):
    task_id: str = Field(..., min_length=1)
    status: str = Field(...)
    progress: float | None = Field(None)
    output_url: str | None = Field(None)
    error: str | None = Field(None)

    @field_validator("task_id")
    @classmethod
    def task_id_must_not_be_blank(cls, value: str) -> str:
        if not value.strip():
            raise ValueError("task_id must not be blank")
        return value
