import pytest

from supervision.detection.utils.vlms import edit_distance, fuzzy_match_index


@pytest.mark.parametrize(
    ("string_1", "string_2", "case_sensitive", "expected_result"),
    [
        # identical strings, various cases
        ("hello", "hello", True, 0),
        ("hello", "hello", False, 0),
        # case sensitive vs insensitive
        ("Test", "test", True, 1),
        ("Test", "test", False, 0),
        ("CASE", "case", True, 4),
        ("CASE", "case", False, 0),
        # completely different
        ("abc", "xyz", True, 3),
        ("abc", "xyz", False, 3),
        # one string empty
        ("hello", "", True, 5),
        ("", "world", True, 5),
        # single character cases
        ("a", "b", True, 1),
        ("A", "a", True, 1),
        ("A", "a", False, 0),
        # whitespaces
        ("hello world", "helloworld", True, 1),
        ("test", " test", True, 1),
        # unicode and emoji
        ("😊", "😊", True, 0),
        ("😊", "😢", True, 1),
        # long string vs empty
        ("a" * 100, "", True, 100),
        ("", "b" * 100, True, 100),
        # prefix/suffix
        ("prefix", "prefixes", True, 2),
        ("suffix", "asuffix", True, 1),
        # leading/trailing whitespace
        (" hello", "hello", True, 1),
        ("hello", "hello ", True, 1),
        # long almost-equal string
        (
            "The quick brown fox jumps over the lazy dog",
            "The quick brown fox jumps over the lazy cog",
            True,
            1,
        ),
        (
            "The quick brown fox jumps over the lazy dog",
            "The quick brown fox jumps over the lazy cog",
            False,
            1,
        ),
        # both empty
        ("", "", True, 0),
        ("", "", False, 0),
        # mixed case with symbols
        ("123ABC!", "123abc!", True, 3),
        ("123ABC!", "123abc!", False, 0),
    ],
)
def test_edit_distance(string_1, string_2, case_sensitive, expected_result) -> None:
    assert (
        edit_distance(string_1, string_2, case_sensitive=case_sensitive)
        == expected_result
    )


@pytest.mark.parametrize(
    ("candidates", "query", "threshold", "case_sensitive", "expected_result"),
    [
        # exact match at index 0
        (["cat", "dog", "rat"], "cat", 0, True, 0),
        # match at index 2 within threshold
        (["cat", "dog", "rat"], "dat", 1, True, 0),
        # no match due to high threshold
        (["cat", "dog", "rat"], "bat", 0, True, None),
        # multiple possible matches, returns first
        (["apple", "apply", "appla"], "apple", 1, True, 0),
        # case-insensitive match
        (["Alpha", "beta", "Gamma"], "alpha", 0, False, 0),
        # case-sensitive: no match
        (["Alpha", "beta", "Gamma"], "alpha", 0, True, None),
        # threshold boundary
        (["alpha", "beta", "gamma"], "bata", 1, True, 1),
        # no match (all distances too high)
        (["one", "two", "three"], "ten", 1, True, None),
        # unicode/emoji match
        (["😊", "😢", "😁"], "😄", 1, True, 0),
        (["😊", "😢", "😁"], "😊", 0, True, 0),
        # empty candidates
        ([], "any", 2, True, None),
        # empty query, non-empty candidates
        (["", "abc"], "", 0, True, 0),
        (["", "abc"], "", 1, True, 0),
        (["a", "b", "c"], "", 1, True, 0),
        # non-empty query, empty candidate
        (["", ""], "a", 1, True, 0),
        # all candidates require higher edit than threshold
        (["short", "words", "only"], "longerword", 2, True, None),
        # repeated candidates
        (["a", "a", "a"], "b", 1, True, 0),
    ],
)
def test_fuzzy_match_index(
    candidates, query, threshold, case_sensitive, expected_result
) -> None:
    assert (
        fuzzy_match_index(
            candidates=candidates,
            query=query,
            threshold=threshold,
            case_sensitive=case_sensitive,
        )
        == expected_result
    )
