"""Tests for single-valued validation at construction time."""

import pytest

from therismos.expr import F


class TestEqValidation:
    """Test that Eq validates single values at construction."""

    def test_eq_with_list_raises_error(self) -> None:
        """Test that Eq raises TypeError when value is a list."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") == [1, 2, 3]
        assert "Eq requires a single value" in str(exc_info.value)
        assert "Use In expression for multiple values" in str(exc_info.value)

    def test_eq_with_tuple_raises_error(self) -> None:
        """Test that Eq raises TypeError when value is a tuple."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") == (1, 2, 3)
        assert "Eq requires a single value" in str(exc_info.value)

    def test_eq_with_set_raises_error(self) -> None:
        """Test that Eq raises TypeError when value is a set."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") == {1, 2, 3}
        assert "Eq requires a single value" in str(exc_info.value)

    def test_eq_with_single_value_succeeds(self) -> None:
        """Test that Eq accepts single values."""
        expr = F("x") == 5
        assert expr is not None
        assert expr.value == 5


class TestNeValidation:
    """Test that Ne validates single values at construction."""

    def test_ne_with_list_raises_error(self) -> None:
        """Test that Ne raises TypeError when value is a list."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") != [1, 2, 3]
        assert "Ne requires a single value" in str(exc_info.value)

    def test_ne_with_tuple_raises_error(self) -> None:
        """Test that Ne raises TypeError when value is a tuple."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") != (1, 2, 3)
        assert "Ne requires a single value" in str(exc_info.value)

    def test_ne_with_set_raises_error(self) -> None:
        """Test that Ne raises TypeError when value is a set."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") != {1, 2, 3}
        assert "Ne requires a single value" in str(exc_info.value)

    def test_ne_with_single_value_succeeds(self) -> None:
        """Test that Ne accepts single values."""
        expr = F("x") != 5
        assert expr is not None
        assert expr.value == 5


class TestLtValidation:
    """Test that Lt validates single values at construction."""

    def test_lt_with_list_raises_error(self) -> None:
        """Test that Lt raises TypeError when value is a list."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") < [1, 2, 3]
        assert "Lt requires a single value" in str(exc_info.value)

    def test_lt_with_tuple_raises_error(self) -> None:
        """Test that Lt raises TypeError when value is a tuple."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") < (1, 2, 3)
        assert "Lt requires a single value" in str(exc_info.value)

    def test_lt_with_single_value_succeeds(self) -> None:
        """Test that Lt accepts single values."""
        expr = F("x") < 5
        assert expr is not None
        assert expr.value == 5


class TestLeValidation:
    """Test that Le validates single values at construction."""

    def test_le_with_list_raises_error(self) -> None:
        """Test that Le raises TypeError when value is a list."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") <= [1, 2, 3]
        assert "Le requires a single value" in str(exc_info.value)

    def test_le_with_tuple_raises_error(self) -> None:
        """Test that Le raises TypeError when value is a tuple."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") <= (1, 2, 3)
        assert "Le requires a single value" in str(exc_info.value)

    def test_le_with_single_value_succeeds(self) -> None:
        """Test that Le accepts single values."""
        expr = F("x") <= 5
        assert expr is not None
        assert expr.value == 5


class TestGtValidation:
    """Test that Gt validates single values at construction."""

    def test_gt_with_list_raises_error(self) -> None:
        """Test that Gt raises TypeError when value is a list."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") > [1, 2, 3]
        assert "Gt requires a single value" in str(exc_info.value)

    def test_gt_with_tuple_raises_error(self) -> None:
        """Test that Gt raises TypeError when value is a tuple."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") > (1, 2, 3)
        assert "Gt requires a single value" in str(exc_info.value)

    def test_gt_with_single_value_succeeds(self) -> None:
        """Test that Gt accepts single values."""
        expr = F("x") > 5
        assert expr is not None
        assert expr.value == 5


