"""Tests for rbsp.query.wildcard -- WildcardQuery, PrefixQuery, wildcard_to_regex,
   WildcardMatcher, PrefixMatcher."""

import os
import re
import sys
import unittest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
from rbsp.query.wildcard import (
    WildcardQuery,
    PrefixQuery,
    wildcard_to_regex,
    WildcardMatcher,
    PrefixMatcher,
)


# ---------------------------------------------------------------------------
# WildcardQuery / PrefixQuery dataclasses
# ---------------------------------------------------------------------------

class TestWildcardQuery(unittest.TestCase):
    """Tests for the WildcardQuery dataclass."""

    def test_fields(self):
        wq = WildcardQuery(pattern="foo*")
        self.assertEqual(wq.pattern, "foo*")

    def test_frozen(self):
        wq = WildcardQuery(pattern="foo*")
        with self.assertRaises(AttributeError):
            wq.pattern = "bar*"


class TestPrefixQuery(unittest.TestCase):
    """Tests for the PrefixQuery dataclass."""

    def test_fields(self):
        pq = PrefixQuery(prefix="foo")
        self.assertEqual(pq.prefix, "foo")

    def test_frozen(self):
        pq = PrefixQuery(prefix="foo")
        with self.assertRaises(AttributeError):
            pq.prefix = "bar"


# ---------------------------------------------------------------------------
# wildcard_to_regex
# ---------------------------------------------------------------------------

class TestWildcardToRegex(unittest.TestCase):
    """Tests for the wildcard_to_regex function."""

    def test_star_matches_any(self):
        r = wildcard_to_regex("foo*")
        self.assertIsNotNone(r.match("foo"))
        self.assertIsNotNone(r.match("foobar"))
        self.assertIsNone(r.match("barfoo"))

    def test_question_mark_matches_single_char(self):
        r = wildcard_to_regex("fo?")
        self.assertIsNotNone(r.match("foo"))
        self.assertIsNotNone(r.match("fob"))
        self.assertIsNone(r.match("fo"))
        self.assertIsNone(r.match("fooo"))

    def test_anchored_full_match(self):
        r = wildcard_to_regex("abc")
        self.assertIsNotNone(r.match("abc"))
        self.assertIsNone(r.match("abcd"))
        self.assertIsNone(r.match("xabc"))

    def test_escapes_metacharacters(self):
        r = wildcard_to_regex("file.txt")
        self.assertIsNotNone(r.match("file.txt"))
        self.assertIsNone(r.match("fileXtxt"))

    def test_complex_pattern(self):
        r = wildcard_to_regex("*.py")
        self.assertIsNotNone(r.match("test.py"))
        self.assertIsNotNone(r.match(".py"))
        self.assertIsNone(r.match("test.pyc"))

    def test_star_and_question(self):
        r = wildcard_to_regex("a*b?c")
        self.assertIsNotNone(r.match("aXXXbYc"))
        self.assertIsNotNone(r.match("abXc"))
        self.assertIsNone(r.match("abc"))  # ? needs exactly one char

    def test_empty_pattern(self):
        r = wildcard_to_regex("")
        self.assertIsNotNone(r.match(""))
        self.assertIsNone(r.match("a"))

    def test_returns_compiled_pattern(self):
        r = wildcard_to_regex("test*")
        self.assertIsInstance(r, re.Pattern)


# ---------------------------------------------------------------------------
# WildcardMatcher — matches
# ---------------------------------------------------------------------------

class TestWildcardMatcherMatches(unittest.TestCase):
    """Tests for WildcardMatcher.matches."""

    def test_star_pattern(self):
        wm = WildcardMatcher("foo*")
        self.assertTrue(wm.matches("foo"))
        self.assertTrue(wm.matches("foobar"))
        self.assertFalse(wm.matches("barfoo"))

    def test_question_pattern(self):
        wm = WildcardMatcher("f??")
        self.assertTrue(wm.matches("foo"))
        self.assertTrue(wm.matches("fab"))
        self.assertFalse(wm.matches("fo"))
        self.assertFalse(wm.matches("fooo"))

    def test_exact_pattern(self):
        wm = WildcardMatcher("exact")
        self.assertTrue(wm.matches("exact"))
        self.assertFalse(wm.matches("exactlyy"))


# ---------------------------------------------------------------------------
# WildcardMatcher — is_prefix_only
# ---------------------------------------------------------------------------

class TestWildcardMatcherIsPrefixOnly(unittest.TestCase):
    """Tests for WildcardMatcher.is_prefix_only."""

    def test_simple_prefix(self):
        self.assertTrue(WildcardMatcher("foo*").is_prefix_only())

    def test_multiple_trailing_stars(self):
        self.assertTrue(WildcardMatcher("foo**").is_prefix_only())

    def test_star_in_middle_is_not_prefix(self):
        self.assertFalse(WildcardMatcher("f*o").is_prefix_only())

    def test_question_mark_is_not_prefix(self):
        self.assertFalse(WildcardMatcher("foo?").is_prefix_only())

    def test_no_wildcard_is_not_prefix(self):
        self.assertFalse(WildcardMatcher("foo").is_prefix_only())

    def test_only_star(self):
        self.assertTrue(WildcardMatcher("*").is_prefix_only())


