"""Tests for cross-dataset gloss comparison module."""

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from sltk.analysis.cross_dataset import (
    ContextPattern,
    CrossDatasetResult,
    DatasetCache,
    DurationStats,
    GlossMatch,
    ParallelExample,
    VocabularyComparison,
    _compute_duration_stats,
    _simple_correlation,
    _tier_matches,
    analyze_gloss_contexts,
    batch_find_glosses,
    compare_dataset_vocabularies,
    compare_gloss_durations,
    find_common_glosses,
    find_gloss_across_datasets,
    find_parallel_examples,
    get_dataset_vocabulary_summary,
)
from sltk.data.core import Sample, Segment, SegmentList

# Suppress unused import warnings - these are used by fixtures
_ = Sample, Segment, SegmentList


# =============================================================================
# Fixtures
# =============================================================================


@pytest.fixture
def mock_samples_dataset1():
    """Create mock samples for dataset 1."""
    return [
        Sample(
            id="sample1",
            dataset="dataset1",
            segments=SegmentList(
                [
                    Segment(start=0.0, end=0.5, label="HELLO", tier="gloss"),
                    Segment(start=0.5, end=1.0, label="WORLD", tier="gloss"),
                    Segment(start=1.0, end=1.5, label="HELLO", tier="gloss"),
                ]
            ),
            glosses=["HELLO", "WORLD", "HELLO"],
            signer_id="signer1",
        ),
        Sample(
            id="sample2",
            dataset="dataset1",
            segments=SegmentList(
                [
                    Segment(start=0.0, end=0.6, label="GOODBYE", tier="gloss"),
                    Segment(start=0.6, end=1.2, label="HELLO", tier="gloss"),
                ]
            ),
            glosses=["GOODBYE", "HELLO"],
            signer_id="signer2",
        ),
    ]


@pytest.fixture
def mock_samples_dataset2():
    """Create mock samples for dataset 2."""
    return [
        Sample(
            id="sample3",
            dataset="dataset2",
            segments=SegmentList(
                [
                    Segment(start=0.0, end=0.4, label="HELLO", tier="gloss"),
                    Segment(start=0.4, end=0.9, label="FRIEND", tier="gloss"),
                ]
            ),
            glosses=["HELLO", "FRIEND"],
            signer_id="signer3",
        ),
        Sample(
            id="sample4",
            dataset="dataset2",
            segments=SegmentList(
                [
                    Segment(start=0.0, end=0.5, label="THANK-YOU", tier="gloss"),
                    Segment(start=0.5, end=0.8, label="HELLO", tier="gloss"),
                ]
            ),
            glosses=["THANK-YOU", "HELLO"],
            signer_id="signer3",
        ),
    ]


@pytest.fixture
def mock_dataset_class(mock_samples_dataset1, mock_samples_dataset2):
    """Create a mock dataset class factory."""

    def create_mock_dataset(samples):
        mock = MagicMock()
        mock.__len__ = MagicMock(return_value=len(samples))
        mock.__getitem__ = MagicMock(side_effect=lambda i: samples[i])
        mock.get_by_id = MagicMock(
            side_effect=lambda id_: next((s for s in samples if s.id == id_), None)
        )
        return mock

    return create_mock_dataset


@pytest.fixture(autouse=True)
def clear_cache():
    """Clear the dataset cache before each test."""
    DatasetCache.clear()
    yield
    DatasetCache.clear()


# =============================================================================
# Test Data Classes
# =============================================================================


class TestDurationStats:
    """Tests for DurationStats dataclass."""

    def test_creation(self):
        """Test DurationStats creation."""
        stats = DurationStats(
            count=10,
            mean=0.5,
            std=0.1,
            min=0.2,
            max=0.8,
            median=0.5,
            quartiles=(0.4, 0.5, 0.6),
        )

        assert stats.count == 10
        assert stats.mean == 0.5
        assert stats.quartiles == (0.4, 0.5, 0.6)

    def test_to_dict(self):
        """Test DurationStats to_dict."""
        stats = DurationStats(
            count=5,
            mean=0.4,
            std=0.05,
            min=0.3,
            max=0.5,
            median=0.4,
            quartiles=(0.35, 0.4, 0.45),
        )

        d = stats.to_dict()
        assert d["count"] == 5
        assert d["mean"] == 0.4
        assert d["quartiles"] == [0.35, 0.4, 0.45]


