"""Live/interactive tests for the Dana REPL."""

import builtins
from unittest.mock import patch

import pytest

from dana.apps.repl import DanaREPLApp

# Mark all tests in this file as live tests
pytestmark = [pytest.mark.asyncio, pytest.mark.live]


@pytest.mark.live
async def test_dana_repl_basic():
    """Test basic REPL functionality."""
    # Mock the prompt session to simulate user input
    mock_inputs = ["private:x = 5", "private:y = 10", "private:x + private:y", "exit"]  # Should return 15

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        # Verify the inputs were processed
        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.live
async def test_dana_repl_multiline():
    """Test multiline input handling."""
    mock_inputs = ["if private:x > 0:", "    print('positive')", "else:", "    print('negative')", "exit"]

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.live
async def test_dana_repl_nlp_commands():
    """Test NLP-related commands."""
    mock_inputs = [
        "/nlp status",  # Check initial status
        "/nlp on",  # Enable NLP
        "/nlp status",  # Verify enabled
        "/nlp test",  # Run NLP test
        "/nlp off",  # Disable NLP
        "/nlp status",  # Verify disabled
        "exit",
    ]

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.live
async def test_dana_repl_nlp_transcoding():
    """Test NLP transcoding functionality."""
    mock_inputs = [
        "/nlp on",
        "calculate 10 + 20",  # Should be transcoded to Dana code
        "add 42 and 17",  # Should be transcoded to Dana code
        "exit",
    ]

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.live
async def test_dana_repl_error_handling():
    """Test error handling in the REPL."""
    mock_inputs = [
        "invalid syntax here",  # Should show error
        "private:x = ",  # Incomplete statement
        "if true:",  # Incomplete block
        "    print('test')",  # Complete the block
        "exit",
    ]

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.live
async def test_dana_repl_interrupt_handling():
    """Test handling of keyboard interrupts."""
    mock_inputs = ["private:x = 5", KeyboardInterrupt(), "exit"]  # Simulate Ctrl+C

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.live
async def test_dana_repl_help_command():
    """Test help command functionality."""
    # Mock the prompt session to simulate user input
    mock_inputs = ["help", "exit"]  # Try help command and exit

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        # Verify the inputs were processed
        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.live
async def test_dana_repl_simple_assignment():
    """Test simple variable assignment in REPL."""
    # Mock the prompt session to simulate user input
    mock_inputs = ["private:a = 5", "exit"]  # Simple assignment and exit

    with patch("prompt_toolkit.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()
        await app.run()

        # Verify the inputs were processed
        assert mock_prompt.call_count == len(mock_inputs)


@pytest.mark.asyncio
async def test_dana_repl_promise_command():
    """Test /promise command functionality."""
    mock_inputs = [
        "/promise x = 42",  # Test /promise with assignment
        "x",  # Check that x has the resolved value
        "/promise 10 + 5",  # Test /promise with expression
        "exit",
    ]

    with patch("dana.apps.repl.ui.prompt_session_manager.PromptSession.prompt_async") as mock_prompt:
        mock_prompt.side_effect = mock_inputs

        app = DanaREPLApp()

        # Capture stdout to verify promise display
        captured_output = []
        original_print = builtins.print

        def capture_print(*args, **kwargs):
            captured_output.append(" ".join(str(arg) for arg in args))
            original_print(*args, **kwargs)

        with patch("builtins.print", side_effect=capture_print):
            await app.run()

        # Check that Promise display was shown for /promise commands
        output_text = " ".join(captured_output)
        assert "Promise[T] (pending)" in output_text, f"Expected Promise display in output: {output_text}"


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