"""
Tests for scene query operations: scene_list, scene_get_tree, node_get_properties.

These tests verify the functionality of query tools that allow AI assistants
to understand the structure and content of Godot projects.
"""

import json
from pathlib import Path

from godot_mcp.server import node_get_properties, scene_get_tree, scene_list


class TestListScenes:
    """Test cases for the scene_list tool."""

    def test_scene_list_empty_project(self, temp_godot_project: Path) -> None:
        """Test listing scenes in a project with no scenes."""
        result = scene_list(project_path=str(temp_godot_project))

        # Parse the result
        data = json.loads(result)

        assert "scenes" in data
        assert isinstance(data["scenes"], list)
        assert data["count"] == 0

    def test_scene_list_single_scene(
        self, temp_godot_project: Path, temp_scene_file: Path
    ) -> None:
        """Test listing scenes in a project with one scene."""
        result = scene_list(project_path=str(temp_godot_project))

        data = json.loads(result)

        assert data["count"] == 1
        assert "test_scene.tscn" in data["scenes"]

    def test_scene_list_multiple_scenes(self, temp_godot_project: Path) -> None:
        """Test listing scenes in a project with multiple scenes."""
        # Create multiple scene files
        scene_content = """[gd_scene format=3]

[node name="root" type="Node2D"]
"""
        (temp_godot_project / "scene1.tscn").write_text(scene_content, encoding="utf-8")
        (temp_godot_project / "scene2.tscn").write_text(scene_content, encoding="utf-8")

        # Create a subdirectory with a scene
        scenes_dir = temp_godot_project / "scenes"
        scenes_dir.mkdir()
        (scenes_dir / "scene3.tscn").write_text(scene_content, encoding="utf-8")

        result = scene_list(project_path=str(temp_godot_project))

        data = json.loads(result)

        assert data["count"] == 3
        assert "scene1.tscn" in data["scenes"]
        assert "scene2.tscn" in data["scenes"]
        assert "scenes/scene3.tscn" in data["scenes"]

    def test_scene_list_specific_directory(self, temp_godot_project: Path) -> None:
        """Test listing scenes in a specific subdirectory."""
        scene_content = """[gd_scene format=3]

[node name="root" type="Node2D"]
"""
        # Create scenes in root
        (temp_godot_project / "root_scene.tscn").write_text(scene_content, encoding="utf-8")

        # Create scenes in subdirectory
        scenes_dir = temp_godot_project / "levels"
        scenes_dir.mkdir()
        (scenes_dir / "level1.tscn").write_text(scene_content, encoding="utf-8")
        (scenes_dir / "level2.tscn").write_text(scene_content, encoding="utf-8")

        result = scene_list(
            project_path=str(temp_godot_project),
            directory="levels",
        )

        data = json.loads(result)

        assert data["count"] == 2
        assert "levels/level1.tscn" in data["scenes"]
        assert "levels/level2.tscn" in data["scenes"]
        # root_scene should not be in the results
        assert "root_scene.tscn" not in data["scenes"]

    def test_scene_list_non_recursive(self, temp_godot_project: Path) -> None:
        """Test listing scenes without recursion."""
        scene_content = """[gd_scene format=3]

[node name="root" type="Node2D"]
"""
        # Create scene in root
        (temp_godot_project / "root_scene.tscn").write_text(scene_content, encoding="utf-8")

        # Create scene in subdirectory
        scenes_dir = temp_godot_project / "sub"
        scenes_dir.mkdir()
        (scenes_dir / "sub_scene.tscn").write_text(scene_content, encoding="utf-8")

        result = scene_list(
            project_path=str(temp_godot_project),
            recursive=False,
        )

        data = json.loads(result)

        assert data["count"] == 1
        assert "root_scene.tscn" in data["scenes"]
        # sub_scene should not be in non-recursive results
        assert "sub/sub_scene.tscn" not in data["scenes"]

    def test_scene_list_invalid_project(self) -> None:
        """Test error handling for invalid project path."""
        result = scene_list(project_path="/nonexistent/path")

        assert "Not a valid Godot project" in result