class TestGlossMatch:
    """Tests for GlossMatch dataclass."""

    def test_creation(self):
        """Test GlossMatch creation."""
        match = GlossMatch(
            dataset="bslcp",
            sample_id="sample1",
            segment_idx=0,
            start=0.0,
            end=0.5,
            duration=0.5,
            context_before=["I"],
            context_after=["YOU"],
            signer_id="signer1",
        )

        assert match.dataset == "bslcp"
        assert match.duration == 0.5

    def test_to_dict(self):
        """Test GlossMatch to_dict."""
        match = GlossMatch(
            dataset="bslcp",
            sample_id="sample1",
            segment_idx=0,
            start=0.0,
            end=0.5,
            duration=0.5,
            context_before=[],
            context_after=[],
        )

        d = match.to_dict()
        assert d["dataset"] == "bslcp"
        assert d["sample_id"] == "sample1"


class TestCrossDatasetResult:
    """Tests for CrossDatasetResult dataclass."""

    def test_creation(self):
        """Test CrossDatasetResult creation."""
        result = CrossDatasetResult(
            gloss="HELLO",
            total_matches=5,
            matches_by_dataset={
                "dataset1": [
                    GlossMatch(
                        dataset="dataset1",
                        sample_id="s1",
                        segment_idx=0,
                        start=0,
                        end=0.5,
                        duration=0.5,
                        context_before=[],
                        context_after=[],
                    )
                ]
            },
            dataset_frequencies={"dataset1": 3, "dataset2": 2},
            duration_stats_by_dataset={},
        )

        assert result.gloss == "HELLO"
        assert result.total_matches == 5

    def test_datasets_with_matches(self):
        """Test datasets_with_matches property."""
        result = CrossDatasetResult(
            gloss="HELLO",
            total_matches=3,
            matches_by_dataset={},
            dataset_frequencies={"dataset1": 3, "dataset2": 0, "dataset3": 1},
            duration_stats_by_dataset={},
        )

        datasets = result.datasets_with_matches
        assert "dataset1" in datasets
        assert "dataset3" in datasets
        assert "dataset2" not in datasets


class TestVocabularyComparison:
    """Tests for VocabularyComparison dataclass."""

    def test_creation(self):
        """Test VocabularyComparison creation."""
        comparison = VocabularyComparison(
            datasets=["ds1", "ds2"],
            shared_glosses={"HELLO", "GOODBYE"},
            unique_glosses={"ds1": {"WORLD"}, "ds2": {"FRIEND"}},
            pairwise_overlap={("ds1", "ds2"): 0.5},
            frequency_correlation={("ds1", "ds2"): 0.8},
            vocabulary_sizes={"ds1": 100, "ds2": 150},
            total_vocabulary={"HELLO", "GOODBYE", "WORLD", "FRIEND"},
        )

        assert len(comparison.shared_glosses) == 2
        assert comparison.vocabulary_sizes["ds1"] == 100

    def test_to_dict(self):
        """Test VocabularyComparison to_dict."""
        comparison = VocabularyComparison(
            datasets=["ds1", "ds2"],
            shared_glosses={"HELLO"},
            unique_glosses={"ds1": set(), "ds2": set()},
            pairwise_overlap={("ds1", "ds2"): 0.5},
            frequency_correlation={("ds1", "ds2"): 0.8},
            vocabulary_sizes={"ds1": 10, "ds2": 15},
            total_vocabulary={"HELLO"},
        )

        d = comparison.to_dict()
        assert "datasets" in d
        assert "shared_glosses" in d
        assert d["total_vocabulary_size"] == 1


