"""Tests for _parser module."""

from __future__ import annotations

import pytest

from db_test_helpers.core._matchers import (
    AnyMatcher,
    EqMatcher,
    parse_matcher,
)
from db_test_helpers.core._parser import parse


# --- parse_matcher (public API) ---


class TestParseMatcher:
    def test_any(self):
        m = parse_matcher("$any()", {})
        assert isinstance(m, AnyMatcher)

    def test_eq_string(self):
        m = parse_matcher("$eq(hello)", {})
        assert isinstance(m, EqMatcher)
        assert m.matches("hello") is True

    def test_eq_int(self):
        m = parse_matcher("$eq(42)", {})
        assert isinstance(m, EqMatcher)
        assert m.matches(42) is True

    def test_eq_quoted_string(self):
        m = parse_matcher('$eq("123")', {})
        assert isinstance(m, EqMatcher)
        assert m.matches("123") is True
        assert m.matches(123) is False

    def test_lt(self):
        m = parse_matcher("$lt(100)", {})
        assert m.matches(50) is True
        assert m.matches(100) is False

    def test_gt(self):
        m = parse_matcher("$gt(0)", {})
        assert m.matches(1) is True
        assert m.matches(0) is False

    def test_not(self):
        m = parse_matcher("$not($eq(admin))", {})
        assert m.matches("user") is True
        assert m.matches("admin") is False

    def test_and(self):
        m = parse_matcher("$and($gt(0),$lt(100))", {})
        assert m.matches(50) is True
        assert m.matches(0) is False

    def test_or(self):
        m = parse_matcher("$or($eq(active),$eq(pending))", {})
        assert m.matches("active") is True
        assert m.matches("deleted") is False

    def test_var(self):
        m = parse_matcher("$var(uid)", {"uid": "abc-123"})
        assert m.matches("abc-123") is True
        assert m.matches("other") is False

    def test_nested(self):
        m = parse_matcher("$not($or($eq(a),$eq(b)))", {})
        assert m.matches("c") is True
        assert m.matches("a") is False


# --- Error cases ---


class TestParseMatcherErrors:
    def test_unknown_function(self):
        with pytest.raises(ValueError, match="Unknown DSL function"):
            parse_matcher("$unknown(x)", {})

    def test_lt_requires_number(self):
        with pytest.raises(ValueError, match="numeric argument"):
            parse_matcher("$lt(hello)", {})

    def test_not_requires_matcher(self):
        with pytest.raises(ValueError, match="matcher argument"):
            parse_matcher("$not(hello)", {})

    def test_and_requires_at_least_2(self):
        with pytest.raises(ValueError, match="at least 2 arguments"):
            parse_matcher("$and($eq(a))", {})

    def test_var_undefined(self):
        with pytest.raises(ValueError, match="Undefined variable"):
            parse_matcher("$var(missing)", {})

    def test_trailing_characters(self):
        with pytest.raises(ValueError, match="Unexpected trailing"):
            parse_matcher("$eq(a)extra", {})


# --- _parser.parse direct call (internal) ---


class TestParseInternal:
    def test_parse_returns_matcher(self):
        m = parse("$eq(hello)", {})
        assert isinstance(m, EqMatcher)
        assert m.matches("hello") is True

    def test_parse_nested(self):
        m = parse("$not($eq(admin))", {})
        assert m.matches("user") is True
        assert m.matches("admin") is False

    def test_parse_with_variables(self):
        m = parse("$var(uid)", {"uid": "abc-123"})
        assert m.matches("abc-123") is True
