"""
tests/test_export.py — Unit tests for timecode conversion and export writers

Tests use synthetic EditTimeline objects (no real video files needed).
"""

from __future__ import annotations

from pathlib import Path

import pytest

from src.export.timecode import (
    seconds_to_fcpxml,
    seconds_to_smpte,
    fcpxml_frame_duration,
    fcpxml_format_name,
    seconds_to_frame_count,
)


# ---------------------------------------------------------------------------
# Timecode helpers
# ---------------------------------------------------------------------------

class TestSecondsToFcpxml:
    def test_zero(self) -> None:
        assert seconds_to_fcpxml(0.0, 24.0) == "0s"

    def test_one_second_at_24fps(self) -> None:
        # 1.0s @ 24fps → 24 frames → 24/24s = 1/1s
        result = seconds_to_fcpxml(1.0, 24.0)
        assert result == "1/1s"

    def test_one_second_at_23976(self) -> None:
        # 1s @ 23.976 → 24000/24000 * 1001/1001 = 1001/1000 ... let's just check it's rational
        result = seconds_to_fcpxml(1.0, 23.976)
        assert result.endswith("s")
        assert "/" in result

    def test_ten_seconds_at_25fps(self) -> None:
        # 10s @ 25fps → 250 frames → 250/25s = 10/1s
        result = seconds_to_fcpxml(10.0, 25.0)
        assert result == "10/1s"

    def test_rational_is_reduced(self) -> None:
        # Should never produce 24/24s
        result = seconds_to_fcpxml(1.0, 24.0)
        num, den = result.rstrip("s").split("/")
        from math import gcd
        assert gcd(int(num), int(den)) == 1


class TestSecondsToSmpte:
    def test_zero(self) -> None:
        assert seconds_to_smpte(0.0, 24.0) == "00:00:00:00"

    def test_one_minute(self) -> None:
        assert seconds_to_smpte(60.0, 25.0) == "00:01:00:00"

    def test_one_hour(self) -> None:
        assert seconds_to_smpte(3600.0, 24.0) == "01:00:00:00"

    def test_frames_overflow(self) -> None:
        # 25fps: 26 frames → 1s + 1 frame = 00:00:01:01
        result = seconds_to_smpte(26 / 25, 25.0)
        assert result == "00:00:01:01"

    def test_format_length(self) -> None:
        result = seconds_to_smpte(123.456, 23.976)
        parts = result.split(":")
        assert len(parts) == 4
        assert all(len(p) == 2 for p in parts)


class TestFcpxmlHelpers:
    def test_frame_duration_24fps(self) -> None:
        assert fcpxml_frame_duration(24.0) == "1/24s"

    def test_frame_duration_23976(self) -> None:
        fd = fcpxml_frame_duration(23.976)
        # Should be "1001/24000s"
        assert fd == "1001/24000s"

    def test_format_name_1080p_2398(self) -> None:
        name = fcpxml_format_name(23.976, 1920, 1080)
        assert "1080" in name
        assert "2398" in name

    def test_frame_count_roundtrip(self) -> None:
        fps = 25.0
        seconds = 10.0
        frames = seconds_to_frame_count(seconds, fps)
        assert frames == 250


# ---------------------------------------------------------------------------
# EDL writer (string output)
# ---------------------------------------------------------------------------

class TestEdlWriter:
    def _make_timeline(self) -> "src.core.models.EditTimeline":  # type: ignore
        from src.core.models import (
            BeatType, EditClip, EditTimeline, MatchResult, TrailerBeat,
        )

        beat = TrailerBeat(
            beat_id=0, trailer_path=Path("trailer.mp4"),
            start_s=0.0, end_s=5.0, start_frame=0, end_frame=120,
            beat_type=BeatType.HOOK,
        )
        match = MatchResult(
            beat_id=0, scene_id=3,
            source_path=Path("movie.mp4"),
            in_point_s=30.0, out_point_s=35.0,
            in_point_frame=720, match_score=0.88,
        )
        clip = EditClip(
            clip_index=0, beat=beat, match=match,
            timeline_start_s=0.0, timeline_end_s=5.0,
        )
        return EditTimeline(
            title="TestTrailer", frame_rate=25.0, clips=(clip,)
        )

    def test_edl_contains_title(self, tmp_path: Path) -> None:
        from src.core.config import load_config
        from src.export.edl_writer import write_edl

        cfg = load_config()
        tl  = self._make_timeline()
        out = write_edl(tl, cfg, output_path=tmp_path / "test.edl")

        text = out.read_text(encoding="utf-8")
        assert "TITLE: TestTrailer" in text

    def test_edl_has_event_line(self, tmp_path: Path) -> None:
        from src.core.config import load_config
        from src.export.edl_writer import write_edl

        cfg = load_config()
        tl  = self._make_timeline()
        out = write_edl(tl, cfg, output_path=tmp_path / "test.edl")

        text = out.read_text(encoding="utf-8")
        assert "001" in text   # event number
        assert "AX" in text    # reel name


# ---------------------------------------------------------------------------
# FCPXML writer (XML structure)
# ---------------------------------------------------------------------------

class TestFcpxmlWriter:
    def _make_timeline(self) -> "src.core.models.EditTimeline":  # type: ignore
        from src.core.models import (
            BeatType, EditClip, EditTimeline, MatchResult, TrailerBeat,
        )

        beat = TrailerBeat(
            beat_id=0, trailer_path=Path("trailer.mp4"),
            start_s=0.0, end_s=5.0, start_frame=0, end_frame=120,
            beat_type=BeatType.HOOK,
        )
        match = MatchResult(
            beat_id=0, scene_id=3,
            source_path=Path("B:/Proxy/movie.mp4"),
            in_point_s=30.0, out_point_s=35.0,
            in_point_frame=720, match_score=0.88,
        )
        clip = EditClip(
            clip_index=0, beat=beat, match=match,
            timeline_start_s=0.0, timeline_end_s=5.0,
        )
        return EditTimeline(
            title="TestTrailer", frame_rate=25.0, clips=(clip,)
        )

    def test_fcpxml_is_valid_xml(self, tmp_path: Path) -> None:
        from xml.etree import ElementTree as ET
        from src.core.config import load_config
        from src.export.fcpxml_writer import write_fcpxml

        cfg = load_config()
        tl  = self._make_timeline()
        out = write_fcpxml(tl, cfg, output_path=tmp_path / "test.fcpxml")

        text = out.read_text(encoding="utf-8")
        text_no_doctype = "\n".join(
            line for line in text.splitlines()
            if not line.strip().startswith("<!DOCTYPE")
        )
        root = ET.fromstring(text_no_doctype)
        # Strip namespace prefix for comparison
        local_tag = root.tag.split("}")[-1] if "}" in root.tag else root.tag
        assert local_tag == "fcpxml"

    def test_fcpxml_has_spine(self, tmp_path: Path) -> None:
        from xml.etree import ElementTree as ET
        from src.core.config import load_config
        from src.export.fcpxml_writer import write_fcpxml

        cfg = load_config()
        tl  = self._make_timeline()
        out = write_fcpxml(tl, cfg, output_path=tmp_path / "test.fcpxml")

        text = out.read_text(encoding="utf-8")
        text_no_doctype = "\n".join(
            line for line in text.splitlines()
            if not line.strip().startswith("<!DOCTYPE")
        )
        # Register the FCPXML namespace so find() works
        ns = {"fcp": "http://www.apple.com/dt/FCPXML/1_10"}
        root  = ET.fromstring(text_no_doctype)
        spine = root.find(".//fcp:spine", ns)
        assert spine is not None
        clips = list(spine)
        assert len(clips) == 1