class TestParallelExample:
    """Tests for ParallelExample dataclass."""

    def test_creation(self):
        """Test ParallelExample creation."""
        match1 = GlossMatch(
            dataset="ds1",
            sample_id="s1",
            segment_idx=0,
            start=0,
            end=0.5,
            duration=0.5,
            context_before=[],
            context_after=[],
        )

        example = ParallelExample(gloss="HELLO", examples={"ds1": [match1], "ds2": []})

        assert example.gloss == "HELLO"
        assert example.total_examples == 1
        assert "ds1" in example.datasets_with_examples


class TestContextPattern:
    """Tests for ContextPattern dataclass."""

    def test_creation(self):
        """Test ContextPattern creation."""
        pattern = ContextPattern(
            pattern=("I", "HELLO", "YOU"),
            frequency=5,
            datasets=["ds1", "ds2"],
        )

        assert pattern.pattern == ("I", "HELLO", "YOU")
        assert pattern.frequency == 5

    def test_str(self):
        """Test ContextPattern string representation."""
        pattern = ContextPattern(
            pattern=("A", "B", "C"),
            frequency=3,
            datasets=["ds1"],
        )

        assert str(pattern) == "A -> B -> C"


# =============================================================================
# Test Helper Functions
# =============================================================================


class TestTierMatches:
    """Tests for _tier_matches helper function."""

    def test_exact_match(self):
        """Test exact tier matching."""
        assert _tier_matches("gloss", "gloss")
        assert _tier_matches("translation", "translation")

    def test_case_insensitive(self):
        """Test case-insensitive matching."""
        assert _tier_matches("Gloss", "gloss")
        assert _tier_matches("GLOSS", "gloss")

    def test_hyphen_underscore_normalized(self):
        """Test hyphen/underscore normalization."""
        assert _tier_matches("gloss-rh", "glossrh")
        assert _tier_matches("gloss_rh", "glossrh")

    def test_gloss_tier_variants(self):
        """Test common gloss tier variants."""
        assert _tier_matches("RH-IDgloss", "gloss")
        assert _tier_matches("LH-Gloss", "gloss")

    def test_no_match(self):
        """Test non-matching tiers."""
        assert not _tier_matches("translation", "gloss")
        assert not _tier_matches("mouthing", "gloss")


class TestComputeDurationStats:
    """Tests for _compute_duration_stats helper function."""

    def test_basic_stats(self):
        """Test basic duration statistics."""
        durations = [0.5, 0.6, 0.4, 0.5, 0.5]

        stats = _compute_duration_stats(durations)

        assert stats.count == 5
        assert abs(stats.mean - 0.5) < 0.01
        assert stats.min == 0.4
        assert stats.max == 0.6
        assert stats.median == 0.5

    def test_empty_list(self):
        """Test with empty duration list."""
        stats = _compute_duration_stats([])

        assert stats.count == 0
        assert stats.mean == 0.0

    def test_single_value(self):
        """Test with single duration."""
        stats = _compute_duration_stats([0.5])

        assert stats.count == 1
        assert stats.mean == 0.5
        assert stats.std == 0.0


class TestSimpleCorrelation:
    """Tests for _simple_correlation helper function."""

    def test_perfect_correlation(self):
        """Test perfect positive correlation."""
        freq1 = {"a": 1, "b": 2, "c": 3}
        freq2 = {"a": 2, "b": 4, "c": 6}
        shared = {"a", "b", "c"}

        corr = _simple_correlation(freq1, freq2, shared)

        assert abs(corr - 1.0) < 0.01

    def test_no_correlation(self):
        """Test insufficient data for correlation."""
        freq1 = {"a": 1}
        freq2 = {"a": 2}
        shared = {"a"}

        corr = _simple_correlation(freq1, freq2, shared)

        assert corr == 0.0


# =============================================================================
# Test DatasetCache
# =============================================================================


class TestDatasetCache:
    """Tests for DatasetCache class."""

    def test_singleton(self):
        """Test singleton pattern."""
        cache1 = DatasetCache.get_instance()
        cache2 = DatasetCache.get_instance()

        assert cache1 is cache2

    def test_clear(self):
        """Test cache clearing."""
        cache = DatasetCache.get_instance()
        cache._vocabularies["test"] = {"a": 1}

        DatasetCache.clear()

        cache = DatasetCache.get_instance()
        assert "test" not in cache._vocabularies