# ---------------------------------------------------------------------------
# WildcardMatcher — get_prefix
# ---------------------------------------------------------------------------

class TestWildcardMatcherGetPrefix(unittest.TestCase):
    """Tests for WildcardMatcher.get_prefix."""

    def test_prefix_before_star(self):
        wm = WildcardMatcher("foo*")
        self.assertEqual(wm.get_prefix(), "foo")

    def test_prefix_before_question(self):
        wm = WildcardMatcher("ab?cd")
        self.assertEqual(wm.get_prefix(), "ab")

    def test_no_wildcard(self):
        wm = WildcardMatcher("hello")
        self.assertEqual(wm.get_prefix(), "hello")

    def test_wildcard_at_start(self):
        wm = WildcardMatcher("*bar")
        self.assertEqual(wm.get_prefix(), "")

    def test_empty_pattern(self):
        wm = WildcardMatcher("")
        self.assertEqual(wm.get_prefix(), "")


# ---------------------------------------------------------------------------
# WildcardMatcher — find_matches
# ---------------------------------------------------------------------------

class TestWildcardMatcherFindMatches(unittest.TestCase):
    """Tests for WildcardMatcher.find_matches."""

    def test_find_matches_star(self):
        wm = WildcardMatcher("py*")
        terms = ["python", "pyre", "java", "pyro"]
        matches = wm.find_matches(terms)
        self.assertEqual(set(matches), {"python", "pyre", "pyro"})

    def test_find_matches_question(self):
        wm = WildcardMatcher("fo?")
        terms = ["foo", "fob", "for", "food", "fo"]
        matches = wm.find_matches(terms)
        self.assertEqual(set(matches), {"foo", "fob", "for"})

    def test_find_matches_empty(self):
        wm = WildcardMatcher("zzz*")
        terms = ["python", "java"]
        matches = wm.find_matches(terms)
        self.assertEqual(matches, [])

    def test_find_matches_all(self):
        wm = WildcardMatcher("*")
        terms = ["a", "b", "c"]
        matches = wm.find_matches(terms)
        self.assertEqual(len(matches), 3)

    def test_find_matches_preserves_order(self):
        wm = WildcardMatcher("*")
        terms = ["c", "a", "b"]
        matches = wm.find_matches(terms)
        self.assertEqual(matches, ["c", "a", "b"])


# ---------------------------------------------------------------------------
# PrefixMatcher — matches
# ---------------------------------------------------------------------------

class TestPrefixMatcherMatches(unittest.TestCase):
    """Tests for PrefixMatcher.matches."""

    def test_matches_prefix(self):
        pm = PrefixMatcher("py")
        self.assertTrue(pm.matches("python"))
        self.assertTrue(pm.matches("py"))
        self.assertFalse(pm.matches("java"))

    def test_empty_prefix_matches_all(self):
        pm = PrefixMatcher("")
        self.assertTrue(pm.matches("anything"))
        self.assertTrue(pm.matches(""))

    def test_no_match(self):
        pm = PrefixMatcher("xyz")
        self.assertFalse(pm.matches("abc"))


# ---------------------------------------------------------------------------
# PrefixMatcher — find_matches
# ---------------------------------------------------------------------------

class TestPrefixMatcherFindMatches(unittest.TestCase):
    """Tests for PrefixMatcher.find_matches with sorted lists."""

    def test_finds_matching_terms(self):
        pm = PrefixMatcher("py")
        sorted_terms = ["alpha", "beta", "pyro", "python", "pyre", "zebra"]
        # Note: sorted_terms must be sorted for bisect to work correctly
        sorted_terms.sort()
        matches = pm.find_matches(sorted_terms)
        self.assertEqual(set(matches), {"pyro", "python", "pyre"})

    def test_no_matches(self):
        pm = PrefixMatcher("zzz")
        sorted_terms = ["alpha", "beta", "gamma"]
        matches = pm.find_matches(sorted_terms)
        self.assertEqual(matches, [])

    def test_empty_prefix_returns_all(self):
        pm = PrefixMatcher("")
        sorted_terms = ["a", "b", "c"]
        matches = pm.find_matches(sorted_terms)
        self.assertEqual(matches, ["a", "b", "c"])

    def test_empty_list(self):
        pm = PrefixMatcher("py")
        matches = pm.find_matches([])
        self.assertEqual(matches, [])

    def test_single_match(self):
        pm = PrefixMatcher("py")
        sorted_terms = ["alpha", "python", "zebra"]
        matches = pm.find_matches(sorted_terms)
        self.assertEqual(matches, ["python"])

    def test_all_match(self):
        pm = PrefixMatcher("a")
        sorted_terms = ["abc", "ade", "afg"]
        matches = pm.find_matches(sorted_terms)
        self.assertEqual(matches, ["abc", "ade", "afg"])

    def test_results_in_sorted_order(self):
        pm = PrefixMatcher("te")
        sorted_terms = ["team", "test", "text", "zebra"]
        matches = pm.find_matches(sorted_terms)
        self.assertEqual(matches, ["team", "test", "text"])
        self.assertEqual(matches, sorted(matches))


if __name__ == "__main__":
    unittest.main()
