"""Tests for glossing module."""

import tempfile
from pathlib import Path

import pytest

from sltk.data.core import Segment, SegmentList
from sltk.glossing import (
    Vocabulary,
    gloss_category,
    is_fingerspelling,
    is_pointing_sign,
    normalize_gloss,
    parse_compound_gloss,
)


class TestVocabulary:
    """Tests for Vocabulary class."""

    def test_create_empty_vocabulary(self):
        """Test creating empty vocabulary with special tokens."""
        vocab = Vocabulary()

        assert len(vocab) == 4  # PAD, UNK, BOS, EOS
        assert vocab.pad_id == 0
        assert vocab.unk_id == 1
        assert vocab.bos_id == 2
        assert vocab.eos_id == 3

    def test_create_vocabulary_from_list(self):
        """Test creating vocabulary from gloss list."""
        glosses = ["HELLO", "WORLD", "HELLO", "TEST"]
        vocab = Vocabulary(glosses, min_count=1)

        # 4 special + 3 unique
        assert len(vocab) == 7
        assert "HELLO" in vocab
        assert "WORLD" in vocab
        assert "TEST" in vocab

    def test_min_count_filtering(self):
        """Test minimum count filtering."""
        glosses = ["HELLO", "HELLO", "HELLO", "RARE", "TEST", "TEST"]
        vocab = Vocabulary(glosses, min_count=2)

        assert "HELLO" in vocab  # count=3
        assert "TEST" in vocab  # count=2
        assert "RARE" not in vocab  # count=1

    def test_encode_decode(self):
        """Test encoding and decoding."""
        vocab = Vocabulary(["HELLO", "WORLD"])

        ids = vocab.encode(["HELLO", "WORLD"])
        assert len(ids) == 2

        decoded = vocab.decode(ids)
        assert decoded == ["HELLO", "WORLD"]

    def test_encode_with_special_tokens(self):
        """Test encoding with BOS/EOS tokens."""
        vocab = Vocabulary(["HELLO", "WORLD"])

        ids = vocab.encode(["HELLO"], add_bos=True, add_eos=True)
        assert len(ids) == 3
        assert ids[0] == vocab.bos_id
        assert ids[-1] == vocab.eos_id

    def test_encode_unknown(self):
        """Test encoding unknown glosses."""
        vocab = Vocabulary(["HELLO"])

        ids = vocab.encode(["HELLO", "UNKNOWN"])
        assert ids[1] == vocab.unk_id

    def test_decode_skip_special(self):
        """Test decoding with special token skipping."""
        vocab = Vocabulary(["HELLO"])

        ids = [vocab.bos_id, vocab["HELLO"], vocab.eos_id]

        with_special = vocab.decode(ids, skip_special=False)
        without_special = vocab.decode(ids, skip_special=True)

        assert len(with_special) == 3
        assert len(without_special) == 1
        assert without_special[0] == "HELLO"

    def test_from_segments(self):
        """Test building vocabulary from segments."""
        segments = SegmentList(
            [
                Segment(0, 1, label="HELLO"),
                Segment(1, 2, label="WORLD"),
                Segment(2, 3, label="HELLO"),
            ]
        )

        vocab = Vocabulary.from_segments(segments)

        assert "HELLO" in vocab
        assert "WORLD" in vocab
        assert vocab.get_count("HELLO") == 2
        assert vocab.get_count("WORLD") == 1

    def test_most_common(self):
        """Test getting most common glosses."""
        glosses = ["A", "A", "A", "B", "B", "C"]
        vocab = Vocabulary(glosses)

        common = vocab.most_common(2)
        assert common[0][0] == "A"
        assert common[0][1] == 3
        assert common[1][0] == "B"
        assert common[1][1] == 2

    def test_save_load(self):
        """Test saving and loading vocabulary."""
        vocab = Vocabulary(["HELLO", "WORLD", "TEST"])

        with tempfile.TemporaryDirectory() as tmpdir:
            path = Path(tmpdir) / "vocab.json"
            vocab.save(path)

            loaded = Vocabulary.load(path)

            assert len(loaded) == len(vocab)
            assert loaded.glosses == vocab.glosses

    def test_getitem(self):
        """Test dictionary-style access."""
        vocab = Vocabulary(["HELLO"])

        # Get ID from gloss
        assert isinstance(vocab["HELLO"], int)

        # Get gloss from ID
        hello_id = vocab["HELLO"]
        assert vocab[hello_id] == "HELLO"


class TestGlossUtilities:
    """Tests for gloss utility functions."""

    def test_normalize_gloss(self):
        """Test gloss normalization."""
        assert normalize_gloss("  HELLO  ") == "hello"
        assert normalize_gloss("HELLO  WORLD") == "hello world"
        assert normalize_gloss("HELLO", lowercase=False) == "HELLO"

    def test_parse_compound_gloss(self):
        """Test compound gloss parsing."""
        assert parse_compound_gloss("HELLO+WORLD") == ["HELLO", "WORLD"]
        assert parse_compound_gloss("A+B+C") == ["A", "B", "C"]
        assert parse_compound_gloss("SINGLE") == ["SINGLE"]
        assert parse_compound_gloss("A + B") == ["A", "B"]

    def test_is_pointing_sign(self):
        """Test pointing sign detection."""
        assert is_pointing_sign("PT:PRO1SG")
        assert is_pointing_sign("PT:LOC")
        assert is_pointing_sign("IX")
        assert is_pointing_sign("POINT")
        assert not is_pointing_sign("HELLO")
        assert not is_pointing_sign("HOUSE")

    def test_is_fingerspelling(self):
        """Test fingerspelling detection."""
        assert is_fingerspelling("FS:HELLO")
        assert is_fingerspelling("FS-JOHN")
        assert is_fingerspelling("#NAME")
        assert not is_fingerspelling("HELLO")
        assert not is_fingerspelling("FINGERSPELL")

    def test_gloss_category(self):
        """Test gloss categorization."""
        assert gloss_category("HELLO") == "lexical"
        assert gloss_category("PT:PRO1SG") == "pointing"
        assert gloss_category("FS:NAME") == "fingerspelling"
        assert gloss_category("HELLO+WORLD") == "compound"


class TestVocabularyWithRealData:
    """Tests using real BSLCP data."""

    @pytest.fixture
    def bslcp_dataset(self):
        """Load BSLCP dataset if available."""
        from pathlib import Path

        bslcp_root = Path("/vol/research/datasets/multiview/BSLCP")
        if not bslcp_root.exists():
            pytest.skip("BSLCP data not available")

        from sltk.data.datasets import BSLCPDataset

        return BSLCPDataset(root=bslcp_root)

    def test_build_from_real_samples(self, bslcp_dataset):
        """Test building vocabulary from real data."""
        samples = [bslcp_dataset[i] for i in range(5)]
        vocab = Vocabulary.from_samples(iter(samples))

        assert len(vocab) > 4  # More than just special tokens
        assert vocab.get_count("PT:PRO1SG") > 0  # Common pointing sign

    def test_categorize_real_glosses(self, bslcp_dataset):
        """Test categorizing real glosses."""
        sample = bslcp_dataset[0]

        categories = {}
        for seg in sample.segments:
            if seg.label:
                cat = gloss_category(seg.label)
                categories[cat] = categories.get(cat, 0) + 1

        # Should have mix of lexical and pointing
        assert "lexical" in categories
        # BSLCP has lots of pointing signs
        assert categories.get("pointing", 0) > 0
