import json
import shutil
import subprocess
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 build_ffmpeg_concat_plan
from autoshorts.rendering.ffmpeg_renderer import FFmpegRenderResult, render_ffmpeg_preview_video
from autoshorts.rendering.local_frame_writer import 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

pytestmark = pytest.mark.skipif(shutil.which("ffmpeg") is None, reason="ffmpeg is required")


def _concat_plan(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 / "frames")
    return build_ffmpeg_concat_plan(preview_plan, write_result, output_path=tmp_path / "preview.mp4")


def test_render_ffmpeg_preview_video_writes_concat_file_and_mp4(tmp_path):
    plan = _concat_plan(tmp_path)

    result = render_ffmpeg_preview_video(plan)

    assert isinstance(result, FFmpegRenderResult)
    assert result.script_id == "ai_life_systems-ai-weekly-review-routine"
    assert result.output_path == plan.output_path
    assert result.concat_file_path == plan.concat_file_path
    assert result.side_effects == ("write_concat", "call_ffmpeg", "write_mp4")
    assert result.external_calls == ("ffmpeg",)
    assert result.returncode == 0
    assert Path(result.concat_file_path).exists()
    assert Path(result.concat_file_path).read_text() == plan.concat_file_content
    output = Path(result.output_path)
    assert output.exists()
    assert output.stat().st_size > 0


@pytest.mark.skipif(shutil.which("ffprobe") is None, reason="ffprobe is required")
def test_render_ffmpeg_preview_video_smoke_probes_dimensions_and_duration(tmp_path):
    plan = _concat_plan(tmp_path)

    result = render_ffmpeg_preview_video(plan)

    probe = subprocess.run(
        [
            "ffprobe",
            "-v",
            "error",
            "-select_streams",
            "v:0",
            "-show_entries",
            "stream=width,height,r_frame_rate,duration",
            "-of",
            "json",
            result.output_path,
        ],
        check=True,
        capture_output=True,
        text=True,
    )
    stream = json.loads(probe.stdout)["streams"][0]
    assert stream["width"] == 540
    assert stream["height"] == 960
    assert stream["r_frame_rate"] == "24/1"
    assert float(stream["duration"]) == pytest.approx(35.0, abs=1.0)


def test_render_ffmpeg_preview_video_rejects_invalid_plan(tmp_path):
    plan = _concat_plan(tmp_path)
    invalid = type(plan)(
        script_id=plan.script_id,
        output_path=str(tmp_path / "preview.mov"),
        concat_file_path=plan.concat_file_path,
        width=plan.width,
        height=plan.height,
        fps=plan.fps,
        entries=plan.entries,
        concat_file_content=plan.concat_file_content,
        side_effects=plan.side_effects,
        external_calls=plan.external_calls,
    )

    with pytest.raises(ValueError, match="output_path must end with .mp4"):
        render_ffmpeg_preview_video(invalid)
