"""Free production job specs for AutoShortsBot.

This layer converts motion-led asset plans into executable *free* production
jobs. It deliberately avoids paid video-credit providers such as Kling, Hailuo,
Runway, Veo, Pika, or Luma. Cinematic-looking motion is approximated with local
procedural animation, parallax, generated shapes, captions, and FFmpeg/Pillow
composition. NVIDIA build.nvidia.com models are text-only helpers here.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from autoshorts.script_drafts import _slug

PAID_VIDEO_PROVIDERS = {"kling", "hailuo", "runway", "veo", "pika", "luma"}


def _job_id(scene: dict[str, Any], suffix: str) -> str:
    return f"{scene['scene_id']}_{suffix}"


def _local_motion_workflow(scene: dict[str, Any]) -> str:
    mode = scene["asset_mode"]
    if mode == "cinematic_ai_video":
        return (
            "Use local procedural motion: illustrated/abstract background, object-layer parallax, Ken Burns push-in, "
            "depth shadows, speed ramps, camera shake on beat, and renderer captions. No paid video-generation credits."
        )
    if mode == "mechanism_animation":
        return (
            "Use local mechanism animation: vector-like shapes, x-ray/layer reveals, animated arrows, risk meters, "
            "price-tag layers, sequential labels, and beat-synced clicks."
        )
    if mode == "kinetic_text_overlay":
        return "Use local kinetic typography: large words, scale pops, slide-ins, glow pulses, and FFmpeg/Pillow compositing."
    if mode == "screen_recording_simulation":
        return "Use local synthetic UI recording: generated interface frames, cursor path, typing reveal, zoom highlights."
    return "Use local animated card renderer: one concept per beat, no static slide holds over one second."


def _local_job(scene: dict[str, Any]) -> dict[str, Any]:
    mode = scene["asset_mode"]
    provider = "local_ffmpeg" if mode == "kinetic_text_overlay" else "local_renderer"
    execution_mode = {
        "cinematic_ai_video": "procedural_motion",
        "mechanism_animation": "mechanism_reveal_animation",
        "kinetic_text_overlay": "kinetic_typography",
        "screen_recording_simulation": "synthetic_screen_capture",
        "animated_card": "animated_cards",
    }.get(mode, "procedural_motion")
    return {
        "job_id": _job_id(scene, "local_motion"),
        "scene_id": scene["scene_id"],
        "asset_mode": mode,
        "provider": provider,
        "execution_mode": execution_mode,
        "cost": "free",
        "requires_api_key": False,
        "duration_seconds": scene["duration_seconds"],
        "overlay_text": scene["overlay_text"],
        "motion_directive": scene["motion_directive"],
        "free_workflow": _local_motion_workflow(scene),
        "source_prompt": scene["generation_prompt"],
        "negative_prompt": scene["negative_prompt"],
        "outputs": {
            "clip_placeholder": f"outputs/clips/{scene['scene_id']}.mp4",
            "metadata_placeholder": f"outputs/clips/{scene['scene_id']}.json",
        },
    }


def _nvidia_prompt_refinement_job(scene: dict[str, Any]) -> dict[str, Any]:
    return {
        "job_id": _job_id(scene, "nvidia_prompt_refine"),
        "scene_id": scene["scene_id"],
        "asset_mode": scene["asset_mode"],
        "provider": "free_nvidia_llm",
        "execution_mode": "prompt_to_motion_brief",
        "cost": "free",
        "requires_api_key": "NVIDIA_API_KEY",
        "side_effect": "text_refinement_only",
        "free_workflow": (
            "Optionally ask a free build.nvidia.com LLM to compress the visual brief into local renderer instructions. "
            "This is text planning only and must not call paid video-credit providers."
        ),
        "input": {
            "visual_direction": scene["source_visual_direction"],
            "retention_goal": scene["retention_goal"],
            "motion_directive": scene["motion_directive"],
        },
        "output_placeholder": f"outputs/briefs/{scene['scene_id']}_motion_brief.json",
    }


def build_free_production_jobs(asset_plan: dict[str, Any]) -> dict[str, Any]:
    """Build executable free production job specs from an asset plan."""
    jobs: list[dict[str, Any]] = []
    for scene in asset_plan["scenes"]:
        jobs.append(_local_job(scene))
        if scene["asset_mode"] in {"cinematic_ai_video", "mechanism_animation"}:
            jobs.append(_nvidia_prompt_refinement_job(scene))

    providers = {job["provider"] for job in jobs}
    if providers & PAID_VIDEO_PROVIDERS:
        raise ValueError(f"Paid video providers are forbidden: {sorted(providers & PAID_VIDEO_PROVIDERS)}")

    return {
        "schema_version": "free_production_jobs.v1",
        "video": asset_plan["video"],
        "budget_policy": {
            "paid_video_credits_allowed": False,
            "allowed_providers": ["local_renderer", "local_ffmpeg", "free_nvidia_llm"],
            "nvidia_llm_scope": "text_refinement_only",
        },
        "jobs": jobs,
        "assembly": {
            "provider": "local_ffmpeg",
            "cost": "free",
            "steps": [
                "render local scene clips",
                "compose kinetic captions",
                "add free/local SFX placeholders",
                "assemble vertical MP4 with FFmpeg",
            ],
        },
    }


def _jobs_filename(video: dict[str, Any]) -> str:
    title_slug = _slug(video["title"])
    video_id = video["id"]
    if video_id.endswith(title_slug):
        return f"{video_id}.json"
    return f"{video_id}-{title_slug}.json"


def write_free_production_jobs(asset_plans: list[dict[str, Any]], output_dir: Path) -> list[Path]:
    """Write free production job specs as JSON files."""
    output_dir.mkdir(parents=True, exist_ok=True)
    paths: list[Path] = []
    for asset_plan in asset_plans:
        jobs = build_free_production_jobs(asset_plan)
        path = output_dir / _jobs_filename(jobs["video"])
        path.write_text(json.dumps(jobs, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
        paths.append(path)
    return paths
