"""Tests for bridge MCP tool implementations with mocked bridge."""

from __future__ import annotations

import pytest
from unittest.mock import AsyncMock, patch

from godotiq.tools.bridge.screenshot import screenshot
from godotiq.tools.bridge.run import run
from godotiq.tools.bridge.perf_snapshot import perf_snapshot
from godotiq.tools.bridge.editor_context import editor_context


class TestScreenshotTool:
    """Screenshot tool parameter validation and bridge delegation."""

    async def test_default_params(self) -> None:
        """Default call uses viewport=game, scale=0.25, format=webp, quality=0.5."""
        mock_bridge = AsyncMock()
        mock_bridge.request_with_retry.return_value = {
            "image": "base64data",
            "format": "webp",
            "width": 960,
            "height": 540,
        }
        with patch("godotiq.tools.bridge.screenshot.get_bridge", return_value=mock_bridge):
            result = await screenshot()

        mock_bridge.request_with_retry.assert_called_once_with(
            "screenshot",
            params={"viewport": "game", "scale": 0.25, "format": "webp", "quality": 0.5},
            max_retries=3,
            retry_delay=0.5,
        )
        assert result["image"] == "base64data"
        assert result["format"] == "webp"
        assert result["width"] == 960
        assert result["height"] == 540

    async def test_invalid_scale_clamped(self) -> None:
        """Negative scale is clamped to 0.25."""
        mock_bridge = AsyncMock()
        mock_bridge.request_with_retry.return_value = {}
        with patch("godotiq.tools.bridge.screenshot.get_bridge", return_value=mock_bridge):
            await screenshot(scale=-1.0)

        assert mock_bridge.request_with_retry.call_args.kwargs["params"]["scale"] == 0.25

    async def test_zero_scale_clamped(self) -> None:
        """Zero scale is clamped to 0.25 (boundary: scale <= 0)."""
        mock_bridge = AsyncMock()
        mock_bridge.request_with_retry.return_value = {}
        with patch("godotiq.tools.bridge.screenshot.get_bridge", return_value=mock_bridge):
            await screenshot(scale=0.0)

        assert mock_bridge.request_with_retry.call_args.kwargs["params"]["scale"] == 0.25

    async def test_invalid_format_fallback(self) -> None:
        """Unknown format falls back to webp."""
        mock_bridge = AsyncMock()
        mock_bridge.request_with_retry.return_value = {}
        with patch("godotiq.tools.bridge.screenshot.get_bridge", return_value=mock_bridge):
            await screenshot(format="bmp")

        assert mock_bridge.request_with_retry.call_args.kwargs["params"]["format"] == "webp"


class TestRunTool:
    """Run tool play/stop actions."""

    async def test_play_main(self) -> None:
        """Play action calls bridge.request('run') with scene and timeout params."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {
            "action": "play", "success": True, "scene": "res://main.tscn", "waited_seconds": 1.0,
        }
        with patch("godotiq.tools.bridge.run.get_bridge", return_value=mock_bridge):
            result = await run(action="play", scene="main")

        mock_bridge.request.assert_called_once_with(
            "run", params={"scene": "main", "timeout": 15.0}, timeout=20.0,
        )
        assert result["action"] == "play"
        assert result["success"] is True

    async def test_stop(self) -> None:
        """Stop action calls bridge.request('stop') with no params."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {}
        with patch("godotiq.tools.bridge.run.get_bridge", return_value=mock_bridge):
            result = await run(action="stop")

        mock_bridge.request.assert_called_once_with("stop")
        assert result == {"action": "stop"}


class TestPerfSnapshotTool:
    """Performance snapshot tool bridge delegation."""

    async def test_basic_call(self) -> None:
        """Calls bridge.request('perf_snapshot') and returns result."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {
            "fps": 60,
            "draw_calls": 120,
            "triangles": 50000,
        }
        with patch("godotiq.tools.bridge.perf_snapshot.get_bridge", return_value=mock_bridge):
            result = await perf_snapshot()

        mock_bridge.request.assert_called_once_with("perf_snapshot")
        assert result["fps"] == 60


class TestEditorContextTool:
    """Editor context tool with connected and disconnected paths."""

    async def test_connected(self) -> None:
        """Connected bridge returns editor state with addon_connected=True."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {
            "editor_version": "4.3",
            "open_scenes": ["res://main.tscn"],
        }
        with patch("godotiq.tools.bridge.editor_context.get_bridge_or_none", return_value=mock_bridge):
            result = await editor_context()

        mock_bridge.request.assert_called_once_with("editor_context")
        assert result["addon_connected"] is True
        assert result["editor_version"] == "4.3"

    async def test_disconnected(self) -> None:
        """No bridge returns fallback with addon_connected=False and note."""
        with patch("godotiq.tools.bridge.editor_context.get_bridge_or_none", return_value=None):
            result = await editor_context()

        assert result["addon_connected"] is False
        assert "note" in result
        assert result["project_path"] == ""
