"""
Tests for Mores decorators.

Prerequisites:
1. Create a .env file in the project root with:
   MORES_API_KEY=<your-api-key>
   MORES_SERVER_URL=http://localhost:8000
   ANTHROPIC_API_KEY=<your-anthropic-key>

2. Start the server:
   uvicorn server.server:app

3. Run tests:
   pytest tests/test_decorators.py

Note: The .env file is automatically loaded by conftest.py
"""

import pytest

from mores import DoubtError, Rejected, Value, doubt, guard, mock


class TestGuardDecorator:
    """Tests for @guard decorator."""

    def test_guard_allows_valid_call(self):
        """Test that guard allows valid function calls."""

        @guard(rules=["Only allow positive numbers"])
        def process_positive(x: int):
            """Process a positive number."""
            return x * 2

        result = process_positive(5)
        assert isinstance(result, Value)
        assert result.data == 10

    def test_guard_rejects_invalid_call(self):
        """Test that guard rejects invalid function calls."""

        @guard(rules=["Only allow positive numbers"])
        def process_positive(x: int):
            """Process a positive number."""
            return x * 2

        result = process_positive(-5)
        assert isinstance(result, Rejected)
        assert (
            "negative" in result.reason.lower() or "positive" in result.reason.lower()
        )

    def test_guard_with_multiple_rules(self):
        """Test guard with multiple validation rules."""

        @guard(rules=["Never allow deletion", "Only allow amounts under 100"])
        def transfer(amount: float, delete: bool = False):
            """Transfer an amount."""
            if delete:
                return "deleted"
            return f"transferred {amount}"

        # Valid call
        result = transfer(50, delete=False)
        assert isinstance(result, Value)
        assert result.data == "transferred 50"

        # Invalid - trying to delete
        result = transfer(50, delete=True)
        assert isinstance(result, Rejected)

        # Invalid - amount too high
        result = transfer(200, delete=False)
        assert isinstance(result, Rejected)


class TestMockDecorator:
    """Tests for @mock decorator."""

    def test_mock_generates_output(self):
        """Test that mock generates plausible output."""

        @mock()
        def calculate_fibonacci(n: int):
            """Calculate the nth Fibonacci number."""
            pass  # Implementation doesn't matter with @mock

        result = calculate_fibonacci(10)
        assert result is not None
        assert len(str(result)) > 0

    def test_mock_with_complex_function(self):
        """Test mock with a more complex function signature."""

        @mock()
        def analyze_sentiment(text: str, language: str = "en"):
            """
            Analyze the sentiment of text.

            Returns: "positive", "negative", or "neutral"
            """
            pass

        result = analyze_sentiment("I love this product!", language="en")
        assert result in ["positive", "negative", "neutral"] or len(result) > 0


class TestDoubtDecorator:
    """Tests for @doubt decorator."""

    def test_doubt_passes_correct_output(self):
        """Test that doubt passes correct function outputs."""

        @doubt()
        def add(a: int, b: int):
            """Add two numbers."""
            return a + b

        # Should not raise - output is correct
        result = add(2, 3)
        assert result == 5

    def test_doubt_catches_incorrect_output(self):
        """Test that doubt catches incorrect function outputs."""

        @doubt()
        def add(a: int, b: int):
            """Add two numbers."""
            return a * b  # Wrong! Multiplying instead of adding

        # Should raise DoubtError
        with pytest.raises(DoubtError) as exc_info:
            add(2, 3)

    def test_doubt_with_complex_logic(self):
        """Test doubt with more complex validation."""

        @doubt()
        def is_prime(n: int):
            """Check if a number is prime."""
            if n < 2:
                return False
            for i in range(2, int(n**0.5) + 1):
                if n % i == 0:
                    return False
            return True

        # Correct outputs should pass
        assert is_prime(7) == True
        assert is_prime(10) == False


class TestDecoratorCombinations:
    """Tests for using multiple decorators together."""

    def test_guard_with_doubt(self):
        """Test combining @guard and @doubt decorators."""

        @doubt()
        @guard(rules=["Only allow positive numbers"])
        def factorial(n: int):
            """Calculate factorial of n."""
            if n == 0 or n == 1:
                return 1
            result = 1
            for i in range(2, n + 1):
                result *= i
            return result

        # Valid input, correct output
        result = factorial(5)
        assert isinstance(result, Value)
        assert result.data == 120

        # Invalid input (negative)
        result = factorial(-5)
        assert isinstance(result, Rejected)


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
