"""Renderer manifest generation for retention-led AutoShortsBot videos."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from autoshorts.script_drafts import _slug

RESOLUTION = {"width": 1080, "height": 1920}
ASPECT_RATIO = "9:16"
FPS = 30


ASSET_REQUIREMENT_HINTS = {
    "kinetic_text": "Generated by renderer from text and style instructions.",
    "screen_recording": "Needs captured or synthesized vertical screen recording; avoid fake unreadable UI text.",
    "card_animation": "Generated by renderer from concise text cards; animate one idea per beat.",
    "stock_broll": "Needs human/phone/context B-roll; avoid generic corporate stock feel.",
    "generated_background": "Optional abstract background only; must not become the main content.",
    "cinematic_ai_video": "Needs photorealistic vertical AI-video clip; no baked text, logos, or watermarks.",
    "mechanism_animation": "Needs renderer-generated mechanism/layer animation with visible movement every 1-2 seconds.",
    "kinetic_text_overlay": "Generated by renderer as large beat-synced caption overlays, not paragraph cards.",
}


def _seconds_to_ms(value: int | float) -> int:
    return round(float(value) * 1000)


def _duration_ms(start: int | float, end: int | float) -> int:
    return max(500, _seconds_to_ms(end) - _seconds_to_ms(start))


def _placeholder(asset_type: str, video_id: str, scene: int) -> str:
    return f"placeholder://{video_id}/scene-{scene:02d}/{asset_type}"


def _visual_style(scene: dict[str, Any]) -> dict[str, Any]:
    base = {
        "safe_area_pct": 8,
        "font": "bold_sans",
        "caption_case": "sentence",
        "motion": "push_zoom" if scene["scene"] == 1 else "cut_or_slide",
        "contrast": "high",
    }
    if scene["asset_type"] == "card_animation":
        base.update({"layout": "minimal_cards", "max_words_on_screen": 18})
    elif scene["asset_type"] == "screen_recording":
        base.update({"layout": "screen_plus_caption", "blur_sensitive_ui": True})
    elif scene["asset_type"] == "stock_broll":
        base.update({"layout": "broll_under_caption", "pace": "fast_cut"})
    elif scene["asset_type"] == "cinematic_ai_video":
        base.update({"layout": "cinematic_under_caption", "no_baked_text": True, "pace": "fast_cut"})
    elif scene["asset_type"] == "mechanism_animation":
        base.update({"layout": "xray_layer_reveal", "max_words_on_screen": 10, "layered_motion": True})
    elif scene["asset_type"] == "kinetic_text_overlay":
        base.update({"layout": "kinetic_full_frame", "max_words_on_screen": 12, "beat_synced": True})
    return base


def _visual_timeline_items(plan: dict[str, Any]) -> list[dict[str, Any]]:
    video_id = plan["id"]
    items: list[dict[str, Any]] = []
    for scene in plan["scenes"]:
        items.append(
            {
                "id": f"scene_{scene['scene']:02d}",
                "track": "visual",
                "type": scene["asset_type"],
                "asset_ref": _placeholder(scene["asset_type"], video_id, scene["scene"]),
                "start_ms": _seconds_to_ms(scene["start"]),
                "duration_ms": _duration_ms(scene["start"], scene["end"]),
                "text": scene["on_screen_text"],
                "visual_direction": scene["visual_direction"],
                "visual_motion": scene.get("visual_motion", "cut_or_slide"),
                "sound_cue": scene.get("sound_cue", "none"),
                "retention_goal": scene["retention_goal"],
                "style": _visual_style(scene),
            }
        )
    return items


def _caption_timeline_items(plan: dict[str, Any]) -> list[dict[str, Any]]:
    beats = plan.get("caption_beats", [])
    total_ms = int(plan["estimated_seconds"]) * 1000
    items: list[dict[str, Any]] = []
    for index, beat in enumerate(beats):
        start_ms = _seconds_to_ms(beat["start"])
        next_start_ms = _seconds_to_ms(beats[index + 1]["start"]) if index + 1 < len(beats) else total_ms
        items.append(
            {
                "id": f"caption_{index + 1:02d}",
                "track": "captions",
                "type": "kinetic_caption",
                "start_ms": start_ms,
                "duration_ms": max(750, next_start_ms - start_ms),
                "text": beat["text"],
                "style": {
                    "safe_area_pct": 8,
                    "position": "lower_third",
                    "max_chars_per_line": 28,
                    "highlight_keywords": True,
                },
            }
        )
    return items


def _asset_requirements(plan: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
    requirements: dict[str, list[dict[str, Any]]] = {key: [] for key in ASSET_REQUIREMENT_HINTS}
    for scene in plan["scenes"]:
        asset_type = scene["asset_type"]
        requirements.setdefault(asset_type, []).append(
            {
                "scene": scene["scene"],
                "asset_ref": _placeholder(asset_type, plan["id"], scene["scene"]),
                "brief": scene["visual_direction"],
                "hint": ASSET_REQUIREMENT_HINTS.get(asset_type, "Provide asset matching the visual direction."),
            }
        )
    return requirements


def build_render_manifest(plan: dict[str, Any]) -> dict[str, Any]:
    """Convert a human shot plan into a structured renderer contract."""
    timeline = _visual_timeline_items(plan) + _caption_timeline_items(plan)
    track_order = {"visual": 0, "captions": 1}
    timeline.sort(key=lambda item: (item["start_ms"], track_order.get(item["track"], 99), item["id"]))
    return {
        "schema_version": "render_manifest.v1",
        "video": {
            "id": plan["id"],
            "title": plan["title"],
            "pillar": plan["pillar"],
            "duration_ms": int(plan["estimated_seconds"]) * 1000,
            "aspect_ratio": ASPECT_RATIO,
            "resolution": RESOLUTION,
            "fps": FPS,
        },
        "timeline": timeline,
        "asset_requirements": _asset_requirements(plan),
        "render_constraints": {
            "avoid_static_slideshow": True,
            "pattern_interrupt_every_seconds": 3,
            "visual_change_every_seconds": plan.get("final_render_intent", {}).get("visual_change_every_seconds", 3),
            "first_frame_hook_required": True,
            "max_words_per_visual_card": 18,
            "max_static_cards": plan.get("final_render_intent", {}).get("static_card_limit", 1),
            "human_approval_required_before_publish": True,
        },
        "production_notes": plan.get("production_notes", []),
    }


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


def write_render_manifests(plans: list[dict[str, Any]], output_dir: Path) -> list[Path]:
    """Write render manifests as pretty JSON files."""
    output_dir.mkdir(parents=True, exist_ok=True)
    paths: list[Path] = []
    for plan in plans:
        manifest = build_render_manifest(plan)
        path = output_dir / _manifest_filename(plan)
        path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
        paths.append(path)
    return paths
