"""
Tests for annotation format converters.
"""

import csv
import json
import shutil
import tempfile
from pathlib import Path

import pytest

from sltk.io.annotations import SCHEMA_VERSION, read_unified_annotation
from sltk.io.converters import (
    _parse_timecode,
    _parse_vtt,
    _sanitize_filename,
    convert_asl_citizen_csv,
    convert_asldict_json,
    convert_csl_daily_json,
    convert_dataset,
    convert_how2sign_csv,
    convert_msasl_json,
    convert_phoenix_csv,
    convert_vtt,
    convert_vtt_directory,
    convert_wlasl_json,
    convert_ytsl_tsv,
    get_converter,
    list_converters,
)


@pytest.fixture
def temp_dir():
    """Create a temporary directory for tests."""
    temp_path = Path(tempfile.mkdtemp())
    yield temp_path
    shutil.rmtree(temp_path)


class TestConverterRegistry:
    """Tests for converter registry."""

    def test_list_converters(self):
        """Test listing available converters."""
        converters = list_converters()
        assert len(converters) > 0
        assert "elan" in converters
        assert "wlasl_json" in converters
        assert "vtt" in converters

    def test_get_converter(self):
        """Test getting a converter by name."""
        converter = get_converter("elan")
        assert converter is not None
        assert callable(converter)

    def test_get_nonexistent_converter(self):
        """Test getting a non-existent converter."""
        converter = get_converter("nonexistent_format")
        assert converter is None


class TestSanitizeFilename:
    """Tests for filename sanitization."""

    def test_basic_sanitization(self):
        """Test basic filename sanitization."""
        assert _sanitize_filename("hello_world") == "hello_world"
        assert _sanitize_filename("hello world") == "hello world"

    def test_special_characters(self):
        """Test removal of problematic characters."""
        assert "/" not in _sanitize_filename("path/to/file")
        assert ":" not in _sanitize_filename("file:name")
        assert "<" not in _sanitize_filename("file<name>")

    def test_length_limit(self):
        """Test filename length limiting."""
        long_name = "a" * 300
        result = _sanitize_filename(long_name)
        assert len(result) <= 200

    def test_empty_string(self):
        """Test empty string handling."""
        assert _sanitize_filename("") == "unknown"
        assert _sanitize_filename("   ") == "unknown"


class TestParseTimecode:
    """Tests for timecode parsing."""

    def test_hms_format(self):
        """Test HH:MM:SS.mmm format."""
        assert _parse_timecode("00:01:30.500") == 90.5
        assert _parse_timecode("01:00:00.000") == 3600.0
        assert _parse_timecode("00:00:01.000") == 1.0

    def test_comma_separator(self):
        """Test comma as decimal separator (SRT format)."""
        assert _parse_timecode("00:01:30,500") == 90.5

    def test_invalid_format(self):
        """Test invalid timecode formats."""
        assert _parse_timecode("invalid") is None
        assert _parse_timecode("1:30") is None


class TestParseVTT:
    """Tests for VTT parsing."""

    def test_parse_vtt_basic(self, temp_dir):
        """Test basic VTT parsing."""
        vtt_content = """WEBVTT

00:00:01.000 --> 00:00:04.000
Hello world

00:00:05.000 --> 00:00:08.000
This is a test
"""
        vtt_file = temp_dir / "test.vtt"
        with open(vtt_file, "w") as f:
            f.write(vtt_content)

        segments = _parse_vtt(vtt_file)

        assert len(segments) == 2
        assert segments[0].start == 1.0
        assert segments[0].end == 4.0
        assert segments[0].label == "Hello world"

    def test_parse_srt(self, temp_dir):
        """Test SRT format parsing."""
        srt_content = """1
00:00:01,000 --> 00:00:04,000
Hello world

2
00:00:05,000 --> 00:00:08,000
This is a test
"""
        srt_file = temp_dir / "test.srt"
        with open(srt_file, "w") as f:
            f.write(srt_content)

        segments = _parse_vtt(srt_file)

        assert len(segments) == 2
        assert segments[0].start == 1.0

    def test_parse_vtt_with_html(self, temp_dir):
        """Test VTT with HTML tags removed."""
        vtt_content = """WEBVTT

00:00:01.000 --> 00:00:04.000
<b>Bold text</b> and <i>italic</i>
"""
        vtt_file = temp_dir / "test.vtt"
        with open(vtt_file, "w") as f:
            f.write(vtt_content)

        segments = _parse_vtt(vtt_file)

        assert "<b>" not in segments[0].label
        assert "<i>" not in segments[0].label
        assert "Bold text" in segments[0].label