# =============================================================================
# Test Main Functions with Mocks
# =============================================================================


class TestFindGlossAcrossDatasets:
    """Tests for find_gloss_across_datasets function."""

    def test_with_mock_datasets(
        self, mock_samples_dataset1, mock_samples_dataset2, mock_dataset_class
    ):
        """Test finding gloss across mock datasets."""
        # Create mock datasets
        dataset1 = mock_dataset_class(mock_samples_dataset1)
        dataset2 = mock_dataset_class(mock_samples_dataset2)

        with patch("sltk.analysis.cross_dataset._get_available_datasets") as mock_list:
            mock_list.return_value = ["dataset1", "dataset2"]

            with patch("sltk.analysis.cross_dataset.DatasetCache.get_dataset") as mock_get:

                def side_effect(name, **kwargs):
                    if name == "dataset1":
                        return dataset1
                    return dataset2

                mock_get.side_effect = side_effect

                # Build vocabulary index manually
                cache = DatasetCache.get_instance()
                cache._vocabularies["dataset1_gloss_None"] = {
                    "hello": 3,
                    "world": 1,
                    "goodbye": 1,
                }
                cache._vocabularies["dataset2_gloss_None"] = {
                    "hello": 2,
                    "friend": 1,
                    "thank-you": 1,
                }
                cache._samples_by_gloss["dataset1_gloss_None"] = {
                    "hello": [("sample1", 0), ("sample1", 2), ("sample2", 1)],
                }
                cache._samples_by_gloss["dataset2_gloss_None"] = {
                    "hello": [("sample3", 0), ("sample4", 1)],
                }

                result = find_gloss_across_datasets("HELLO", datasets=["dataset1", "dataset2"])

                assert result.gloss == "HELLO"
                assert result.dataset_frequencies["dataset1"] == 3
                assert result.dataset_frequencies["dataset2"] == 2

    def test_case_insensitive_search(self, mock_samples_dataset1, mock_dataset_class):
        """Test case-insensitive gloss search."""
        dataset = mock_dataset_class(mock_samples_dataset1)

        with patch("sltk.analysis.cross_dataset.DatasetCache.get_dataset") as mock_get:
            mock_get.return_value = dataset

            cache = DatasetCache.get_instance()
            cache._vocabularies["dataset1_gloss_None"] = {"hello": 3}
            cache._samples_by_gloss["dataset1_gloss_None"] = {
                "hello": [("sample1", 0)],
            }

            # Search with different case
            result = find_gloss_across_datasets(
                "hello", datasets=["dataset1"], case_sensitive=False
            )

            assert result.dataset_frequencies["dataset1"] == 1


class TestCompareDatasetVocabularies:
    """Tests for compare_dataset_vocabularies function."""

    def test_vocabulary_comparison(self):
        """Test vocabulary comparison between datasets."""
        cache = DatasetCache.get_instance()
        cache._vocabularies["ds1_gloss_None"] = {"hello": 10, "world": 5, "unique1": 2}
        cache._vocabularies["ds2_gloss_None"] = {"hello": 8, "world": 3, "unique2": 4}

        with patch("sltk.analysis.cross_dataset.DatasetCache.get_vocabulary") as mock_vocab:

            def side_effect(name, **kwargs):
                key = f"{name}_gloss_None"
                return cache._vocabularies.get(key, {})

            mock_vocab.side_effect = side_effect

            result = compare_dataset_vocabularies(["ds1", "ds2"])

            assert "hello" in result.shared_glosses
            assert "world" in result.shared_glosses
            assert "unique1" in result.unique_glosses["ds1"]
            assert "unique2" in result.unique_glosses["ds2"]

    def test_pairwise_overlap(self):
        """Test pairwise Jaccard similarity calculation."""
        cache = DatasetCache.get_instance()
        cache._vocabularies["ds1_gloss_None"] = {"a": 1, "b": 1, "c": 1}  # 3 items
        cache._vocabularies["ds2_gloss_None"] = {"b": 1, "c": 1, "d": 1}  # 3 items

        with patch("sltk.analysis.cross_dataset.DatasetCache.get_vocabulary") as mock_vocab:

            def side_effect(name, **kwargs):
                key = f"{name}_gloss_None"
                return cache._vocabularies.get(key, {})

            mock_vocab.side_effect = side_effect

            result = compare_dataset_vocabularies(["ds1", "ds2"])

            # Intersection: {b, c} = 2, Union: {a, b, c, d} = 4
            # Jaccard = 2/4 = 0.5
            assert abs(result.pairwise_overlap[("ds1", "ds2")] - 0.5) < 0.01


