"""Tests for dataset loaders with real data."""

from pathlib import Path

import pytest

from sltk.data.core import Sample
from sltk.data.datasets.base import BaseDataset
from sltk.exceptions import DatasetNotFoundError

# Skip tests if data directories don't exist
BSLCP_ROOT = Path("/vol/research/datasets/multiview/BSLCP")
BOBSL_ROOT = Path("/vol/research/datasets/mixedmode/BOBSL/bobsl-v1-release")
ASLDICT_ROOT = Path("/vol/research/datasets/singlevideo/asldict")
BSLCP_FEATURES = Path("/vol/research/SignFeaturePool/features2/BSLCP2")


class TestBSLCPDataset:
    """Tests for BSLCP dataset loader."""

    @pytest.mark.skipif(not BSLCP_ROOT.exists(), reason="BSLCP data not available")
    def test_load_dataset(self):
        """Test loading BSLCP dataset."""
        from sltk.data.datasets import BSLCPDataset

        dataset = BSLCPDataset(root=BSLCP_ROOT)

        assert dataset.NAME == "bslcp"
        assert len(dataset) > 0
        print(f"BSLCP: {len(dataset)} samples found")

    @pytest.mark.skipif(not BSLCP_ROOT.exists(), reason="BSLCP data not available")
    def test_load_sample(self):
        """Test loading a single BSLCP sample."""
        from sltk.data.datasets import BSLCPDataset

        dataset = BSLCPDataset(root=BSLCP_ROOT)

        if len(dataset) > 0:
            sample = dataset[0]

            assert isinstance(sample, Sample)
            assert sample.id is not None
            assert sample.dataset == "bslcp"
            assert sample.segments is not None

            print(f"Sample ID: {sample.id}")
            print(f"Signer: {sample.signer_id}")
            print(f"Num segments: {len(sample.segments)}")

            if len(sample.segments) > 0:
                print(f"First 5 glosses: {sample.glosses[:5]}")

    @pytest.mark.skipif(not BSLCP_ROOT.exists(), reason="BSLCP data not available")
    def test_segment_content(self):
        """Test that segments have valid content."""
        from sltk.data.datasets import BSLCPDataset

        dataset = BSLCPDataset(root=BSLCP_ROOT)

        if len(dataset) > 0:
            sample = dataset[0]

            for seg in list(sample.segments)[:5]:
                assert seg.start >= 0
                assert seg.end >= seg.start
                assert seg.tier is not None
                print(f"  {seg.start:.2f}-{seg.end:.2f}: {seg.label} (tier: {seg.tier})")

    @pytest.mark.skipif(not BSLCP_ROOT.exists(), reason="BSLCP data not available")
    def test_get_signers(self):
        """Test getting list of signers."""
        from sltk.data.datasets import BSLCPDataset

        dataset = BSLCPDataset(root=BSLCP_ROOT)
        signers = dataset.get_signers()

        assert len(signers) > 0
        print(f"Signers: {signers}")

    @pytest.mark.skipif(
        not BSLCP_ROOT.exists() or not BSLCP_FEATURES.exists(),
        reason="BSLCP data or features not available",
    )
    def test_load_with_poses(self):
        """Test loading BSLCP with pose features."""
        from sltk.data.datasets import BSLCPDataset

        dataset = BSLCPDataset(
            root=BSLCP_ROOT,
            feature_root=BSLCP_FEATURES,
            load_poses=True,
            pose_format="wilor",
        )

        # Find a sample that has features
        for i in range(min(5, len(dataset))):
            sample = dataset[i]
            if sample.poses is not None:
                print(f"Sample {sample.id} has poses: {sample.poses.shape}")
                assert sample.poses.num_frames > 0
                break