class TestWLASLConverter:
    """Tests for WLASL JSON converter."""

    def test_convert_basic(self, temp_dir):
        """Test basic WLASL conversion."""
        wlasl_data = [
            {
                "gloss": "HELLO",
                "instances": [
                    {
                        "video_id": "vid001",
                        "split": "train",
                        "frame_start": 0,
                        "frame_end": 50,
                        "fps": 25,
                        "signer_id": "signer_01",
                    }
                ],
            },
            {
                "gloss": "WORLD",
                "instances": [
                    {
                        "video_id": "vid002",
                        "split": "test",
                        "frame_start": 10,
                        "frame_end": 60,
                        "fps": 25,
                    }
                ],
            },
        ]

        json_file = temp_dir / "wlasl.json"
        with open(json_file, "w") as f:
            json.dump(wlasl_data, f)

        output_dir = temp_dir / "output"
        output_files = convert_wlasl_json(json_file, output_dir, dataset="wlasl")

        assert len(output_files) == 2
        assert all(f.exists() for f in output_files)

        # Check first sample
        sample = read_unified_annotation(output_files[0])
        assert sample.glosses == ["HELLO"]
        assert sample.split == "train"
        assert sample.signer_id == "signer_01"

    def test_convert_preserves_timing(self, temp_dir):
        """Test that timing info is preserved."""
        wlasl_data = [
            {
                "gloss": "TEST",
                "instances": [
                    {
                        "video_id": "vid001",
                        "frame_start": 25,  # 1 second at 25fps
                        "frame_end": 75,  # 3 seconds at 25fps
                        "fps": 25,
                    }
                ],
            },
        ]

        json_file = temp_dir / "wlasl.json"
        with open(json_file, "w") as f:
            json.dump(wlasl_data, f)

        output_dir = temp_dir / "output"
        convert_wlasl_json(json_file, output_dir)

        sample = read_unified_annotation(output_dir / "TEST_vid001.json")
        assert sample.segments is not None
        assert len(sample.segments) == 1
        assert sample.segments[0].start == 1.0
        assert sample.segments[0].end == 3.0


class TestMSASLConverter:
    """Tests for MS-ASL JSON converter."""

    def test_convert_basic(self, temp_dir):
        """Test basic MS-ASL conversion."""
        msasl_data = [
            {
                "clean_text": "HELLO",
                "video_id": "vid001",
                "start_time": 0.5,
                "end_time": 2.0,
                "split": "train",
            },
        ]

        json_file = temp_dir / "msasl.json"
        with open(json_file, "w") as f:
            json.dump(msasl_data, f)

        output_dir = temp_dir / "output"
        output_files = convert_msasl_json(json_file, output_dir)

        assert len(output_files) == 1
        sample = read_unified_annotation(output_files[0])
        assert sample.glosses == ["HELLO"]


class TestASLDictConverter:
    """Tests for ASLDict JSON converter."""

    def test_convert_basic(self, temp_dir):
        """Test basic ASLDict conversion."""
        asldict_data = [
            {
                "word": "hello",
                "word_page_link": "https://example.com/hello",
                "entries": [
                    {
                        "entry_id": "source-hello-001",
                        "entry_word": "hello",
                        "entry_source": "TestSource",
                        "entry_video_link": "https://example.com/videos/hello.mp4",
                    }
                ],
            },
        ]

        json_file = temp_dir / "asldict.json"
        with open(json_file, "w") as f:
            json.dump(asldict_data, f)

        output_dir = temp_dir / "output"
        output_files = convert_asldict_json(json_file, output_dir)

        assert len(output_files) == 1
        sample = read_unified_annotation(output_files[0])
        assert sample.glosses == ["hello"]
        assert sample.metadata.get("entry_source") == "TestSource"
        assert sample.metadata.get("video_link") == "https://example.com/videos/hello.mp4"


