"""Side-effect-free render manifest generation from shot plans."""

from __future__ import annotations

from dataclasses import dataclass

from autoshorts.ideas.candidate import ValidationResult
from autoshorts.rendering.shot_plan import ShotPlan, validate_shot_plan


@dataclass(frozen=True)
class RenderSettings:
    width: int = 1080
    height: int = 1920
    fps: int = 30
    no_generated_text_inside_assets: bool = True
    captions_renderer_owned: bool = True
    static_card_limit: int = 0


@dataclass(frozen=True)
class RenderLayer:
    text: str
    renderer_owned: bool = True


@dataclass(frozen=True)
class RenderTimelineItem:
    scene_purpose: str
    start_seconds: int
    end_seconds: int
    asset_mode: str
    motion_instruction: str
    placeholder_asset_path: str
    caption_layer: RenderLayer
    label_layer: RenderLayer


@dataclass(frozen=True)
class RenderManifest:
    script_id: str
    title: str
    settings: RenderSettings
    timeline: tuple[RenderTimelineItem, ...]


def _is_blank(value: str) -> bool:
    return not (value or "").strip()


def _placeholder_path(script_id: str, index: int, purpose: str) -> str:
    return f"placeholders/{script_id}/{index:02d}-{purpose}.png"


def build_render_manifest(plan: ShotPlan) -> RenderManifest:
    """Turn a validated shot plan into a machine-readable render manifest."""

    validation = validate_shot_plan(plan)
    if not validation.is_valid:
        raise ValueError("; ".join(validation.errors))

    timeline = tuple(
        RenderTimelineItem(
            scene_purpose=scene.purpose,
            start_seconds=scene.start_seconds,
            end_seconds=scene.end_seconds,
            asset_mode=scene.asset_mode,
            motion_instruction=scene.visual_action,
            placeholder_asset_path=_placeholder_path(plan.script_id, index, scene.purpose),
            caption_layer=RenderLayer(text=scene.caption_beat, renderer_owned=True),
            label_layer=RenderLayer(text=scene.purpose.replace("_", " "), renderer_owned=True),
        )
        for index, scene in enumerate(plan.scenes, start=1)
    )
    return RenderManifest(
        script_id=plan.script_id,
        title=plan.title,
        settings=RenderSettings(),
        timeline=timeline,
    )


def validate_render_manifest(manifest: RenderManifest) -> ValidationResult:
    errors: list[str] = []
    if _is_blank(manifest.script_id):
        errors.append("script_id is required")
    if _is_blank(manifest.title):
        errors.append("title is required")

    settings = manifest.settings
    if (settings.width, settings.height) != (1080, 1920):
        errors.append("render size must be vertical 1080x1920")
    if settings.fps != 30:
        errors.append("fps must be 30")
    if not settings.no_generated_text_inside_assets:
        errors.append("generated text inside assets is not allowed")
    if not settings.captions_renderer_owned:
        errors.append("captions must be renderer-owned")
    if settings.static_card_limit != 0:
        errors.append("static_card_limit must be 0")

    if not manifest.timeline:
        errors.append("at least one timeline item is required")

    previous_end: int | None = None
    for index, item in enumerate(manifest.timeline, start=1):
        if _is_blank(item.scene_purpose):
            errors.append(f"timeline item {index} scene_purpose is required")
        if item.end_seconds <= item.start_seconds:
            errors.append(f"timeline item {index} end_seconds must be greater than start_seconds")
        if previous_end is not None and item.start_seconds < previous_end:
            errors.append(f"timeline item {index} start_seconds overlaps previous item")
        previous_end = item.end_seconds

        for field_name, value in {
            "asset_mode": item.asset_mode,
            "motion_instruction": item.motion_instruction,
            "placeholder_asset_path": item.placeholder_asset_path,
            "caption_layer text": item.caption_layer.text,
            "label_layer text": item.label_layer.text,
        }.items():
            if _is_blank(value):
                errors.append(f"timeline item {index} {field_name} is required")

        if item.asset_mode == "static_slideshow":
            errors.append("static slideshow asset mode is not allowed")
        if "baked-in" in item.motion_instruction.casefold():
            errors.append("baked-in text in render assets is not allowed")
        if not item.caption_layer.renderer_owned:
            errors.append(f"timeline item {index} caption layer must be renderer-owned")
        if not item.label_layer.renderer_owned:
            errors.append(f"timeline item {index} label layer must be renderer-owned")

    return ValidationResult(is_valid=not errors, errors=tuple(errors))