class TestFindParallelExamples:
    """Tests for find_parallel_examples function."""

    def test_find_parallel(self, mock_samples_dataset1, mock_dataset_class):
        """Test finding parallel examples."""
        # Create mock dataset (used indirectly via patch)
        _ = mock_dataset_class(mock_samples_dataset1)

        with patch("sltk.analysis.cross_dataset.find_gloss_across_datasets") as mock_find:
            mock_find.return_value = CrossDatasetResult(
                gloss="HELLO",
                total_matches=2,
                matches_by_dataset={
                    "ds1": [
                        GlossMatch(
                            dataset="ds1",
                            sample_id="s1",
                            segment_idx=0,
                            start=0,
                            end=0.5,
                            duration=0.5,
                            context_before=[],
                            context_after=[],
                        )
                    ],
                    "ds2": [
                        GlossMatch(
                            dataset="ds2",
                            sample_id="s2",
                            segment_idx=0,
                            start=0,
                            end=0.4,
                            duration=0.4,
                            context_before=[],
                            context_after=[],
                        )
                    ],
                },
                dataset_frequencies={"ds1": 1, "ds2": 1},
                duration_stats_by_dataset={},
            )

            result = find_parallel_examples(["HELLO"], datasets=["ds1", "ds2"])

            assert len(result) == 1
            assert result[0].gloss == "HELLO"
            assert len(result[0].examples["ds1"]) == 1
            assert len(result[0].examples["ds2"]) == 1


class TestCompareGlossDurations:
    """Tests for compare_gloss_durations function."""

    def test_duration_comparison(self):
        """Test comparing gloss durations."""
        with patch("sltk.analysis.cross_dataset.find_gloss_across_datasets") as mock_find:
            mock_find.return_value = CrossDatasetResult(
                gloss="HELLO",
                total_matches=2,
                matches_by_dataset={},
                dataset_frequencies={},
                duration_stats_by_dataset={
                    "ds1": DurationStats(
                        count=10,
                        mean=0.5,
                        std=0.1,
                        min=0.3,
                        max=0.7,
                        median=0.5,
                        quartiles=(0.4, 0.5, 0.6),
                    ),
                    "ds2": DurationStats(
                        count=8,
                        mean=0.3,
                        std=0.05,
                        min=0.2,
                        max=0.4,
                        median=0.3,
                        quartiles=(0.25, 0.3, 0.35),
                    ),
                },
            )

            result = compare_gloss_durations("HELLO", datasets=["ds1", "ds2"])

            assert "ds1" in result
            assert "ds2" in result
            assert result["ds1"].mean == 0.5
            assert result["ds2"].mean == 0.3


class TestAnalyzeGlossContexts:
    """Tests for analyze_gloss_contexts function."""

    def test_context_analysis(self):
        """Test analyzing gloss contexts."""
        with patch("sltk.analysis.cross_dataset.find_gloss_across_datasets") as mock_find:
            mock_find.return_value = CrossDatasetResult(
                gloss="HELLO",
                total_matches=4,
                matches_by_dataset={
                    "ds1": [
                        GlossMatch(
                            dataset="ds1",
                            sample_id="s1",
                            segment_idx=1,
                            start=0.5,
                            end=1.0,
                            duration=0.5,
                            context_before=["I"],
                            context_after=["YOU"],
                        ),
                        GlossMatch(
                            dataset="ds1",
                            sample_id="s2",
                            segment_idx=1,
                            start=0.5,
                            end=1.0,
                            duration=0.5,
                            context_before=["I"],
                            context_after=["FRIEND"],
                        ),
                    ],
                },
                dataset_frequencies={"ds1": 2},
                duration_stats_by_dataset={},
            )

            result = analyze_gloss_contexts(
                "HELLO", datasets=["ds1"], context_size=1, min_frequency=1
            )

            # Should have patterns like ("I", "HELLO") and ("HELLO", "YOU")
            assert len(result) > 0

            # Check that patterns contain the gloss
            for pattern in result:
                assert "HELLO" in pattern.pattern


