"""Tests for basic expression creation and DSL."""

import re

import pytest

from therismos import (
    FALSE,
    TRUE,
    AllExpr,
    AnyExpr,
    Eq,
    F,
    Field,
    Ge,
    Gt,
    In,
    IsNull,
    Le,
    Lt,
    Ne,
    NotExpr,
    Regex,
)


class TestFieldCreation:
    """Test Field creation and basic operations."""

    def test_field_creation_simple(self) -> None:
        """Test creating a field without a type."""
        field = Field("name")
        assert field.name == "name"
        assert field.type_ is None

    def test_field_creation_with_type(self) -> None:
        """Test creating a field with a type."""
        field = Field("age", int)
        assert field.name == "age"
        assert field.type_ is int

    def test_f_helper_function(self) -> None:
        """Test the F() helper function."""
        field = F("email")
        assert field.name == "email"
        assert field.type_ is None

        field_with_type = F("count", int)
        assert field_with_type.name == "count"
        assert field_with_type.type_ is int


class TestAtomicExpressions:
    """Test atomic expression creation via DSL."""

    @pytest.mark.parametrize(
        "operation,value,expected_type",
        [
            (lambda f: f == 10, 10, Eq),
            (lambda f: f != 10, 10, Ne),
            (lambda f: f < 10, 10, Lt),
            (lambda f: f <= 10, 10, Le),
            (lambda f: f > 10, 10, Gt),
            (lambda f: f >= 10, 10, Ge),
        ],
    )
    def test_comparison_operators(self, operation: object, value: int, expected_type: type) -> None:
        """Test comparison operators create correct expression types."""
        field = F("age")
        expr = operation(field)
        assert isinstance(expr, expected_type)
        assert expr.field.name == "age"  # type: ignore
        assert expr.value == value  # type: ignore

    def test_regex_matches(self) -> None:
        """Test regex matching expression."""
        field = F("name")
        expr = field.matches(r"^A.*")
        assert isinstance(expr, Regex)
        assert expr.field.name == "name"
        assert expr.value == r"^A.*"
        assert expr.flags is None

    def test_regex_matches_with_flags(self) -> None:
        """Test regex matching with flags."""
        field = F("name")
        expr = field.matches(r"^A.*", re.IGNORECASE)
        assert isinstance(expr, Regex)
        assert expr.flags == re.IGNORECASE

    def test_in_with_values(self) -> None:
        """Test IN expression with individual values."""
        field = F("status")
        expr = field.is_in("active", "pending")
        assert isinstance(expr, In)
        assert expr.field.name == "status"
        assert expr.values == ("active", "pending")

    def test_one_of_with_list(self) -> None:
        """Test is_one_of with a list."""
        field = F("status")
        expr = field.is_one_of(["active", "pending", "completed"])
        assert isinstance(expr, In)
        assert expr.values == ("active", "pending", "completed")

    def test_is_null(self) -> None:
        """Test IS NULL expression."""
        field = F("deleted_at")
        expr = field.is_null()
        assert isinstance(expr, IsNull)
        assert expr.field.name == "deleted_at"
        assert expr.is_null is True

    def test_is_not_null(self) -> None:
        """Test IS NOT NULL expression."""
        field = F("created_at")
        expr = field.is_not_null()
        assert isinstance(expr, IsNull)
        assert expr.field.name == "created_at"
        assert expr.is_null is False


class TestCompoundExpressions:
    """Test compound expression creation via DSL."""

    def test_and_operator(self) -> None:
        """Test & operator creates AllExpr."""
        age = F("age")
        name = F("name")
        expr = (age > 18) & (name == "Alice")
        assert isinstance(expr, AllExpr)
        assert len(expr.exprs) == 2

    def test_or_operator(self) -> None:
        """Test | operator creates AnyExpr."""
        status = F("status")
        expr = (status == "active") | (status == "pending")
        assert isinstance(expr, AnyExpr)
        assert len(expr.exprs) == 2

    def test_not_operator(self) -> None:
        """Test ~ operator creates NotExpr."""
        age = F("age")
        expr = ~(age > 18)
        assert isinstance(expr, NotExpr)
        assert isinstance(expr.expr, Gt)

    def test_complex_expression(self) -> None:
        """Test complex nested expression."""
        age = F("age")
        name = F("name")
        status = F("status")

        expr = ((age > 18) & (name == "Alice")) | (status == "admin")
        assert isinstance(expr, AnyExpr)
        assert isinstance(expr.exprs[0], AllExpr)


class TestImmutability:
    """Test that all expressions are immutable."""

    def test_field_immutability(self) -> None:
        """Test that Field is frozen."""
        field = F("name")
        with pytest.raises(AttributeError):
            field.name = "other"  # type: ignore

    def test_eq_immutability(self) -> None:
        """Test that Eq is frozen."""
        field = F("age")
        expr = field == 10
        with pytest.raises(AttributeError):
            expr.value = 20  # type: ignore

    def test_all_expr_immutability(self) -> None:
        """Test that AllExpr is frozen."""
        age = F("age")
        expr = (age > 18) & (age < 65)
        with pytest.raises(AttributeError):
            expr.exprs = ()  # type: ignore


class TestLogicalConstants:
    """Test TRUE and FALSE constants."""

    def test_true_constant(self) -> None:
        """Test TRUE constant."""
        from therismos import TrueExpr

        assert isinstance(TRUE, TrueExpr)

    def test_false_constant(self) -> None:
        """Test FALSE constant."""
        from therismos import FalseExpr

        assert isinstance(FALSE, FalseExpr)

    def test_true_false_in_expressions(self) -> None:
        """Test using TRUE/FALSE in compound expressions."""
        age = F("age")
        expr1 = (age > 18) & TRUE
        assert isinstance(expr1, AllExpr)

        expr2 = (age > 18) | FALSE
        assert isinstance(expr2, AnyExpr)
