from pathlib import Path
import subprocess

from autoshorts.audio.final_preview import (
    AudioCaptionPreviewPlan,
    build_audio_caption_preview_plan,
    render_audio_caption_preview,
    validate_audio_caption_preview_plan,
)
from autoshorts.audio.subtitles import build_final_motion_subtitle_track, build_hormozi_subtitle_track


def test_build_audio_caption_preview_plan_is_side_effect_free_and_writes_ass_content(tmp_path):
    video_path = tmp_path / "timing.mp4"
    audio_path = tmp_path / "voice.mp3"
    track = build_hormozi_subtitle_track("Most AI automations quietly rot when workflows lack boundaries", total_duration_seconds=5)

    plan = build_audio_caption_preview_plan(
        video_path=video_path,
        audio_path=audio_path,
        subtitle_track=track,
        output_path=tmp_path / "final-preview.mp4",
    )

    assert plan.video_path == str(video_path)
    assert plan.audio_path == str(audio_path)
    assert plan.output_path.endswith("final-preview.mp4")
    assert plan.subtitle_file_path.endswith("final-preview.ass")
    assert plan.duration_mode == "preserve_video"
    assert "[Script Info]" in plan.subtitle_file_content
    assert "Dialogue:" in plan.subtitle_file_content
    assert "MOST AI AUTOMATIONS" in plan.subtitle_file_content
    assert plan.side_effects == ()
    assert plan.external_calls == ()


def test_render_ass_subtitle_file_uses_compact_final_motion_style():
    track = build_final_motion_subtitle_track(
        "Before you automate anything, decide three things: what should go through, what should be reviewed, and what should stop.",
        total_duration_seconds=6,
    )
    plan = build_audio_caption_preview_plan(
        video_path="timing.mp4",
        audio_path="voice.mp3",
        subtitle_track=track,
        output_path="final-preview.mp4",
    )

    assert track.style.name == "truetrace_final_motion_compact"
    assert all(1 <= len(cue.text.split()) <= 4 for cue in track.cues)
    assert "ANYTHING DECIDE THREE" not in plan.subtitle_file_content
    assert "Style: TrueTraceCaption,Arial,72," in plan.subtitle_file_content
    assert ",80,80,320,1" in plan.subtitle_file_content


def test_validate_audio_caption_preview_plan_rejects_bad_paths_and_unsafe_side_effects(tmp_path):
    track = build_hormozi_subtitle_track("valid subtitle text", total_duration_seconds=2)
    plan = AudioCaptionPreviewPlan(
        video_path="video.mov",
        audio_path="voice.wav",
        output_path="out.avi",
        subtitle_file_path="out.srt",
        subtitle_file_content="",
        subtitle_track=track,
        duration_mode="shortest",
        side_effects=("write_file",),
        external_calls=("ffmpeg",),
    )

    validation = validate_audio_caption_preview_plan(plan)

    assert validation.is_valid is False
    assert "video_path must end with .mp4" in validation.errors
    assert "audio_path must end with .mp3" in validation.errors
    assert "output_path must end with .mp4" in validation.errors
    assert "subtitle_file_path must end with .ass" in validation.errors
    assert "duration_mode must be preserve_video or trim_to_audio" in validation.errors
    assert "audio caption preview plans must not have side effects" in validation.errors


def test_build_audio_caption_preview_plan_can_explicitly_trim_to_audio(tmp_path):
    track = build_hormozi_subtitle_track("Prompt is not system workflow boundaries matter", total_duration_seconds=4)

    plan = build_audio_caption_preview_plan(
        video_path=tmp_path / "timing.mp4",
        audio_path=tmp_path / "voice.mp3",
        subtitle_track=track,
        output_path=tmp_path / "final.mp4",
        duration_mode="trim_to_audio",
    )

    assert plan.duration_mode == "trim_to_audio"
    assert plan.side_effects == ()
    assert plan.external_calls == ()


def test_render_audio_caption_preview_preserves_video_duration_by_default(tmp_path):
    input_video = tmp_path / "timing.mp4"
    audio = tmp_path / "voice.mp3"
    output = tmp_path / "final.mp4"
    input_video.write_bytes(b"fake video")
    audio.write_bytes(b"fake audio")
    track = build_hormozi_subtitle_track("Prompt is not system workflow boundaries matter", total_duration_seconds=4)
    plan = build_audio_caption_preview_plan(
        video_path=input_video,
        audio_path=audio,
        subtitle_track=track,
        output_path=output,
    )
    calls = []

    def fake_runner(command, *, capture_output, text, check):
        calls.append(command)
        output.write_bytes(b"fake final video")
        return subprocess.CompletedProcess(command, 0, stdout="ok", stderr="")

    result = render_audio_caption_preview(plan, runner=fake_runner)

    assert Path(plan.subtitle_file_path).exists()
    assert output.exists()
    assert calls[0][:5] == ["ffmpeg", "-y", "-i", str(input_video), "-i"]
    assert "ass=" in " ".join(calls[0])
    assert "-shortest" not in calls[0]
    assert result.output_path == str(output)
    assert result.duration_mode == "preserve_video"
    assert result.side_effects == ("write_subtitles", "call_ffmpeg", "write_mp4")
    assert result.external_calls == ("ffmpeg",)


def test_render_audio_caption_preview_trims_only_when_explicit(tmp_path):
    input_video = tmp_path / "timing.mp4"
    audio = tmp_path / "voice.mp3"
    output = tmp_path / "final.mp4"
    input_video.write_bytes(b"fake video")
    audio.write_bytes(b"fake audio")
    track = build_hormozi_subtitle_track("Prompt is not system workflow boundaries matter", total_duration_seconds=4)
    plan = build_audio_caption_preview_plan(
        video_path=input_video,
        audio_path=audio,
        subtitle_track=track,
        output_path=output,
        duration_mode="trim_to_audio",
    )
    calls = []

    def fake_runner(command, *, capture_output, text, check):
        calls.append(command)
        output.write_bytes(b"fake final video")
        return subprocess.CompletedProcess(command, 0, stdout="ok", stderr="")

    result = render_audio_caption_preview(plan, runner=fake_runner)

    assert "-shortest" in calls[0]
    assert result.duration_mode == "trim_to_audio"
