import pytest

from autoshorts.content.brief import create_content_brief
from autoshorts.content.script_draft import create_script_draft
from autoshorts.rendering.local_preview import (
    LocalPreviewPlan,
    build_local_preview_plan,
    validate_local_preview_plan,
)
from autoshorts.rendering.manifest import RenderManifest, RenderSettings, build_render_manifest
from autoshorts.rendering.shot_plan import create_shot_plan


def _manifest():
    brief = create_content_brief(
        pillar_id="ai_life_systems",
        topic="AI weekly review routine",
        audience_problem="I use AI tools but still lose control of priorities.",
        payoff="a 10-minute Sunday review that keeps AI as assistant, not boss",
    )
    script = create_script_draft(brief)
    return build_render_manifest(create_shot_plan(script))


def test_build_local_preview_plan_creates_side_effect_free_frame_plan():
    manifest = _manifest()

    plan = build_local_preview_plan(manifest)

    assert isinstance(plan, LocalPreviewPlan)
    assert plan.script_id == "ai_life_systems-ai-weekly-review-routine"
    assert plan.title == "AI weekly review routine"
    assert plan.width == 540
    assert plan.height == 960
    assert plan.fps == 24
    assert plan.output_dir == "data/previews/ai_life_systems-ai-weekly-review-routine"
    assert plan.side_effects == ()
    assert plan.external_calls == ()
    assert len(plan.frames) == len(manifest.timeline)
    assert plan.frames[0].frame_id == "frame-01-first_frame_hook"
    assert plan.frames[0].start_ms == 0
    assert plan.frames[0].duration_ms == 3000
    assert plan.frames[0].scene_purpose == "first_frame_hook"
    assert plan.frames[0].caption_text == "If AI runs your week, you have already lost control."
    assert plan.frames[0].asset_mode == "kinetic_text_overlay"
    assert plan.frames[0].placeholder_image_path == (
        "data/previews/ai_life_systems-ai-weekly-review-routine/frame-01-first_frame_hook.png"
    )
    assert plan.frames[-1].start_ms == 30000
    assert plan.frames[-1].duration_ms == 5000


def test_validate_local_preview_plan_accepts_default_plan():
    result = validate_local_preview_plan(build_local_preview_plan(_manifest()))

    assert result.is_valid is True
    assert result.errors == ()


def test_build_local_preview_plan_rejects_invalid_manifest():
    manifest = _manifest()
    invalid = RenderManifest(
        script_id=manifest.script_id,
        title=manifest.title,
        settings=RenderSettings(width=1920, height=1080),
        timeline=manifest.timeline,
    )

    with pytest.raises(ValueError, match="render size must be vertical 1080x1920"):
        build_local_preview_plan(invalid)


def test_validate_local_preview_plan_rejects_side_effects_external_calls_and_bad_size():
    plan = build_local_preview_plan(_manifest())
    unsafe = LocalPreviewPlan(
        script_id=plan.script_id,
        title=plan.title,
        width=1920,
        height=1080,
        fps=0,
        output_dir=plan.output_dir,
        frames=plan.frames,
        side_effects=("write_png",),
        external_calls=("ffmpeg",),
    )

    result = validate_local_preview_plan(unsafe)

    assert result.is_valid is False
    assert "preview size must be vertical" in result.errors
    assert "fps must be positive" in result.errors
    assert "preview plan must not have side effects" in result.errors
    assert "preview plan must not call external tools" in result.errors


def test_validate_local_preview_plan_rejects_bad_frame_timing_and_missing_fields():
    plan = build_local_preview_plan(_manifest())
    first = plan.frames[0]
    bad_frame = type(first)(
        frame_id="",
        scene_purpose="",
        start_ms=1000,
        duration_ms=0,
        caption_text="",
        label_text="",
        asset_mode="",
        motion_instruction="",
        placeholder_image_path="",
    )
    unsafe = LocalPreviewPlan(
        script_id=plan.script_id,
        title=plan.title,
        width=plan.width,
        height=plan.height,
        fps=plan.fps,
        output_dir=plan.output_dir,
        frames=(bad_frame,) + plan.frames[1:],
        side_effects=(),
        external_calls=(),
    )

    result = validate_local_preview_plan(unsafe)

    assert result.is_valid is False
    assert "frame 1 frame_id is required" in result.errors
    assert "frame 1 duration_ms must be positive" in result.errors
    assert "first frame must start at 0ms" in result.errors
    assert "frame 1 caption_text is required" in result.errors
    assert "frame 1 placeholder_image_path is required" in result.errors
