"""Tests for godotiq_build_scene tool -- validation, registration, and config."""

from __future__ import annotations

import inspect

import pytest

from godotiq.tools.bridge.build_scene import validate_build_params


class TestModeExclusivity:
    """Exactly one of grid/line/scatter/nodes must be provided."""

    def test_no_mode_returns_error(self) -> None:
        """Passing zero modes returns an error dict."""
        params, error = validate_build_params(grid=None, line=None, scatter=None, nodes=None)
        assert params is None
        assert error is not None
        assert "error" in error

    def test_multiple_modes_returns_error(self) -> None:
        """Passing grid + scatter simultaneously returns an error."""
        params, error = validate_build_params(
            grid={"rows": 2, "cols": 2},
            line=None,
            scatter={"items": [{"scene": "res://a.tscn"}]},
            nodes=None,
        )
        assert params is None
        assert error is not None
        assert "error" in error


class TestGridValidation:
    """Grid mode parameter validation."""

    def test_valid_grid(self) -> None:
        """Grid with rows=3, cols=3, spacing=2.0 passes; returned dict has 'grid' key."""
        params, error = validate_build_params(
            grid={"rows": 3, "cols": 3, "spacing": 2.0},
            line=None, scatter=None, nodes=None,
        )
        assert error is None
        assert params is not None
        assert "grid" in params

    def test_single_node_grid(self) -> None:
        """Grid 1x1 passes validation (count=1)."""
        params, error = validate_build_params(
            grid={"rows": 1, "cols": 1},
            line=None, scatter=None, nodes=None,
        )
        assert error is None
        assert params is not None

    def test_grid_missing_rows(self) -> None:
        """Grid without 'rows' key returns error."""
        params, error = validate_build_params(
            grid={"cols": 3},
            line=None, scatter=None, nodes=None,
        )
        assert params is None
        assert "error" in error

    def test_grid_zero_cols(self) -> None:
        """Grid with cols=0 returns error."""
        params, error = validate_build_params(
            grid={"rows": 3, "cols": 0},
            line=None, scatter=None, nodes=None,
        )
        assert params is None
        assert "error" in error

    def test_grid_exceeds_node_limit(self) -> None:
        """Grid with rows=17, cols=16 (272 nodes) returns error mentioning 256 limit."""
        params, error = validate_build_params(
            grid={"rows": 17, "cols": 16},
            line=None, scatter=None, nodes=None,
        )
        assert params is None
        assert "256" in error["error"]


class TestLineValidation:
    """Line mode parameter validation."""

    def test_valid_line(self) -> None:
        """Line with 2 points passes validation."""
        params, error = validate_build_params(
            grid=None,
            line={"points": [[0, 0, 0], [10, 0, 0]], "spacing": 2.0},
            scatter=None, nodes=None,
        )
        assert error is None
        assert params is not None
        assert "line" in params

    def test_line_insufficient_points(self) -> None:
        """Line with only 1 point returns error (needs >= 2)."""
        params, error = validate_build_params(
            grid=None,
            line={"points": [[0, 0, 0]]},
            scatter=None, nodes=None,
        )
        assert params is None
        assert "error" in error


class TestScatterValidation:
    """Scatter mode parameter validation."""

    def test_valid_scatter(self) -> None:
        """Scatter with 3 items passes; returned dict has 'scatter' key."""
        params, error = validate_build_params(
            grid=None, line=None,
            scatter={"items": [
                {"scene": "res://a.tscn", "position": [0, 0, 0]},
                {"scene": "res://b.tscn", "position": [1, 0, 0]},
                {"scene": "res://c.tscn", "position": [2, 0, 0]},
            ]},
            nodes=None,
        )
        assert error is None
        assert params is not None
        assert "scatter" in params

    def test_scatter_missing_items(self) -> None:
        """Scatter without 'items' key returns error."""
        params, error = validate_build_params(
            grid=None, line=None,
            scatter={"count": 5},
            nodes=None,
        )
        assert params is None
        assert "error" in error

    def test_scatter_empty_items(self) -> None:
        """Scatter with empty items list returns error."""
        params, error = validate_build_params(
            grid=None, line=None,
            scatter={"items": []},
            nodes=None,
        )
        assert params is None
        assert "error" in error

    def test_scatter_exceeds_node_limit(self) -> None:
        """Scatter with 257 items returns error mentioning 256."""
        params, error = validate_build_params(
            grid=None, line=None,
            scatter={"items": [{"scene": f"res://{i}.tscn"} for i in range(257)]},
            nodes=None,
        )
        assert params is None
        assert "256" in error["error"]


class TestNodesValidation:
    """Nodes mode parameter validation."""

    def test_valid_nodes(self) -> None:
        """Nodes list with 4 items passes validation."""
        params, error = validate_build_params(
            grid=None, line=None, scatter=None,
            nodes=[
                {"type": "MeshInstance3D", "name": "A"},
                {"type": "MeshInstance3D", "name": "B"},
                {"type": "MeshInstance3D", "name": "C"},
                {"type": "MeshInstance3D", "name": "D"},
            ],
        )
        assert error is None
        assert params is not None
        assert "nodes" in params

    def test_nodes_empty_list(self) -> None:
        """Nodes as empty list returns error."""
        params, error = validate_build_params(
            grid=None, line=None, scatter=None, nodes=[],
        )
        assert params is None
        assert "error" in error

    def test_nodes_exceeds_node_limit(self) -> None:
        """Nodes with 257 items returns error mentioning 256."""
        params, error = validate_build_params(
            grid=None, line=None, scatter=None,
            nodes=[{"type": "Node3D", "name": f"N{i}"} for i in range(257)],
        )
        assert params is None
        assert "256" in error["error"]


