"""Tests for trigram regex extraction."""

from __future__ import annotations

import zlib

from attocode_intel._internal.integrations.context.trigram_regex import (
    extract_required_trigrams,
    longest_literal_run,
)


def _hash(s: str) -> int:
    """Compute the CRC32 trigram hash for a 3-char string."""
    return zlib.crc32(s.encode("utf-8")) & 0xFFFFFFFF


# ---------------------------------------------------------------------------
# extract_required_trigrams
# ---------------------------------------------------------------------------
class TestExtractRequiredTrigrams:
    def test_simple_literal(self) -> None:
        hashes = extract_required_trigrams("grep_search")
        assert len(hashes) > 0
        # "gre" should be one of the trigrams
        assert _hash("gre") in hashes

    def test_wildcard_returns_empty(self) -> None:
        assert extract_required_trigrams(".*") == []

    def test_dot_star_middle(self) -> None:
        hashes = extract_required_trigrams("parse.*Config")
        # Should have trigrams from both "parse" and "Config"
        assert _hash("par") in hashes
        assert _hash("Con") in hashes

    def test_short_pattern(self) -> None:
        # "ab" is too short for any trigram
        assert extract_required_trigrams("ab") == []

    def test_exactly_three_chars(self) -> None:
        hashes = extract_required_trigrams("abc")
        assert len(hashes) == 1
        assert _hash("abc") in hashes

    def test_character_class(self) -> None:
        # [abc]def -> only "def" is a guaranteed literal run
        hashes = extract_required_trigrams("[abc]defgh")
        assert _hash("def") in hashes
        # "abc" should NOT be in hashes (it's a character class)
        assert _hash("abc") not in hashes

    def test_alternation_no_common(self) -> None:
        # foo|bar -> no common prefix >= 3 chars
        assert extract_required_trigrams("foo|bar") == []

    def test_group(self) -> None:
        hashes = extract_required_trigrams("(hello)world")
        assert _hash("hel") in hashes
        assert _hash("wor") in hashes

    def test_quantifier_min_zero(self) -> None:
        # x? means x may not appear, so "x" doesn't contribute
        hashes = extract_required_trigrams("x?hello")
        assert _hash("hel") in hashes

    def test_quantifier_min_one(self) -> None:
        # x+ means x appears at least once
        hashes = extract_required_trigrams("hello+world")
        assert _hash("hel") in hashes

    def test_case_insensitive(self) -> None:
        hashes = extract_required_trigrams("Hello", case_insensitive=True)
        assert _hash("hel") in hashes
        # "Hel" should NOT be present (lowercased)
        assert _hash("Hel") not in hashes

    def test_invalid_regex(self) -> None:
        assert extract_required_trigrams("(?P<unclosed") == []

    def test_anchor_ignored(self) -> None:
        hashes_with = extract_required_trigrams("^hello$")
        hashes_without = extract_required_trigrams("hello")
        assert set(hashes_with) == set(hashes_without)

    def test_returns_deduplicated(self) -> None:
        hashes = extract_required_trigrams("aaaaaa")
        # "aaa" appears 4 times but should be deduplicated
        assert hashes.count(_hash("aaa")) <= 1

    def test_dot_pattern(self) -> None:
        # Single dot matches any char -> breaks literal run
        hashes = extract_required_trigrams("hel.o_world")
        # "hel" is too short? No: "hel" is only 3 chars -> 1 trigram
        # But the dot breaks the run, so we get nothing >= 3 from "hel"
        # Actually "hel" is exactly 3 chars -> 1 trigram
        # The dot breaks it, so we have "hel" (nope, only 3 chars before dot)
        # Let's check: "hel" before dot -> 3 chars -> 1 trigram
        # Actually the sre_parse will see LITERAL h, LITERAL e, LITERAL l, ANY .
        # So we get "hel" as a run of 3 -> yields 1 trigram
        # And "o_world" after dot -> "o_w", "wor", "orl", "rld" -> 5 chars, multiple trigrams
        # Wait, after the dot: "o_world" -> 7 chars -> 5 trigrams
        assert len(hashes) > 0


