"""Tests for verify_motion tool."""

from __future__ import annotations

import pytest
from unittest.mock import AsyncMock, patch


class TestVerifyMotion:
    """Tests for the verify_motion bridge function."""

    async def test_returns_moving_when_property_changes(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},  # ping
            {"results": [{"found": True, "properties": {"position": "(1, 0, 0)"}}]},  # snap1
            {"results": [{"found": True, "properties": {"position": "(5, 0, 0)"}}]},  # snap2
        ]
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep"):
                from godotiq.tools.bridge.verify_motion import verify_motion
                result = await verify_motion(node="/root/Main/Car")

        assert result["verdict"] == "MOVING"
        assert result["changed"] is True
        assert result["before"] == "(1, 0, 0)"
        assert result["after"] == "(5, 0, 0)"

    async def test_returns_static_when_property_same(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},
            {"results": [{"found": True, "properties": {"position": "(1, 0, 0)"}}]},
            {"results": [{"found": True, "properties": {"position": "(1, 0, 0)"}}]},
        ]
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep"):
                from godotiq.tools.bridge.verify_motion import verify_motion
                result = await verify_motion(node="/root/Main/Car")

        assert result["verdict"] == "STATIC"
        assert result["changed"] is False

    async def test_uses_correct_property_name(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},
            {"results": [{"found": True, "properties": {"velocity": "(2, 0)"}}]},
            {"results": [{"found": True, "properties": {"velocity": "(3, 0)"}}]},
        ]
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep"):
                from godotiq.tools.bridge.verify_motion import verify_motion
                result = await verify_motion(node="Player", property_name="velocity")

        # Verify state_inspect was called with velocity
        inspect_call = mock_bridge.request.call_args_list[1]
        assert inspect_call[1]["params"]["queries"][0]["properties"] == ["velocity"]
        assert result["property_name"] == "velocity"

    async def test_respects_custom_duration(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},
            {"results": [{"found": True, "properties": {"position": "(0, 0)"}}]},
            {"results": [{"found": True, "properties": {"position": "(1, 0)"}}]},
        ]
        mock_sleep = AsyncMock()
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep", mock_sleep):
                from godotiq.tools.bridge.verify_motion import verify_motion
                result = await verify_motion(node="Player", duration=5.0)

        mock_sleep.assert_awaited_once_with(5.0)
        assert result["duration_seconds"] == 5.0

    async def test_error_when_game_not_running(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {"game_running": False}
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            from godotiq.tools.bridge.verify_motion import verify_motion
            result = await verify_motion(node="Player")

        assert "error" in result
        assert "not running" in result["error"]
        # Only ping was called, no state_inspect
        assert mock_bridge.request.call_count == 1

    async def test_error_when_second_inspect_fails(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},
            {"results": [{"found": True, "properties": {"position": "(1, 0)"}}]},
            {"results": [{"found": False}]},  # game stopped
        ]
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep"):
                from godotiq.tools.bridge.verify_motion import verify_motion
                result = await verify_motion(node="Player")

        assert result["verdict"] == "ERROR"
        assert "stopped" in result["error"]

    async def test_first_inspect_failure_no_sleep(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},
            {"results": [{"found": False}]},  # node not found
        ]
        mock_sleep = AsyncMock()
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep", mock_sleep):
                from godotiq.tools.bridge.verify_motion import verify_motion
                result = await verify_motion(node="MissingNode")

        assert "error" in result
        mock_sleep.assert_not_called()

    async def test_default_property_is_position(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},
            {"results": [{"found": True, "properties": {"position": "(0, 0)"}}]},
            {"results": [{"found": True, "properties": {"position": "(0, 0)"}}]},
        ]
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep"):
                from godotiq.tools.bridge.verify_motion import verify_motion
                await verify_motion(node="Player")

        inspect_call = mock_bridge.request.call_args_list[1]
        assert inspect_call[1]["params"]["queries"][0]["properties"] == ["position"]

    async def test_default_duration_is_two(self) -> None:
        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = [
            {"game_running": True},
            {"results": [{"found": True, "properties": {"position": "(0, 0)"}}]},
            {"results": [{"found": True, "properties": {"position": "(0, 0)"}}]},
        ]
        mock_sleep = AsyncMock()
        with patch("godotiq.tools.bridge.verify_motion.get_bridge", return_value=mock_bridge):
            with patch("godotiq.tools.bridge.verify_motion.asyncio.sleep", mock_sleep):
                from godotiq.tools.bridge.verify_motion import verify_motion
                await verify_motion(node="Player")

        mock_sleep.assert_awaited_once_with(2.0)


class TestVerifyMotionRegistration:
    """Tests for MCP registration."""

    async def test_tool_registered(self) -> None:
        from godotiq.server import mcp
        tools = await mcp.list_tools()
        tool_names = [t.name for t in tools]
        assert "godotiq_verify_motion" in tool_names
