"""Tests for input simulation tool."""

import pytest
from unittest.mock import AsyncMock, patch

from godotiq.tools.bridge.input_sim import input_sim


class TestInputSimTool:
    @pytest.mark.asyncio
    async def test_empty_commands(self):
        result = await input_sim(commands=[])
        assert result.get("success") is False or "error" in result

    @pytest.mark.asyncio
    async def test_no_commands(self):
        result = await input_sim()
        assert "error" in result

    @pytest.mark.asyncio
    async def test_action_commands(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={
            "success": True,
            "commands_executed": 2,
            "results": [
                {"type": "action", "actions": ["move_left"], "ok": True},
                {"type": "wait", "ms": 200, "ok": True},
            ],
            "side_effects": [],
        })
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            result = await input_sim(commands=[
                {"actions": ["move_left"], "hold_ms": 500},
                {"wait_ms": 200},
            ])
            assert result["success"] is True
            assert result["commands_executed"] == 2

    @pytest.mark.asyncio
    async def test_tap_command(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={
            "success": True,
            "commands_executed": 1,
            "results": [{"type": "tap", "target": "AcceptButton", "ok": True}],
        })
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            result = await input_sim(commands=[{"tap": "AcceptButton"}])
            assert result["success"] is True

    @pytest.mark.asyncio
    async def test_side_effects_tracking(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={
            "success": True,
            "commands_executed": 1,
            "results": [{"type": "tap", "ok": True}],
            "side_effects": [
                {"node": "EconomyManager", "property": "cash", "from": 1000, "to": 950},
            ],
        })
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            result = await input_sim(
                commands=[{"tap": "BuyButton"}],
                track_side_effects=True,
            )
            assert len(result["side_effects"]) == 1
            assert result["side_effects"][0]["property"] == "cash"

    @pytest.mark.asyncio
    async def test_timeout_value(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={"success": True})
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            await input_sim(commands=[{"actions": ["jump"]}])
            call_kwargs = mock_bridge.request.call_args
            assert call_kwargs.kwargs.get("timeout") == 65.0 or call_kwargs[1].get("timeout") == 65.0

    @pytest.mark.asyncio
    async def test_wait_for_signal(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={
            "success": True,
            "commands_executed": 1,
            "results": [{"type": "tap", "ok": True}],
            "signal_received": True,
            "signal_data": {"received": True, "timed_out": False},
        })
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            result = await input_sim(
                commands=[{"tap": "AcceptButton"}],
                wait_for="signal:OrderManager.order_accepted",
                wait_for_timeout_ms=3000,
            )
            assert result["signal_received"] is True
            call_params = mock_bridge.request.call_args[1]["params"]
            assert call_params["wait_for"] == "signal:OrderManager.order_accepted"
            assert call_params["wait_for_timeout_ms"] == 3000

    @pytest.mark.asyncio
    async def test_key_command(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={
            "success": True,
            "commands_executed": 1,
            "results": [{"type": "key", "key": "space", "hold_ms": 100, "ok": True}],
        })
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            result = await input_sim(commands=[{"key": "space", "hold_ms": 100}])
            assert result["success"] is True

    @pytest.mark.asyncio
    async def test_continue_on_error(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={
            "success": False,
            "commands_executed": 2,
            "results": [
                {"type": "action", "ok": False, "error": "Unknown action"},
                {"type": "wait", "ok": True},
            ],
        })
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            result = await input_sim(
                commands=[{"actions": ["bad_action"]}, {"wait_ms": 100}],
                continue_on_error=True,
            )
            call_params = mock_bridge.request.call_args[1]["params"]
            assert call_params["continue_on_error"] is True

    @pytest.mark.asyncio
    async def test_params_passed_correctly(self):
        mock_bridge = AsyncMock()
        mock_bridge.request = AsyncMock(return_value={"success": True})
        with patch("godotiq.tools.bridge.input_sim.get_bridge", return_value=mock_bridge):
            await input_sim(
                commands=[{"actions": ["jump"]}],
                track_side_effects=True,
                continue_on_error=True,
                wait_for="signal:GM.done",
                wait_for_timeout_ms=8000,
            )
            call_args = mock_bridge.request.call_args
            assert call_args[0][0] == "input"
            params = call_args[1]["params"]
            assert params["track_side_effects"] is True
            assert params["continue_on_error"] is True
            assert params["wait_for"] == "signal:GM.done"
            assert params["wait_for_timeout_ms"] == 8000