class TestBatchFindGlosses:
    """Tests for batch_find_glosses function."""

    def test_batch_search(self):
        """Test batch gloss search."""
        with patch("sltk.analysis.cross_dataset.find_gloss_across_datasets") as mock_find:

            def side_effect(gloss, **kwargs):
                return CrossDatasetResult(
                    gloss=gloss,
                    total_matches=1,
                    matches_by_dataset={},
                    dataset_frequencies={"ds1": 1},
                    duration_stats_by_dataset={},
                )

            mock_find.side_effect = side_effect

            results = batch_find_glosses(["HELLO", "GOODBYE"], datasets=["ds1"])

            assert "HELLO" in results
            assert "GOODBYE" in results
            assert results["HELLO"].gloss == "HELLO"


class TestFindCommonGlosses:
    """Tests for find_common_glosses function."""

    def test_find_common(self):
        """Test finding common glosses."""
        cache = DatasetCache.get_instance()
        cache._vocabularies["ds1_gloss_None"] = {
            "hello": 10,
            "world": 5,
            "unique1": 2,
        }
        cache._vocabularies["ds2_gloss_None"] = {
            "hello": 8,
            "world": 3,
            "unique2": 4,
        }

        with patch("sltk.analysis.cross_dataset.compare_dataset_vocabularies") as mock_compare:
            mock_compare.return_value = VocabularyComparison(
                datasets=["ds1", "ds2"],
                shared_glosses={"hello", "world"},
                unique_glosses={},
                pairwise_overlap={},
                frequency_correlation={},
                vocabulary_sizes={"ds1": 3, "ds2": 3},
                total_vocabulary=set(),
            )

            with patch("sltk.analysis.cross_dataset.DatasetCache.get_vocabulary") as mock_vocab:

                def side_effect(name, **kwargs):
                    key = f"{name}_gloss_None"
                    return cache._vocabularies.get(key, {})

                mock_vocab.side_effect = side_effect

                result = find_common_glosses(
                    ["ds1", "ds2"],
                    min_frequency_per_dataset=3,
                    top_n=10,
                )

                # Should find glosses with freq >= 3 in both
                gloss_names = [g for g, _ in result]
                assert "hello" in gloss_names
                assert "world" in gloss_names


class TestGetDatasetVocabularySummary:
    """Tests for get_dataset_vocabulary_summary function."""

    def test_summary(self):
        """Test vocabulary summary generation."""
        cache = DatasetCache.get_instance()
        cache._vocabularies["ds1_gloss_None"] = {"a": 10, "b": 5, "c": 1}

        with patch("sltk.analysis.cross_dataset._get_available_datasets") as mock_list:
            mock_list.return_value = ["ds1"]

            with patch("sltk.analysis.cross_dataset.DatasetCache.get_vocabulary") as mock_vocab:

                def side_effect(name, **kwargs):
                    key = f"{name}_gloss_None"
                    return cache._vocabularies.get(key, {})

                mock_vocab.side_effect = side_effect

                result = get_dataset_vocabulary_summary(["ds1"])

                assert "ds1" in result
                assert result["ds1"]["vocabulary_size"] == 3
                assert result["ds1"]["total_tokens"] == 16
                assert result["ds1"]["max_frequency"] == 10


# =============================================================================
# Integration Tests (with real data paths)
# =============================================================================


BSLCP_ROOT = Path("/vol/research/datasets/multiview/BSLCP")
BOBSL_ROOT = Path("/vol/research/datasets/mixedmode/BOBSL/bobsl-v1-release")