class TestLineEdgeCases:
    """Additional line validation edge cases."""

    def test_line_exceeds_node_limit(self) -> None:
        """Line with long distance and small spacing exceeds 256 limit."""
        params, error = validate_build_params(
            grid=None,
            line={"points": [[0, 0, 0], [1000, 0, 0]], "spacing": 1.0},
            scatter=None, nodes=None,
        )
        assert params is None
        assert "256" in error["error"]

    def test_line_malformed_point(self) -> None:
        """Line with malformed point data returns error, not exception."""
        params, error = validate_build_params(
            grid=None,
            line={"points": [[], [10, 0, 0]]},
            scatter=None, nodes=None,
        )
        assert params is None
        assert "error" in error


class TestBuildSceneRegistration:
    """Verify godotiq_build_scene is registered as an MCP tool."""

    @pytest.mark.asyncio
    async def test_build_scene_tool_registered(self) -> None:
        """godotiq_build_scene appears in mcp.list_tools()."""
        from godotiq.server import mcp
        tools = await mcp.list_tools()
        tool_names = [t.name for t in tools]
        assert "godotiq_build_scene" in tool_names

    def test_build_scene_signature(self) -> None:
        """Wrapper function has expected parameters."""
        from godotiq.tools.bridge import godotiq_build_scene
        sig = inspect.signature(godotiq_build_scene)
        param_names = list(sig.parameters.keys())
        for expected in ["target_scene", "parent", "detail", "grid", "line", "scatter", "nodes", "ctx"]:
            assert expected in param_names, f"Missing parameter: {expected}"


class TestBuildSceneTimeout:
    """Verify protocol timeout configuration."""

    def test_build_scene_timeout_configured(self) -> None:
        """build_scene has 30s timeout in TOOL_TIMEOUTS."""
        from godotiq.bridge.protocol import TOOL_TIMEOUTS
        assert "build_scene" in TOOL_TIMEOUTS
        assert TOOL_TIMEOUTS["build_scene"] == 30.0


class TestGridConnectivityValidation:
    """Tests for validate_grid_connectivity() function."""

    def test_empty_overrides_no_warnings(self) -> None:
        """Empty overrides dict returns no warnings."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        assert validate_grid_connectivity({}, 1.0) == []

    def test_single_override_no_warnings(self) -> None:
        """Single override tile returns no warnings (nothing to check against)."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        assert validate_grid_connectivity({"0,0": {}}, 1.0) == []

    def test_connected_l_shape_no_warnings(self) -> None:
        """L-shaped connected path returns no warnings."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "1,0": {}, "2,0": {}, "2,1": {}, "2,2": {}}
        assert validate_grid_connectivity(overrides, 2.0) == []

    def test_connected_t_shape_no_warnings(self) -> None:
        """T-shaped connected path returns no warnings."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "0,1": {}, "0,2": {}, "1,1": {}}
        assert validate_grid_connectivity(overrides, 1.0) == []

    def test_straight_horizontal_no_warnings(self) -> None:
        """Straight horizontal line returns no warnings."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "0,1": {}, "0,2": {}, "0,3": {}}
        assert validate_grid_connectivity(overrides, 1.0) == []

    def test_isolated_tile_returns_warning(self) -> None:
        """Tile at (5,5) far from others produces a warning."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "0,1": {}, "5,5": {}}
        warnings = validate_grid_connectivity(overrides, 1.0)
        assert len(warnings) == 1
        assert "5, 5" in warnings[0] or "5,5" in warnings[0]

    def test_two_isolated_tiles_both_warned(self) -> None:
        """Two tiles with no adjacency both get warnings."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "5,5": {}}
        warnings = validate_grid_connectivity(overrides, 1.0)
        assert len(warnings) == 2

    def test_two_connected_clusters_no_warnings(self) -> None:
        """Two separate clusters where each tile has a neighbor produce no warnings."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "0,1": {}, "3,3": {}, "3,4": {}}
        assert validate_grid_connectivity(overrides, 1.0) == []

    def test_custom_position_excluded(self) -> None:
        """Override with 'position' key is excluded from validation."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "0,1": {}, "5,5": {"position": [10, 0, 10]}}
        warnings = validate_grid_connectivity(overrides, 1.0)
        assert len(warnings) == 0

    def test_warning_message_contains_coordinates(self) -> None:
        """Warning message includes the isolated tile's coordinates."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"0,0": {}, "0,1": {}, "7,3": {}}
        warnings = validate_grid_connectivity(overrides, 1.0)
        assert len(warnings) == 1
        assert "7" in warnings[0] and "3" in warnings[0]

    def test_parses_row_col_correctly(self) -> None:
        """Verifies row,col parsing for multi-digit coordinates."""
        from godotiq.tools.bridge.build_scene import validate_grid_connectivity
        overrides = {"10,20": {}, "10,21": {}}
        assert validate_grid_connectivity(overrides, 1.0) == []