class TestGeValidation:
    """Test that Ge validates single values at construction."""

    def test_ge_with_list_raises_error(self) -> None:
        """Test that Ge raises TypeError when value is a list."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") >= [1, 2, 3]
        assert "Ge requires a single value" in str(exc_info.value)

    def test_ge_with_tuple_raises_error(self) -> None:
        """Test that Ge raises TypeError when value is a tuple."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") >= (1, 2, 3)
        assert "Ge requires a single value" in str(exc_info.value)

    def test_ge_with_single_value_succeeds(self) -> None:
        """Test that Ge accepts single values."""
        expr = F("x") >= 5
        assert expr is not None
        assert expr.value == 5


class TestRegexValidation:
    """Test that Regex validates single pattern at construction."""

    def test_regex_with_list_raises_error(self) -> None:
        """Test that Regex raises TypeError when pattern is a list."""
        with pytest.raises(TypeError) as exc_info:
            F("name").matches(["pattern1", "pattern2"])  # type: ignore[arg-type]
        assert "Regex requires a single value" in str(exc_info.value)

    def test_regex_with_tuple_raises_error(self) -> None:
        """Test that Regex raises TypeError when pattern is a tuple."""
        with pytest.raises(TypeError) as exc_info:
            F("name").matches(("pattern1", "pattern2"))  # type: ignore[arg-type]
        assert "Regex requires a single value" in str(exc_info.value)

    def test_regex_with_string_pattern_succeeds(self) -> None:
        """Test that Regex accepts string patterns."""
        expr = F("name").matches(r"test.*")
        assert expr is not None
        assert expr.value == r"test.*"


class TestInValidation:
    """Test that In accepts multiple values (no validation needed)."""

    def test_in_with_multiple_values(self) -> None:
        """Test that In accepts multiple values."""
        expr = F("x").is_in(1, 2, 3)
        assert expr is not None
        assert expr.values == (1, 2, 3)

    def test_in_with_list_argument(self) -> None:
        """Test that In.is_one_of accepts a list."""
        expr = F("x").is_one_of([1, 2, 3])
        assert expr is not None
        assert expr.values == (1, 2, 3)


class TestIsNullValidation:
    """Test that IsNull has no value validation (works on fields)."""

    def test_is_null_construction(self) -> None:
        """Test that IsNull can be constructed."""
        expr = F("x").is_null()
        assert expr is not None
        assert expr.is_null is True

    def test_is_not_null_construction(self) -> None:
        """Test that is_not_null can be constructed."""
        expr = F("x").is_not_null()
        assert expr is not None
        assert expr.is_null is False


class TestValidationErrorMessages:
    """Test that validation error messages are clear and helpful."""

    def test_error_message_includes_expression_type(self) -> None:
        """Test that error messages include the expression type."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") == [1, 2, 3]
        assert "Eq" in str(exc_info.value)

    def test_error_message_includes_collection_type(self) -> None:
        """Test that error messages include the collection type."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") == [1, 2, 3]
        assert "list" in str(exc_info.value)

    def test_error_message_suggests_in_expression(self) -> None:
        """Test that error messages suggest using In for multiple values."""
        with pytest.raises(TypeError) as exc_info:
            _ = F("x") == [1, 2, 3]
        assert "Use In expression" in str(exc_info.value)


class TestValidationWithDifferentCollectionTypes:
    """Test validation with various collection types."""

    def test_empty_list_raises_error(self) -> None:
        """Test that even empty lists raise errors."""
        with pytest.raises(TypeError):
            _ = F("x") == []

    def test_nested_list_raises_error(self) -> None:
        """Test that nested lists raise errors."""
        with pytest.raises(TypeError):
            _ = F("x") == [[1, 2], [3, 4]]

    def test_frozenset_raises_error(self) -> None:
        """Test that frozensets raise errors."""
        with pytest.raises(TypeError):
            _ = F("x") == frozenset([1, 2, 3])

    def test_none_value_succeeds(self) -> None:
        """Test that None values are accepted."""
        expr = F("x") == None  # noqa: E711
        assert expr is not None
        assert expr.value is None

    def test_dict_value_succeeds(self) -> None:
        """Test that dict values are accepted (not validated)."""
        expr = F("x") == {"key": "value"}
        assert expr is not None
        assert expr.value == {"key": "value"}