class TestHow2SignConverter:
    """Tests for How2Sign CSV converter."""

    def test_convert_basic(self, temp_dir):
        """Test basic How2Sign CSV conversion."""
        csv_file = temp_dir / "how2sign.csv"
        with open(csv_file, "w", newline="") as f:
            writer = csv.writer(f, delimiter="\t")
            writer.writerow(
                [
                    "VIDEO_ID",
                    "VIDEO_NAME",
                    "SENTENCE_ID",
                    "SENTENCE_NAME",
                    "START_REALIGNED",
                    "END_REALIGNED",
                    "SENTENCE",
                ]
            )
            writer.writerow(
                [
                    "vid001",
                    "vid001-rgb_front",
                    "vid001_0",
                    "vid001_0-rgb_front",
                    "1.0",
                    "5.0",
                    "Hello world",
                ]
            )

        output_dir = temp_dir / "output"
        output_files = convert_how2sign_csv(csv_file, output_dir, split="train")

        assert len(output_files) == 1
        sample = read_unified_annotation(output_files[0])
        assert sample.text == "Hello world"
        assert sample.split == "train"
        assert sample.segments is not None
        assert sample.segments[0].start == 1.0
        assert sample.segments[0].end == 5.0


class TestASLCitizenConverter:
    """Tests for ASL Citizen CSV converter."""

    def test_convert_basic(self, temp_dir):
        """Test basic ASL Citizen conversion."""
        csv_file = temp_dir / "asl_citizen.csv"
        with open(csv_file, "w", newline="") as f:
            writer = csv.DictWriter(f, fieldnames=["video_id", "gloss", "signer_id"])
            writer.writeheader()
            writer.writerow({"video_id": "vid001", "gloss": "HELLO", "signer_id": "s01"})

        output_dir = temp_dir / "output"
        output_files = convert_asl_citizen_csv(csv_file, output_dir)

        assert len(output_files) == 1
        sample = read_unified_annotation(output_files[0])
        assert sample.glosses == ["HELLO"]
        assert sample.signer_id == "s01"


class TestVTTConverter:
    """Tests for VTT converter."""

    def test_convert_single_file(self, temp_dir):
        """Test converting a single VTT file."""
        vtt_content = """WEBVTT

00:00:01.000 --> 00:00:04.000
Hello world

00:00:05.000 --> 00:00:08.000
This is a test
"""
        vtt_file = temp_dir / "test.vtt"
        with open(vtt_file, "w") as f:
            f.write(vtt_content)

        output_file = temp_dir / "output.json"
        convert_vtt(vtt_file, output_file, dataset="test")

        sample = read_unified_annotation(output_file)
        assert sample.segments is not None
        assert len(sample.segments) == 2
        assert sample.text == "Hello world This is a test"

    def test_convert_directory(self, temp_dir):
        """Test converting a directory of VTT files."""
        vtt_dir = temp_dir / "subtitles"
        vtt_dir.mkdir()

        for i in range(3):
            with open(vtt_dir / f"video_{i}.vtt", "w") as f:
                f.write(f"""WEBVTT

00:00:01.000 --> 00:00:04.000
Subtitle {i}
""")

        output_dir = temp_dir / "output"
        output_files = convert_vtt_directory(vtt_dir, output_dir, dataset="test")

        assert len(output_files) == 3


class TestCSLDailyConverter:
    """Tests for CSL-Daily JSON converter."""

    def test_convert_dict_format(self, temp_dir):
        """Test CSL-Daily dict format conversion."""
        csl_data = {
            "sample1": {
                "video_id": "vid001",
                "gloss": "你好 世界",
                "text": "Hello world",
                "split": "train",
            },
        }

        json_file = temp_dir / "csl_daily.json"
        with open(json_file, "w") as f:
            json.dump(csl_data, f)

        output_dir = temp_dir / "output"
        output_files = convert_csl_daily_json(json_file, output_dir)

        assert len(output_files) == 1
        sample = read_unified_annotation(output_files[0])
        assert sample.glosses == ["你好", "世界"]
        assert sample.text == "Hello world"