class TestBOBSLDataset:
    """Tests for BOBSL dataset loader."""

    @pytest.mark.skipif(not BOBSL_ROOT.exists(), reason="BOBSL data not available")
    def test_load_dataset_mouthings(self):
        """Test loading BOBSL with mouthings."""
        from sltk.data.datasets import BOBSLDataset

        dataset = BOBSLDataset(
            root=BOBSL_ROOT,
            split="train",
            annotation_type="mouthings",
        )

        assert dataset.NAME == "bobsl"
        assert len(dataset) > 0
        print(f"BOBSL train: {len(dataset)} videos")

    @pytest.mark.skipif(not BOBSL_ROOT.exists(), reason="BOBSL data not available")
    def test_load_sample_mouthings(self):
        """Test loading a BOBSL sample with mouthings."""
        from sltk.data.datasets import BOBSLDataset

        dataset = BOBSLDataset(
            root=BOBSL_ROOT,
            split="train",
            annotation_type="mouthings",
        )

        if len(dataset) > 0:
            sample = dataset[0]

            assert isinstance(sample, Sample)
            assert sample.dataset == "bobsl"
            assert sample.segments is not None

            print(f"Sample ID: {sample.id}")
            print(f"Num mouthings: {len(sample.segments)}")

            if len(sample.segments) > 0:
                print("First 5 mouthings:")
                for seg in list(sample.segments)[:5]:
                    print(
                        f"  {seg.start:.2f}-{seg.end:.2f}: {seg.label} (conf: {seg.confidence:.2f})"
                    )

    @pytest.mark.skipif(not BOBSL_ROOT.exists(), reason="BOBSL data not available")
    def test_load_subtitles(self):
        """Test loading BOBSL with subtitles."""
        from sltk.data.datasets import BOBSLDataset

        dataset = BOBSLDataset(
            root=BOBSL_ROOT,
            split="train",
            annotation_type="subtitles",
        )

        print(f"BOBSL subtitles: {len(dataset)} videos")

        if len(dataset) > 0:
            sample = dataset[0]
            print(f"Sample {sample.id} has {len(sample.segments)} subtitle segments")

    @pytest.mark.skipif(not BOBSL_ROOT.exists(), reason="BOBSL data not available")
    def test_vocabulary(self):
        """Test getting vocabulary from mouthings."""
        from sltk.data.datasets import BOBSLDataset

        dataset = BOBSLDataset(
            root=BOBSL_ROOT,
            split="train",
            annotation_type="mouthings",
        )

        vocab = dataset.get_vocabulary(min_count=10)
        print(f"Vocabulary (min_count=10): {len(vocab)} words")
        if vocab:
            print(f"Sample words: {vocab[:10]}")

    @pytest.mark.skipif(not BOBSL_ROOT.exists(), reason="BOBSL data not available")
    def test_split_stats(self):
        """Test getting split statistics."""
        from sltk.data.datasets import BOBSLDataset

        dataset = BOBSLDataset(
            root=BOBSL_ROOT,
            split="train",
            annotation_type="mouthings",
        )

        stats = dataset.get_split_stats()
        print(f"BOBSL train stats: {stats}")


class TestASLDictDataset:
    """Tests for ASLDict dataset loader."""

    @pytest.mark.skipif(not ASLDICT_ROOT.exists(), reason="ASLDict data not available")
    def test_load_dataset(self):
        """Test loading ASLDict dataset."""
        from sltk.data.datasets import ASLDictDataset

        dataset = ASLDictDataset(root=ASLDICT_ROOT)

        assert dataset.NAME == "asldict"
        assert len(dataset) > 0
        print(f"ASLDict: {len(dataset)} entries")

    @pytest.mark.skipif(not ASLDICT_ROOT.exists(), reason="ASLDict data not available")
    def test_load_sample(self):
        """Test loading a single ASLDict sample."""
        from sltk.data.datasets import ASLDictDataset

        dataset = ASLDictDataset(root=ASLDICT_ROOT)

        if len(dataset) > 0:
            sample = dataset[0]

            assert isinstance(sample, Sample)
            assert sample.dataset == "asldict"
            assert sample.glosses is not None
            assert len(sample.glosses) > 0

            print(f"Sample ID: {sample.id}")
            print(f"Word: {sample.glosses[0]}")
            print(f"Source: {sample.metadata.get('source')}")

    @pytest.mark.skipif(not ASLDICT_ROOT.exists(), reason="ASLDict data not available")
    def test_vocabulary(self):
        """Test getting vocabulary."""
        from sltk.data.datasets import ASLDictDataset

        dataset = ASLDictDataset(root=ASLDICT_ROOT)
        vocab = dataset.get_vocabulary()

        assert len(vocab) > 0
        print(f"ASLDict vocabulary: {len(vocab)} words")
        print(f"Sample words: {vocab[:10]}")

    @pytest.mark.skipif(not ASLDICT_ROOT.exists(), reason="ASLDict data not available")
    def test_sources(self):
        """Test getting data sources."""
        from sltk.data.datasets import ASLDictDataset

        dataset = ASLDictDataset(root=ASLDICT_ROOT)
        sources = dataset.get_sources()

        print(f"ASLDict sources: {sources}")

    @pytest.mark.skipif(not ASLDICT_ROOT.exists(), reason="ASLDict data not available")
    def test_filter_by_word(self):
        """Test filtering by word."""
        from sltk.data.datasets import ASLDictDataset

        dataset = ASLDictDataset(root=ASLDICT_ROOT)

        # Find a common word
        word_counts = dataset.get_word_counts()
        if word_counts:
            # Get a word with multiple entries
            multi_entry_words = [w for w, c in word_counts.items() if c > 1]
            if multi_entry_words:
                word = multi_entry_words[0]
                entries = dataset.filter_by_word(word)
                print(f"Word '{word}' has {len(entries)} entries")