# ---------------------------------------------------------------------------
# longest_literal_run
# ---------------------------------------------------------------------------
class TestLongestLiteralRun:
    def test_simple(self) -> None:
        assert longest_literal_run("def hello") == "def hello"

    def test_split(self) -> None:
        result = longest_literal_run("foo.*bar")
        assert result in ("foo", "bar")

    def test_empty(self) -> None:
        assert longest_literal_run(".*") == ""

    def test_longer_wins(self) -> None:
        result = longest_literal_run("ab.*longer_string")
        assert result == "longer_string"

    def test_invalid_pattern(self) -> None:
        assert longest_literal_run("(?P<bad") == ""


# ---------------------------------------------------------------------------
# Complex regex patterns
# ---------------------------------------------------------------------------
class TestComplexPatterns:
    def test_alternation_with_common_prefix(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        # "foobar|foobaz" shares "foo" and "ba" trigrams
        result = extract_required_trigrams("foobar|foobaz")
        assert isinstance(result, list)

    def test_quantifier_optional_drops_trigrams(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        # "hello?" makes the 'o' optional, so "hell" is the reliable prefix
        result = extract_required_trigrams("hello?")
        assert isinstance(result, list)

    def test_character_class(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        # "[abc]def" - first char is variable, "def" is literal
        result = extract_required_trigrams("[abc]def")
        assert isinstance(result, list)

    def test_dot_wildcard(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        # "hel.o" - dot breaks trigram extraction
        result = extract_required_trigrams("hel.o")
        assert isinstance(result, list)

    def test_empty_pattern(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        result = extract_required_trigrams("")
        assert result == []

    def test_short_pattern(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        # "ab" is too short for trigrams
        result = extract_required_trigrams("ab")
        assert result == []

    def test_case_insensitive_flag(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        result_sensitive = extract_required_trigrams("Hello")
        result_insensitive = extract_required_trigrams("Hello", case_insensitive=True)
        assert isinstance(result_sensitive, list)
        assert isinstance(result_insensitive, list)

    def test_escaped_special_chars(self):
        from attocode_intel._internal.integrations.context.trigram_regex import (
            extract_required_trigrams,
        )
        # Escaped dot should be treated as literal
        result = extract_required_trigrams(r"foo\.bar")
        assert isinstance(result, list)
        assert len(result) > 0


# ---------------------------------------------------------------------------
# include_literals
# ---------------------------------------------------------------------------
class TestIncludeLiterals:
    def test_returns_tuple(self) -> None:
        result = extract_required_trigrams("grep_search", include_literals=True)
        assert isinstance(result, tuple)
        hashes, literals = result
        assert len(hashes) == len(literals)
        assert len(hashes) > 0

    def test_literals_are_3_chars(self) -> None:
        _, literals = extract_required_trigrams("hello_world", include_literals=True)
        for lit in literals:
            assert len(lit) == 3

    def test_known_trigram(self) -> None:
        hashes, literals = extract_required_trigrams("abc", include_literals=True)
        assert hashes == [_hash("abc")]
        assert literals == ["abc"]

    def test_case_insensitive_lowercases(self) -> None:
        _, literals = extract_required_trigrams(
            "Hello", include_literals=True, case_insensitive=True,
        )
        for lit in literals:
            assert lit == lit.lower()

    def test_empty_pattern(self) -> None:
        hashes, literals = extract_required_trigrams(".*", include_literals=True)
        assert hashes == []
        assert literals == []

    def test_invalid_regex(self) -> None:
        hashes, literals = extract_required_trigrams("(?P<bad", include_literals=True)
        assert hashes == []
        assert literals == []

    def test_hashes_match_non_literal_mode(self) -> None:
        hashes_only = extract_required_trigrams("def process_event")
        hashes_with, _ = extract_required_trigrams(
            "def process_event", include_literals=True,
        )
        assert set(hashes_only) == set(hashes_with)