class TestPhoenixConverter:
    """Tests for Phoenix CSV converter."""

    def test_convert_basic(self, temp_dir):
        """Test basic Phoenix conversion."""
        csv_file = temp_dir / "phoenix.csv"
        with open(csv_file, "w", newline="") as f:
            writer = csv.writer(f, delimiter="|")
            writer.writerow(["id", "annotation", "translation", "speaker"])
            writer.writerow(["sample1", "HELLO WORLD", "Hello world", "signer01"])

        output_dir = temp_dir / "output"
        output_files = convert_phoenix_csv(csv_file, output_dir)

        assert len(output_files) == 1
        sample = read_unified_annotation(output_files[0])
        assert sample.glosses == ["HELLO", "WORLD"]
        assert sample.text == "Hello world"


class TestYTSLConverter:
    """Tests for YTSL-25 TSV converter."""

    def test_convert_basic(self, temp_dir):
        """Test basic YTSL conversion."""
        tsv_file = temp_dir / "ytsl.tsv"
        with open(tsv_file, "w", newline="") as f:
            writer = csv.DictWriter(
                f, fieldnames=["video_id", "caption", "language"], delimiter="\t"
            )
            writer.writeheader()
            writer.writerow({"video_id": "vid001", "caption": "Hello", "language": "asl"})

        output_dir = temp_dir / "output"
        output_files = convert_ytsl_tsv(tsv_file, output_dir)

        assert len(output_files) == 1
        sample = read_unified_annotation(output_files[0])
        assert sample.text == "Hello"


class TestConvertDataset:
    """Tests for the convert_dataset function."""

    def test_auto_detect_vtt(self, temp_dir):
        """Test auto-detection of VTT format."""
        vtt_dir = temp_dir / "source"
        vtt_dir.mkdir()
        with open(vtt_dir / "test.vtt", "w") as f:
            f.write("WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nHello")

        output_dir = temp_dir / "output"
        result = convert_dataset(
            dataset="test",
            source_dir=vtt_dir,
            output_dir=output_dir,
        )

        assert result.source_format == "vtt"
        assert result.files_created >= 1

    def test_unknown_format(self, temp_dir):
        """Test handling of unknown format."""
        source_dir = temp_dir / "source"
        source_dir.mkdir()
        with open(source_dir / "unknown.xyz", "w") as f:
            f.write("unknown format")

        output_dir = temp_dir / "output"
        result = convert_dataset(
            dataset="test",
            source_dir=source_dir,
            output_dir=output_dir,
            source_format="unknown_format",
        )

        assert result.files_created == 0
        assert len(result.errors) > 0


class TestSchemaCompliance:
    """Test that converters produce schema-compliant output."""

    def test_wlasl_schema_compliant(self, temp_dir):
        """Test WLASL output is schema-compliant."""
        wlasl_data = [{"gloss": "TEST", "instances": [{"video_id": "v1"}]}]
        json_file = temp_dir / "wlasl.json"
        with open(json_file, "w") as f:
            json.dump(wlasl_data, f)

        output_dir = temp_dir / "output"
        convert_wlasl_json(json_file, output_dir)

        # Read and check required fields
        with open(output_dir / "TEST_v1.json") as f:
            data = json.load(f)

        assert data["schema_version"] == SCHEMA_VERSION
        assert "sample_id" in data
        assert "dataset" in data

    def test_vtt_schema_compliant(self, temp_dir):
        """Test VTT output is schema-compliant."""
        vtt_file = temp_dir / "test.vtt"
        with open(vtt_file, "w") as f:
            f.write("WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nHello")

        output_file = temp_dir / "output.json"
        convert_vtt(vtt_file, output_file, dataset="test")

        with open(output_file) as f:
            data = json.load(f)

        assert data["schema_version"] == SCHEMA_VERSION
        assert "sample_id" in data
        assert "dataset" in data