class TestCrossDatasetIntegration:
    """Integration tests with real datasets."""

    @pytest.mark.skipif(
        not BSLCP_ROOT.exists() or not BOBSL_ROOT.exists(),
        reason="BSLCP or BOBSL data not available",
    )
    def test_real_cross_dataset_search(self):
        """Test cross-dataset search with real data."""
        # Clear cache to ensure fresh load
        DatasetCache.clear()

        # This would test with real data
        result = find_gloss_across_datasets(
            "HELLO",
            datasets=["bslcp", "bobsl"],
            max_results_per_dataset=5,
        )

        assert result.gloss == "HELLO"
        print(f"Found {result.total_matches} matches for HELLO")
        for ds, freq in result.dataset_frequencies.items():
            print(f"  {ds}: {freq} occurrences")


# =============================================================================
# Edge Cases and Error Handling
# =============================================================================


class TestEdgeCases:
    """Tests for edge cases and error handling."""

    def test_empty_dataset_list(self):
        """Test with empty dataset list."""
        result = find_gloss_across_datasets("HELLO", datasets=[])

        assert result.total_matches == 0
        assert len(result.matches_by_dataset) == 0

    def test_nonexistent_gloss(self, mock_samples_dataset1, mock_dataset_class):
        """Test searching for non-existent gloss."""
        dataset = mock_dataset_class(mock_samples_dataset1)

        with patch("sltk.analysis.cross_dataset.DatasetCache.get_dataset") as mock_get:
            mock_get.return_value = dataset

            cache = DatasetCache.get_instance()
            cache._vocabularies["dataset1_gloss_None"] = {"hello": 1}
            cache._samples_by_gloss["dataset1_gloss_None"] = {}

            result = find_gloss_across_datasets("NONEXISTENT", datasets=["dataset1"])

            assert result.total_matches == 0
            assert result.dataset_frequencies.get("dataset1", 0) == 0

    def test_vocabulary_comparison_single_dataset(self):
        """Test vocabulary comparison with single dataset."""
        cache = DatasetCache.get_instance()
        cache._vocabularies["ds1_gloss_None"] = {"a": 1, "b": 2}

        with patch("sltk.analysis.cross_dataset.DatasetCache.get_vocabulary") as mock_vocab:

            def side_effect(name, **kwargs):
                key = f"{name}_gloss_None"
                return cache._vocabularies.get(key, {})

            mock_vocab.side_effect = side_effect

            result = compare_dataset_vocabularies(["ds1"])

            # With single dataset, shared = empty, unique = all
            assert len(result.shared_glosses) == 0
            assert "a" in result.unique_glosses["ds1"]
            assert "b" in result.unique_glosses["ds1"]

    def test_context_with_no_surrounding_glosses(self):
        """Test context extraction with isolated gloss."""
        # Create a sample with isolated gloss - shows data structure exists
        _ = Sample(
            id="sample_isolated",
            segments=SegmentList([Segment(start=0.0, end=0.5, label="LONELY", tier="gloss")]),
        )

        # The GlossMatch should have empty context lists
        match = GlossMatch(
            dataset="test",
            sample_id="sample_isolated",
            segment_idx=0,
            start=0.0,
            end=0.5,
            duration=0.5,
            context_before=[],
            context_after=[],
        )

        assert len(match.context_before) == 0
        assert len(match.context_after) == 0


class TestImportExport:
    """Tests for import and export from module."""

    def test_all_exports_accessible(self):
        """Test that all exports are accessible from sltk.analysis."""
        from sltk.analysis import (
            ContextPattern,
            CrossDatasetResult,
            DatasetCache,
            DurationStats,
            GlossMatch,
            ParallelExample,
            VocabularyComparison,
            analyze_gloss_contexts,
            batch_find_glosses,
            compare_dataset_vocabularies,
            compare_gloss_durations,
            find_common_glosses,
            find_gloss_across_datasets,
            find_parallel_examples,
            get_dataset_vocabulary_summary,
        )

        # Just verify imports work
        assert ContextPattern is not None
        assert find_gloss_across_datasets is not None
