from typing import Iterable

import pytest

from debputy.lsp.spellchecking import (
    _HAS_HUNSPELL,
    HunspellSpellchecker,
    _builtin_exception_words,
    _NO_CORRECTIONS,
    Spellchecker,
)


@pytest.fixture(scope="session")
def test_spellchecker() -> Spellchecker:

    class TestSpellchecker(Spellchecker):
        def provide_corrections_for(self, word: str) -> Iterable[str]:
            raise NotImplementedError

    return TestSpellchecker()


@pytest.fixture(scope="session")
def hunspell_spellchecker() -> Spellchecker:
    if not _HAS_HUNSPELL:
        pytest.skip("Missing python3-hunspell hunspell-en-us")
        return

    return HunspellSpellchecker()


@pytest.mark.skipif(not _HAS_HUNSPELL, reason="Missing python3-hunspell hunspell-en-us")
def test_hunspell_checker(hunspell_spellchecker: Spellchecker) -> None:
    r = frozenset(hunspell_spellchecker.provide_corrections_for("tets"))
    assert r
    assert "test" in r


@pytest.mark.skipif(not _HAS_HUNSPELL, reason="Missing python3-hunspell hunspell-en-us")
def test_hunspell_respects_known_exclusions(
    hunspell_spellchecker: Spellchecker,
) -> None:
    for word in _builtin_exception_words():
        assert hunspell_spellchecker.provide_corrections_for(word) is _NO_CORRECTIONS


@pytest.mark.parametrize(
    "excluded_word",
    [
        "dpkg-foo",
        "dh_foo",
        "dh-sequence-foo",
        "update-foo",
        "debconf-foo",
        "DEB_FOO",
        "DPKG_FOO",
    ],
)
@pytest.mark.skipif(not _HAS_HUNSPELL, reason="Missing python3-hunspell hunspell-en-us")
def test_hunspell_respects_word_level_exclusions(
    hunspell_spellchecker: Spellchecker, excluded_word: str
) -> None:
    assert (
        hunspell_spellchecker.provide_corrections_for(excluded_word) is _NO_CORRECTIONS
    )


def test_iter_words(test_spellchecker: Spellchecker) -> None:
    line = "  * debputy: Avoid installing unnecessary files into `udeb` packages"
    words = list(test_spellchecker.iter_words(line))

    # Validate that `udeb` is excluded and words have been isolated
    assert [w[0] for w in words] == [
        "debputy",
        "Avoid",
        "installing",
        "unnecessary",
        "files",
        "into",
        "packages",
    ]

    # Check the word positions
    for word, start, end in words:
        assert line[start:end] == word


@pytest.mark.parametrize(
    "excluded_word",
    [
        "31_aide_ldconfig",
        "SCRIPTRETVAL",
        "$SCRIPTRETVAL",
        "{script_retval}",
        "32bit",
        "<some@email.com>",
    ],
)
def test_general_exceptions(
    test_spellchecker: Spellchecker, excluded_word: str
) -> None:
    line = f"some phrase where {excluded_word} appears"
    words = list(test_spellchecker.iter_words(line))
    # Assert we are excluding the word
    assert [w[0] for w in words] == ["some", "phrase", "where", "appears"]

    for word, start, end in words:
        assert line[start:end] == word