class TestDatasetIntegration:
    """Integration tests across datasets."""

    def test_base_dataset_interface(self):
        """Test that all datasets implement base interface."""
        from sltk.data.datasets import ASLDictDataset, BOBSLDataset, BSLCPDataset

        datasets = [BSLCPDataset, BOBSLDataset, ASLDictDataset]

        for ds_class in datasets:
            assert hasattr(ds_class, "NAME")
            assert hasattr(ds_class, "TASKS")
            assert hasattr(ds_class, "LANGUAGES")
            assert issubclass(ds_class, BaseDataset)

    def test_sample_unified_format(self):
        """Test that all samples follow unified format."""
        from sltk.data.datasets import ASLDictDataset, BOBSLDataset, BSLCPDataset

        # Test with each dataset that's available
        datasets_to_test = []

        if BSLCP_ROOT.exists():
            datasets_to_test.append(BSLCPDataset(root=BSLCP_ROOT))
        if BOBSL_ROOT.exists():
            datasets_to_test.append(BOBSLDataset(root=BOBSL_ROOT, split="train"))
        if ASLDICT_ROOT.exists():
            datasets_to_test.append(ASLDictDataset(root=ASLDICT_ROOT))

        for dataset in datasets_to_test:
            if len(dataset) > 0:
                sample = dataset[0]

                # All samples should have these attributes
                assert hasattr(sample, "id")
                assert hasattr(sample, "video_path")
                assert hasattr(sample, "poses")
                assert hasattr(sample, "segments")
                assert hasattr(sample, "glosses")
                assert hasattr(sample, "dataset")
                assert hasattr(sample, "metadata")

                print(f"{dataset.NAME}: Sample format OK")


# =============================================================================
# Mock-based tests (don't require real data)
# =============================================================================

import csv
import json


class TestDatasetRegistry:
    """Tests for dataset registry functionality."""

    def test_list_datasets(self):
        """Test listing available datasets."""
        from sltk.data.datasets import list_datasets

        datasets = list_datasets()
        assert isinstance(datasets, list)
        assert len(datasets) > 0
        assert "wlasl" in datasets
        assert "bslcp" in datasets
        assert "phoenix2014t" in datasets

    def test_list_datasets_for_language(self):
        """Test listing datasets for specific languages."""
        from sltk.data.datasets import list_datasets_for_language

        asl_datasets = list_datasets_for_language("asl")
        assert "wlasl" in asl_datasets
        assert "asldict" in asl_datasets

        bsl_datasets = list_datasets_for_language("bsl")
        assert "bslcp" in bsl_datasets

    def test_unknown_dataset(self):
        """Test error for unknown dataset."""
        from sltk.data.datasets import get_dataset

        with pytest.raises(DatasetNotFoundError):
            get_dataset("nonexistent_dataset", root="/tmp")


class TestWLASLDatasetMock:
    """Tests for WLASL dataset loader with mock data."""

    @pytest.fixture
    def mock_wlasl_dir(self, tmp_path):
        """Create a mock WLASL directory structure."""
        annotations = [
            {
                "gloss": "book",
                "instances": [
                    {
                        "video_id": "video1",
                        "split": "train",
                        "frame_start": 0,
                        "frame_end": 30,
                        "fps": 30,
                    },
                    {
                        "video_id": "video2",
                        "split": "val",
                        "frame_start": 0,
                        "frame_end": 25,
                        "fps": 25,
                    },
                ],
            },
            {
                "gloss": "drink",
                "instances": [
                    {
                        "video_id": "video3",
                        "split": "train",
                        "frame_start": 10,
                        "frame_end": 50,
                        "fps": 30,
                    },
                ],
            },
        ]

        with open(tmp_path / "WLASL_v0.3.json", "w") as f:
            json.dump(annotations, f)

        (tmp_path / "videos").mkdir()
        return tmp_path

    def test_wlasl_loading(self, mock_wlasl_dir):
        """Test WLASL dataset loading."""
        from sltk.data.datasets import WLASLDataset

        dataset = WLASLDataset(
            root=mock_wlasl_dir,
            split="train",
            num_classes=100,
        )

        assert len(dataset) == 2
        assert dataset.NAME == "wlasl"

    def test_wlasl_vocabulary(self, mock_wlasl_dir):
        """Test WLASL vocabulary extraction."""
        from sltk.data.datasets import WLASLDataset

        dataset = WLASLDataset(root=mock_wlasl_dir, split="all", num_classes=2)
        vocab = dataset.get_vocabulary()

        assert "book" in vocab
        assert "drink" in vocab


