"""
Tests for scripting and resource operations: script_create, script_list,
script_detach, resource_list.

These tests verify the Phase 2 tools that enable creating and managing
scripts and resources.
"""

import json
from pathlib import Path

from godot_mcp.server import (
    script_attach,
    scene_create,
    script_create,
    script_detach,
    node_get_properties,
    resource_list,
    script_list,
)


class TestCreateScript:
    """Test cases for the script_create tool."""

    def test_script_create_default_template(self, temp_godot_project: Path) -> None:
        """Test creating a script with default template."""
        result = script_create(
            project_path=str(temp_godot_project),
            script_path="scripts/player.gd",
        )

        assert "successfully" in result.lower()

        # Verify file was created
        script_file = temp_godot_project / "scripts" / "player.gd"
        assert script_file.exists()

        content = script_file.read_text(encoding="utf-8")
        assert "extends Node" in content
        assert "_ready" in content

    def test_script_create_custom_extends(self, temp_godot_project: Path) -> None:
        """Test creating a script with custom extends type."""
        result = script_create(
            project_path=str(temp_godot_project),
            script_path="scripts/character.gd",
            extends_type="CharacterBody2D",
        )

        assert "successfully" in result.lower()

        script_file = temp_godot_project / "scripts" / "character.gd"
        content = script_file.read_text(encoding="utf-8")
        assert "extends CharacterBody2D" in content

    def test_script_create_custom_content(self, temp_godot_project: Path) -> None:
        """Test creating a script with custom content."""
        custom_content = """extends Node2D

var speed: float = 100.0

func move(delta: float) -> void:
\tposition.x += speed * delta
"""
        result = script_create(
            project_path=str(temp_godot_project),
            script_path="scripts/mover.gd",
            content=custom_content,
        )

        assert "successfully" in result.lower()

        script_file = temp_godot_project / "scripts" / "mover.gd"
        content = script_file.read_text(encoding="utf-8")
        assert "var speed" in content
        assert "func move" in content

    def test_script_create_adds_extension(self, temp_godot_project: Path) -> None:
        """Test that .gd extension is added automatically."""
        result = script_create(
            project_path=str(temp_godot_project),
            script_path="scripts/test_auto_ext",
        )

        assert "successfully" in result.lower()

        script_file = temp_godot_project / "scripts" / "test_auto_ext.gd"
        assert script_file.exists()

    def test_script_create_invalid_project(self) -> None:
        """Test error handling for invalid project."""
        result = script_create(
            project_path="/nonexistent/path",
            script_path="test.gd",
        )

        assert "not a valid godot project" in result.lower()


class TestListScripts:
    """Test cases for the script_list tool."""

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

        data = json.loads(result)
        assert "scripts" in data
        assert data["count"] == 0

    def test_script_list_with_scripts(self, temp_godot_project: Path) -> None:
        """Test listing scripts after creating some."""
        # Create scripts
        script_create(
            project_path=str(temp_godot_project),
            script_path="player.gd",
        )
        script_create(
            project_path=str(temp_godot_project),
            script_path="scripts/enemy.gd",
        )

        result = script_list(project_path=str(temp_godot_project))

        data = json.loads(result)
        assert data["count"] == 2
        assert "player.gd" in data["scripts"]
        assert "scripts/enemy.gd" in data["scripts"]

    def test_script_list_specific_directory(self, temp_godot_project: Path) -> None:
        """Test listing scripts in a specific directory."""
        # Create scripts in different locations
        script_create(
            project_path=str(temp_godot_project),
            script_path="root_script.gd",
        )
        script_create(
            project_path=str(temp_godot_project),
            script_path="scripts/sub_script.gd",
        )

        result = script_list(
            project_path=str(temp_godot_project),
            directory="scripts",
        )

        data = json.loads(result)
        assert data["count"] == 1
        assert "scripts/sub_script.gd" in data["scripts"]


class TestDetachScript:
    """Test cases for the script_detach tool."""

    def test_script_detach_basic(
        self, temp_godot_project: Path, temp_scene_file: Path, temp_script_file: Path
    ) -> None:
        """Test detaching a script from a node."""
        # First attach a script
        script_attach(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root",
            script_path=str(temp_script_file),
        )

        # Verify script is attached
        props = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root",
        )
        data = json.loads(props)
        assert "script" in data

        # Detach the script
        result = script_detach(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root",
        )

        assert "detached" in result.lower() or "successfully" in result.lower()

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

        assert "failed" in result.lower() or "not found" in result.lower()


class TestListResources:
    """Test cases for the resource_list tool."""

    def test_resource_list_empty_project(self, temp_godot_project: Path) -> None:
        """Test listing resources in an empty project."""
        result = resource_list(project_path=str(temp_godot_project))

        data = json.loads(result)
        assert "images" in data
        assert "audio" in data
        assert "total_count" in data

    def test_resource_list_with_files(self, temp_godot_project: Path) -> None:
        """Test listing resources after creating some files."""
        # Create some dummy resource files
        (temp_godot_project / "assets").mkdir(exist_ok=True)
        (temp_godot_project / "assets" / "player.png").write_bytes(b"\x89PNG")
        (temp_godot_project / "assets" / "music.ogg").write_bytes(b"OggS")
        (temp_godot_project / "assets" / "font.ttf").write_bytes(b"\x00\x01")

        result = resource_list(project_path=str(temp_godot_project))

        data = json.loads(result)
        assert data["total_count"] >= 3
        assert len(data["images"]) >= 1
        assert len(data["audio"]) >= 1
        assert len(data["fonts"]) >= 1

    def test_resource_list_specific_directory(self, temp_godot_project: Path) -> None:
        """Test listing resources in a specific directory."""
        # Create files in different locations
        (temp_godot_project / "root_image.png").write_bytes(b"\x89PNG")
        (temp_godot_project / "sprites").mkdir(exist_ok=True)
        (temp_godot_project / "sprites" / "sprite.png").write_bytes(b"\x89PNG")

        result = resource_list(
            project_path=str(temp_godot_project),
            directory="sprites",
        )

        data = json.loads(result)
        assert data["total_count"] >= 1
        # Should only find sprites/sprite.png, not root_image.png
        image_paths = [p for p in data["images"]]
        assert any("sprites" in p for p in image_paths)


class TestIntegrationWorkflow:
    """Integration tests for scripting and resource workflows."""

    def test_script_lifecycle(self, temp_godot_project: Path) -> None:
        """Test complete script lifecycle: create, attach, list, detach."""
        # Create a scene
        scene_create(
            project_path=str(temp_godot_project),
            scene_path="game.tscn",
            root_node_type="Node2D",
        )

        # Create a script
        script_create(
            project_path=str(temp_godot_project),
            script_path="game_controller.gd",
            extends_type="Node2D",
        )

        # List scripts - should find the script
        scripts_result = script_list(project_path=str(temp_godot_project))
        scripts_data = json.loads(scripts_result)
        assert "game_controller.gd" in scripts_data["scripts"]

        # Attach script to scene
        script_attach(
            project_path=str(temp_godot_project),
            scene_path="game.tscn",
            node_path="root",
            script_path="game_controller.gd",
        )

        # Verify script is attached
        props = node_get_properties(
            project_path=str(temp_godot_project),
            scene_path="game.tscn",
            node_path="root",
        )
        props_data = json.loads(props)
        assert "script" in props_data

        # Detach script
        script_detach(
            project_path=str(temp_godot_project),
            scene_path="game.tscn",
            node_path="root",
        )
