"""Tests for the scene_tree bridge tool."""

from __future__ import annotations

import pytest
from unittest.mock import AsyncMock, patch

from godotiq.tools.bridge.scene_tree import scene_tree

DEFAULT_INCLUDE = ["transform", "script", "groups", "visibility"]


class TestSceneTree:
    """scene_tree parameter validation and bridge delegation."""

    async def test_default_params(self) -> None:
        """Default call forwards depth=3 and default include list to bridge."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {"root": "Root", "tree": []}
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            result = await scene_tree()

        mock_bridge.request.assert_called_once_with(
            "scene_tree",
            params={
                "root": "",
                "depth": 3,
                "filter_type": "",
                "include": DEFAULT_INCLUDE,
            },
        )
        assert result == {"root": "Root", "tree": []}

    async def test_depth_clamped_below_1(self) -> None:
        """Depth values below 1 are clamped to 1."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {}
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            await scene_tree(depth=0)

        assert mock_bridge.request.call_args.kwargs["params"]["depth"] == 1

    async def test_depth_clamped_above_10(self) -> None:
        """Depth values above 10 are clamped to 10."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {}
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            await scene_tree(depth=50)

        assert mock_bridge.request.call_args.kwargs["params"]["depth"] == 10

    async def test_root_param_forwarded(self) -> None:
        """Custom root path is forwarded to the bridge."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {}
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            await scene_tree(root="Level/Enemies")

        assert mock_bridge.request.call_args.kwargs["params"]["root"] == "Level/Enemies"

    async def test_filter_type_forwarded(self) -> None:
        """filter_type string is forwarded to the bridge."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {}
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            await scene_tree(filter_type="MeshInstance3D")

        assert mock_bridge.request.call_args.kwargs["params"]["filter_type"] == "MeshInstance3D"

    async def test_custom_include_list(self) -> None:
        """A caller-supplied include list overrides the default."""
        mock_bridge = AsyncMock()
        mock_bridge.request.return_value = {}
        custom = ["transform", "script"]
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            await scene_tree(include=custom)

        assert mock_bridge.request.call_args.kwargs["params"]["include"] == custom

    async def test_returns_bridge_response(self) -> None:
        """The bridge response dict is returned unmodified."""
        mock_bridge = AsyncMock()
        expected = {
            "root": "Main",
            "scene_path": "res://main.tscn",
            "total_nodes": 42,
            "returned_nodes": 10,
            "tree": [{"n": "Player", "t": "CharacterBody3D"}],
        }
        mock_bridge.request.return_value = expected
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            result = await scene_tree()

        assert result == expected

    async def test_raises_bridge_connection_error(self) -> None:
        """BridgeConnectionError from get_bridge propagates."""
        from godotiq.bridge.connection import BridgeConnectionError

        with patch(
            "godotiq.tools.bridge.scene_tree.get_bridge",
            side_effect=BridgeConnectionError("not connected"),
        ):
            with pytest.raises(BridgeConnectionError):
                await scene_tree()

    async def test_raises_bridge_timeout_error(self) -> None:
        """BridgeTimeoutError from bridge.request propagates."""
        from godotiq.bridge.connection import BridgeTimeoutError

        mock_bridge = AsyncMock()
        mock_bridge.request.side_effect = BridgeTimeoutError("timed out")
        with patch("godotiq.tools.bridge.scene_tree.get_bridge", return_value=mock_bridge):
            with pytest.raises(BridgeTimeoutError):
                await scene_tree()