class TestGetSceneTree:
    """Test cases for the scene_get_tree tool."""

    def test_scene_get_tree_basic(self, temp_godot_project: Path, temp_scene_file: Path) -> None:
        """Test getting the node tree of a basic scene."""
        result = scene_get_tree(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
        )

        data = json.loads(result)

        assert data["name"] == "root"
        assert data["type"] == "Node2D"
        assert "path" in data

    def test_scene_get_tree_with_children(self, temp_godot_project: Path) -> None:
        """Test getting the node tree of a scene with child nodes."""
        # Create a scene with child nodes
        scene_content = """[gd_scene format=3]

[node name="root" type="Node2D"]

[node name="Player" type="CharacterBody2D" parent="."]

[node name="Sprite" type="Sprite2D" parent="Player"]

[node name="Camera" type="Camera2D" parent="."]
"""
        scene_path = temp_godot_project / "complex_scene.tscn"
        scene_path.write_text(scene_content, encoding="utf-8")

        result = scene_get_tree(
            project_path=str(temp_godot_project),
            scene_path="complex_scene.tscn",
        )

        data = json.loads(result)

        assert data["name"] == "root"
        assert data["type"] == "Node2D"
        assert "children" in data
        assert len(data["children"]) == 2

        # Find Player node
        player = next((c for c in data["children"] if c["name"] == "Player"), None)
        assert player is not None
        assert player["type"] == "CharacterBody2D"
        assert "children" in player
        assert player["children"][0]["name"] == "Sprite"

    def test_scene_get_tree_max_depth(self, temp_godot_project: Path) -> None:
        """Test getting the node tree with depth limit."""
        # Create a scene with nested nodes
        scene_content = """[gd_scene format=3]

[node name="root" type="Node2D"]

[node name="Level1" type="Node2D" parent="."]

[node name="Level2" type="Node2D" parent="Level1"]

[node name="Level3" type="Node2D" parent="Level1/Level2"]
"""
        scene_path = temp_godot_project / "nested_scene.tscn"
        scene_path.write_text(scene_content, encoding="utf-8")

        result = scene_get_tree(
            project_path=str(temp_godot_project),
            scene_path="nested_scene.tscn",
            max_depth=1,
        )

        data = json.loads(result)

        assert data["name"] == "root"
        assert "children" in data
        # Level1 should be included
        level1 = data["children"][0]
        assert level1["name"] == "Level1"
        # Level1's children should be counted but not detailed
        assert "children_count" in level1 or "children" not in level1

    def test_scene_get_tree_invalid_scene(self, temp_godot_project: Path) -> None:
        """Test error handling for non-existent scene."""
        result = scene_get_tree(
            project_path=str(temp_godot_project),
            scene_path="nonexistent.tscn",
        )

        assert "does not exist" in result

    def test_scene_get_tree_invalid_project(self) -> None:
        """Test error handling for invalid project path."""
        result = scene_get_tree(
            project_path="/nonexistent/path",
            scene_path="test.tscn",
        )

        assert "Not a valid Godot project" in result


class TestGetNodeProperties:
    """Test cases for the node_get_properties tool."""

    def test_node_get_properties_root(
        self, temp_godot_project: Path, temp_scene_file: Path
    ) -> None:
        """Test getting properties of the root node."""
        result = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root",
        )

        data = json.loads(result)

        assert data["node_name"] == "root"
        assert data["node_type"] == "Node2D"
        assert "properties" in data
        assert data["property_count"] > 0

    def test_node_get_properties_child_node(self, temp_godot_project: Path) -> None:
        """Test getting properties of a child node."""
        # Create a scene with a child node
        scene_content = """[gd_scene format=3]

[node name="root" type="Node2D"]

[node name="Sprite" type="Sprite2D" parent="."]
"""
        scene_path = temp_godot_project / "sprite_scene.tscn"
        scene_path.write_text(scene_content, encoding="utf-8")

        result = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path="sprite_scene.tscn",
            node_path="root/Sprite",
        )

        data = json.loads(result)

        assert data["node_name"] == "Sprite"
        assert data["node_type"] == "Sprite2D"
        assert "properties" in data

        # Check for Sprite2D-specific properties
        prop_names = [p["name"] for p in data["properties"]]
        assert "texture" in prop_names or any("texture" in name for name in prop_names)

    def test_node_get_properties_with_script(
        self, temp_godot_project: Path, temp_script_file: Path
    ) -> None:
        """Test getting properties of a node with attached script."""
        # Create a scene with a script attached
        scene_content = f"""[gd_scene load_steps=2 format=3]

[ext_resource type="Script" path="res://{temp_script_file}" id="1"]

[node name="root" type="Node2D"]
script = ExtResource("1")
"""
        scene_path = temp_godot_project / "scripted_scene.tscn"
        scene_path.write_text(scene_content, encoding="utf-8")

        result = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path="scripted_scene.tscn",
            node_path="root",
        )

        data = json.loads(result)

        assert data["node_name"] == "root"
        # Script info should be included
        assert "script" in data
        assert "test_script.gd" in data["script"]

    def test_node_get_properties_include_inherited(
        self, temp_godot_project: Path, temp_scene_file: Path
    ) -> None:
        """Test getting properties including inherited ones."""
        result_basic = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root",
            include_inherited=False,
        )

        result_inherited = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root",
            include_inherited=True,
        )

        data_basic = json.loads(result_basic)
        data_inherited = json.loads(result_inherited)

        # Including inherited properties should return more properties
        assert data_inherited["property_count"] >= data_basic["property_count"]

    def test_node_get_properties_invalid_node(
        self, temp_godot_project: Path, temp_scene_file: Path
    ) -> None:
        """Test error handling for non-existent node."""
        result = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root/NonExistent",
        )

        assert "not found" in result.lower()

    def test_node_get_properties_invalid_scene(self, temp_godot_project: Path) -> None:
        """Test error handling for non-existent scene."""
        result = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path="nonexistent.tscn",
            node_path="root",
        )

        assert "does not exist" in result

    def test_node_get_properties_missing_params(self) -> None:
        """Test error handling for missing required parameters."""
        result = node_get_properties(
            project_path="",
            scene_path="",
            node_path="",
        )

        assert "required" in result.lower()