class TestPhoenix2014TDatasetMock:
    """Tests for Phoenix-2014T dataset loader with mock data."""

    @pytest.fixture
    def mock_phoenix_dir(self, tmp_path):
        """Create a mock Phoenix directory structure."""
        anno_dir = tmp_path / "annotations" / "manual"
        anno_dir.mkdir(parents=True)

        with open(anno_dir / "train.corpus.csv", "w") as f:
            f.write("name|video|annotation|translation|speaker\n")
            f.write("sample1|folder1|WOLKEN REGEN|Es wird regnen|speaker1\n")
            f.write("sample2|folder2|SONNE WARM|Die Sonne scheint|speaker2\n")

        return tmp_path

    def test_phoenix_loading(self, mock_phoenix_dir):
        """Test Phoenix-2014T dataset loading."""
        from sltk.data.datasets import Phoenix2014TDataset

        dataset = Phoenix2014TDataset(root=mock_phoenix_dir, split="train")

        assert len(dataset) == 2
        assert dataset.NAME == "phoenix2014t"
        assert "dgs" in dataset.LANGUAGES


class TestYTSL25DatasetMock:
    """Tests for YTSL-25 dataset loader with mock data."""

    @pytest.fixture
    def mock_ytsl_dir(self, tmp_path):
        """Create a mock YTSL-25 directory structure."""
        (tmp_path / "captions").mkdir()

        with open(tmp_path / "captions" / "video1.tsv", "w") as f:
            writer = csv.writer(f, delimiter="\t")
            writer.writerow(["video_id", "start", "end", "text", "language"])
            writer.writerow(["video1", "0.0", "2.0", "Hello world", "asl"])
            writer.writerow(["video1", "2.0", "4.0", "How are you", "asl"])

        metadata = {"video1": {"split": "train", "duration": 10.0}}
        with open(tmp_path / "metadata.json", "w") as f:
            json.dump(metadata, f)

        return tmp_path

    def test_ytsl_loading(self, mock_ytsl_dir):
        """Test YTSL-25 dataset loading."""
        from sltk.data.datasets import YTSL25Dataset

        dataset = YTSL25Dataset(root=mock_ytsl_dir, split="train", language="asl")

        assert dataset.NAME == "ytsl25"
        assert dataset.language == "asl"
        assert "caption_based_recognition" in dataset.TASKS

    def test_ytsl_annotation_type(self, mock_ytsl_dir):
        """Test YTSL-25 uses captions not glosses."""
        from sltk.data.datasets import YTSL25Dataset

        dataset = YTSL25Dataset(root=mock_ytsl_dir, split="all", language="asl")
        summary = dataset.summary()

        assert summary.get("annotation_type") == "caption"


class TestYTASLDatasetMock:
    """Tests for YT-ASL dataset loader with mock data."""

    @pytest.fixture
    def mock_ytasl_dir(self, tmp_path):
        """Create a mock YT-ASL directory structure."""
        (tmp_path / "subtitles").mkdir()

        vtt_content = """WEBVTT

00:00:00.500 --> 00:00:02.000
Hello everyone

00:00:02.500 --> 00:00:04.000
Welcome to the show
"""
        with open(tmp_path / "subtitles" / "video1.vtt", "w") as f:
            f.write(vtt_content)

        manifest = [{"video_id": "video1", "split": "train"}]
        with open(tmp_path / "manifest.json", "w") as f:
            json.dump(manifest, f)

        return tmp_path

    def test_ytasl_loading(self, mock_ytasl_dir):
        """Test YT-ASL dataset loading."""
        from sltk.data.datasets import YTASLDataset

        dataset = YTASLDataset(root=mock_ytasl_dir, split="train")

        assert dataset.NAME == "yt_asl"
        assert "asl" in dataset.LANGUAGES

    def test_ytasl_vtt_parsing(self, mock_ytasl_dir):
        """Test VTT subtitle parsing."""
        from sltk.data.datasets import YTASLDataset

        dataset = YTASLDataset(root=mock_ytasl_dir, split="all")

        assert "video1" in dataset._subtitles
        subs = dataset._subtitles["video1"]
        assert len(subs) == 2
        assert subs[0]["text"] == "Hello everyone"


class TestAllDatasetsInterface:
    """Tests for all dataset common interface."""

    def test_all_datasets_have_required_attributes(self):
        """Test that all datasets have required class attributes."""
        from sltk.data.datasets import DATASETS

        for name, ds_class in DATASETS.items():
            assert hasattr(ds_class, "NAME"), f"{name} missing NAME"
            assert hasattr(ds_class, "TASKS"), f"{name} missing TASKS"
            assert hasattr(ds_class, "LANGUAGES"), f"{name} missing LANGUAGES"
            assert hasattr(ds_class, "CITATION"), f"{name} missing CITATION"
            assert issubclass(ds_class, BaseDataset), f"{name} not subclass of BaseDataset"
