"""Wan2.2 Image-to-Video prompt planning for mechanism-led shorts.

This module converts motion-led shot plans into concrete keyframe and I2V
prompt specs. It deliberately keeps generated text, numbers, labels, and UI
out of AI-video clips; those belong to the deterministic renderer.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from autoshorts.script_drafts import _slug

STYLE_PROFILE = "photorealistic_macro_investigation"
WAN22_QUALITY_PRESET = "wan22_i2v_quality_416x736"

BASE_NEGATIVE_PROMPT = (
    "cartoon, animation, deformed subject, duplicated subject, disappearing subject, "
    "melting body, distorted geometry, blurry, low detail, flickering colors, abstract, "
    "static image, camera shake, overexposed, underexposed, dark frame, text, subtitles, "
    "captions, watermark, logo, unreadable letters, fake UI text"
)

CAR_NEGATIVE_PROMPT = (
    BASE_NEGATIVE_PROMPT
    + ", unreadable license plate, fake license plate text, brand logos, distorted wheels, "
    "melted car body, broken windshield geometry, fake numbers, fake labels"
)


def _is_wan_candidate(scene: dict[str, Any]) -> bool:
    return scene.get("asset_type") == "cinematic_ai_video"


def _car_keyframe_prompt(scene: dict[str, Any]) -> str:
    return (
        "Photorealistic vertical image of a red used compact car in a bright European vehicle "
        "inspection garage, full front three-quarter view, clean empty license plate area, "
        "visible wheels and body panels, soft overhead lights, realistic reflections, shallow "
        "depth of field, balanced exposure, high quality inspection-bay photography. "
        "Leave clean negative space for captions in the upper third and lower third. "
        "No text, no logos, no watermark."
    )


def _car_motion_prompt(scene: dict[str, Any]) -> str:
    motion = _motion_directive(scene)
    mechanism_clause = ""
    if scene.get("purpose") == "problem":
        mechanism_clause = " A subtle surface layer starts to peel away visually, hinting that the visible price is not the full system."
    return (
        "Photorealistic vertical video of the same red used compact car in a bright inspection "
        f"garage. Camera {motion}.{mechanism_clause} Subtle light sweep over the hood and windshield, realistic "
        "reflections, stable composition, balanced exposure, high quality automotive inspection "
        "footage. Preserve the same car and garage. No text, no logo, no watermark."
    )


def _motion_directive(scene: dict[str, Any]) -> str:
    purpose = scene.get("purpose", "")
    if purpose == "pattern_interrupt":
        return "slowly pushes in from a front three-quarter angle, then lands on a subtle freeze-like emphasis"
    if purpose == "problem":
        return "slowly pushes in from a front three-quarter angle with gentle parallax"
    visual_motion = scene.get("visual_motion", "")
    if visual_motion:
        return visual_motion
    return "slowly pushes in with subtle parallax"


def _fallback_keyframe_prompt(scene: dict[str, Any]) -> str:
    return (
        "Photorealistic vertical macro-investigation image, one clear subject in a realistic "
        f"environment. Visual task: {scene['visual_direction']}. Bright balanced exposure, clean "
        "composition, high contrast, enough negative space for renderer captions, no readable text, "
        "no logos, no watermark."
    )


def _fallback_motion_prompt(scene: dict[str, Any]) -> str:
    motion = _motion_directive(scene)
    return (
        "Photorealistic vertical video preserving the same subject and environment from the start "
        f"image. Camera {motion}. Motion supports this retention goal: {scene['retention_goal']}. "
        "Balanced exposure, stable composition, realistic texture. No text, no logo, no watermark."
    )


def _scene_prompt_spec(scene: dict[str, Any], shot_plan: dict[str, Any]) -> dict[str, Any]:
    visual = " ".join(
        [
            shot_plan.get("title", ""),
            shot_plan.get("pillar", ""),
            scene.get("visual_direction", ""),
            scene.get("voiceover", ""),
        ]
    ).lower()
    is_car_scene = "auto" in visual or "car" in visual or "occasion" in visual or "garage" in visual
    keyframe_prompt = _car_keyframe_prompt(scene) if is_car_scene else _fallback_keyframe_prompt(scene)
    motion_prompt = _car_motion_prompt(scene) if is_car_scene else _fallback_motion_prompt(scene)
    negative_prompt = CAR_NEGATIVE_PROMPT if is_car_scene else BASE_NEGATIVE_PROMPT
    return {
        "scene_id": f"scene_{scene['scene']:02d}",
        "source_scene": scene["scene"],
        "purpose": scene["purpose"],
        "asset_type": scene["asset_type"],
        "style_profile": STYLE_PROFILE,
        "wan_preset": WAN22_QUALITY_PRESET,
        "keyframe_prompt": keyframe_prompt,
        "i2v_motion_prompt": motion_prompt,
        "negative_prompt": negative_prompt,
        "motion_directive": _motion_directive(scene),
        "source_visual_direction": scene["visual_direction"],
        "retention_goal": scene["retention_goal"],
        "clip_requirements": {
            "subject_identifiable_first_half_second": True,
            "no_baked_text": True,
            "caption_safe_space": "upper third and lower third",
            "preferred_duration_seconds": max(2, round(float(scene["end"]) - float(scene["start"]), 1)),
            "review_required": True,
        },
        "comfyui": {
            "workflow_family": "native_wan22_ti2v_i2v",
            "model": "wan2.2_ti2v_5B_fp16.safetensors",
            "text_encoder": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
            "vae": "wan2.2_vae.safetensors",
            "resolution": {"width": 416, "height": 736},
            "frames": 61,
            "fps": 12,
            "steps": 16,
            "cfg": 5.0,
        },
    }


def _renderer_note(scene: dict[str, Any]) -> dict[str, Any]:
    return {
        "scene_id": f"scene_{scene['scene']:02d}",
        "source_scene": scene["scene"],
        "purpose": scene["purpose"],
        "asset_type": scene["asset_type"],
        "visual_direction": scene["visual_direction"],
        "visual_motion": scene.get("visual_motion", ""),
        "note": "Renderer-generated motion layer; keep labels, arrows, numbers and captions deterministic.",
    }


def build_i2v_prompt_plan(shot_plan: dict[str, Any]) -> dict[str, Any]:
    """Build Wan2.2 I2V prompt specs from a mechanism shot plan."""
    ai_scenes = [scene for scene in shot_plan["scenes"] if _is_wan_candidate(scene)]
    renderer_scenes = [scene for scene in shot_plan["scenes"] if not _is_wan_candidate(scene)]
    return {
        "schema_version": "i2v_prompt_plan.v1",
        "style_profile": STYLE_PROFILE,
        "video": {
            "id": shot_plan["id"],
            "title": shot_plan["title"],
            "series": shot_plan["series"],
            "pillar": shot_plan["pillar"],
        },
        "strategy": {
            "production_mode": "keyframe_to_wan22_i2v_to_deterministic_renderer",
            "ai_video_role": "photorealistic texture and motion only",
            "renderer_role": "captions, labels, numbers, arrows, mechanism layers and audit-safe truth",
            "static_card_limit": shot_plan.get("final_render_intent", {}).get("static_card_limit", 0),
        },
        "scenes": [_scene_prompt_spec(scene, shot_plan) for scene in ai_scenes],
        "renderer_only_scenes": [_renderer_note(scene) for scene in renderer_scenes],
    }


def _prompt_plan_filename(plan: dict[str, Any]) -> str:
    return f"{_slug(plan['video']['id'])}.json"


def write_i2v_prompt_plans(shot_plans: list[dict[str, Any]], output_dir: Path) -> list[Path]:
    """Write Wan2.2 I2V prompt plans as JSON files."""
    output_dir.mkdir(parents=True, exist_ok=True)
    paths: list[Path] = []
    for shot_plan in shot_plans:
        prompt_plan = build_i2v_prompt_plan(shot_plan)
        path = output_dir / _prompt_plan_filename(prompt_plan)
        path.write_text(json.dumps(prompt_plan, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
        paths.append(path)
    return paths
