from pathlib import Path

import pytest

from autoshorts.content.brief import create_content_brief
from autoshorts.content.script_draft import create_script_draft
from autoshorts.rendering.ffmpeg_plan import FFmpegConcatPlan, build_ffmpeg_concat_plan, validate_ffmpeg_concat_plan
from autoshorts.rendering.local_frame_writer import LocalFrameWriteResult, write_local_preview_frames
from autoshorts.rendering.local_preview import build_local_preview_plan
from autoshorts.rendering.manifest import build_render_manifest
from autoshorts.rendering.shot_plan import create_shot_plan


def _preview_plan_and_frames(tmp_path):
    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)
    preview_plan = build_local_preview_plan(build_render_manifest(create_shot_plan(script)))
    write_result = write_local_preview_frames(preview_plan, output_dir=tmp_path)
    return preview_plan, write_result


def test_build_ffmpeg_concat_plan_creates_side_effect_free_concat_plan(tmp_path):
    preview_plan, write_result = _preview_plan_and_frames(tmp_path)

    plan = build_ffmpeg_concat_plan(preview_plan, write_result, output_path=tmp_path / "preview.mp4")

    assert isinstance(plan, FFmpegConcatPlan)
    assert plan.script_id == "ai_life_systems-ai-weekly-review-routine"
    assert plan.output_path == str(tmp_path / "preview.mp4")
    assert plan.concat_file_path == str(tmp_path / "preview.concat.txt")
    assert plan.width == 540
    assert plan.height == 960
    assert plan.fps == 24
    assert plan.side_effects == ()
    assert plan.external_calls == ()
    assert len(plan.entries) == len(preview_plan.frames)
    assert plan.entries[0].image_path.endswith("frame-01-first_frame_hook.png")
    assert plan.entries[0].duration_seconds == 3.0
    assert plan.entries[-1].duration_seconds == 5.0
    assert "file '" in plan.concat_file_content
    assert "duration 3.000" in plan.concat_file_content
    assert plan.concat_file_content.strip().endswith("frame-05-cta.png'")


def test_validate_ffmpeg_concat_plan_accepts_default_plan(tmp_path):
    preview_plan, write_result = _preview_plan_and_frames(tmp_path)

    result = validate_ffmpeg_concat_plan(
        build_ffmpeg_concat_plan(preview_plan, write_result, output_path=tmp_path / "preview.mp4")
    )

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


def test_build_ffmpeg_concat_plan_rejects_mismatched_frame_count(tmp_path):
    preview_plan, write_result = _preview_plan_and_frames(tmp_path)
    mismatched = LocalFrameWriteResult(
        script_id=write_result.script_id,
        output_dir=write_result.output_dir,
        frame_paths=write_result.frame_paths[:-1],
        side_effects=write_result.side_effects,
        external_calls=write_result.external_calls,
    )

    with pytest.raises(ValueError, match="frame count must match preview plan"):
        build_ffmpeg_concat_plan(preview_plan, mismatched, output_path=tmp_path / "preview.mp4")


def test_build_ffmpeg_concat_plan_rejects_non_png_frame_paths(tmp_path):
    preview_plan, write_result = _preview_plan_and_frames(tmp_path)
    bad = LocalFrameWriteResult(
        script_id=write_result.script_id,
        output_dir=write_result.output_dir,
        frame_paths=(str(Path(write_result.frame_paths[0]).with_suffix(".jpg")),) + write_result.frame_paths[1:],
        side_effects=write_result.side_effects,
        external_calls=write_result.external_calls,
    )

    with pytest.raises(ValueError, match="frame paths must be png files"):
        build_ffmpeg_concat_plan(preview_plan, bad, output_path=tmp_path / "preview.mp4")


def test_validate_ffmpeg_concat_plan_rejects_bad_output_and_external_calls(tmp_path):
    preview_plan, write_result = _preview_plan_and_frames(tmp_path)
    plan = build_ffmpeg_concat_plan(preview_plan, write_result, output_path=tmp_path / "preview.mp4")
    unsafe = FFmpegConcatPlan(
        script_id=plan.script_id,
        output_path=str(tmp_path / "preview.mov"),
        concat_file_path=plan.concat_file_path,
        width=0,
        height=plan.height,
        fps=0,
        entries=plan.entries,
        concat_file_content=plan.concat_file_content,
        side_effects=("write_concat",),
        external_calls=("ffmpeg",),
    )

    result = validate_ffmpeg_concat_plan(unsafe)

    assert result.is_valid is False
    assert "output_path must end with .mp4" in result.errors
    assert "width and height must be positive" in result.errors
    assert "fps must be positive" in result.errors
    assert "ffmpeg concat plans must not have side effects" in result.errors
    assert "ffmpeg concat plans must not call external tools" in result.errors
