"""
E2E tests for Script operations.

Tests script-related tools using real Godot demo projects:
- script_attach
"""

from pathlib import Path


from godot_mcp.server import node_add, script_attach, scene_create


class TestAttachScript:
    """E2E tests for script_attach tool."""

    def test_script_attach_to_root_node(self, demo_project_copy: Path) -> None:
        """Test attaching a script to the root node of a new scene."""
        # Create a scene
        scene_create(
            project_path=str(demo_project_copy),
            scene_path="e2e_script_test.tscn",
            root_node_type="Node2D",
        )

        # Create a script file
        script_content = """extends Node2D

func _ready():
    print("E2E test script loaded!")

func test_method():
    return 42
"""
        script_path = demo_project_copy / "e2e_test_script.gd"
        script_path.write_text(script_content, encoding="utf-8")

        # Attach the script
        result = script_attach(
            project_path=str(demo_project_copy),
            scene_path="e2e_script_test.tscn",
            node_path="root",
            script_path="e2e_test_script.gd",
        )

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

        # Verify scene contains script reference
        scene_file = demo_project_copy / "e2e_script_test.tscn"
        content = scene_file.read_text(encoding="utf-8")
        assert "e2e_test_script.gd" in content

    def test_script_attach_to_child_node(self, demo_project_copy: Path) -> None:
        """Test attaching a script to a child node."""
        # Create a scene with a child node
        scene_create(
            project_path=str(demo_project_copy),
            scene_path="e2e_child_script.tscn",
            root_node_type="Node2D",
        )
        node_add(
            project_path=str(demo_project_copy),
            scene_path="e2e_child_script.tscn",
            node_type="Sprite2D",
            node_name="Player",
            parent_node_path="root",
        )

        # Create a script for the child
        script_content = """extends Sprite2D

var speed = 200.0

func _process(delta):
    pass
"""
        script_path = demo_project_copy / "e2e_player_script.gd"
        script_path.write_text(script_content, encoding="utf-8")

        # Attach to child node
        result = script_attach(
            project_path=str(demo_project_copy),
            scene_path="e2e_child_script.tscn",
            node_path="root/Player",
            script_path="e2e_player_script.gd",
        )

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

    def test_script_attach_characterbody2d(self, demo_project_copy: Path) -> None:
        """Test attaching a script to a CharacterBody2D node."""
        scene_create(
            project_path=str(demo_project_copy),
            scene_path="e2e_character.tscn",
            root_node_type="Node2D",
        )
        node_add(
            project_path=str(demo_project_copy),
            scene_path="e2e_character.tscn",
            node_type="CharacterBody2D",
            node_name="Character",
            parent_node_path="root",
        )

        # Create a character controller script
        script_content = """extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -400.0

func _physics_process(delta):
    if not is_on_floor():
        velocity += get_gravity() * delta

    if Input.is_action_just_pressed("ui_accept") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    var direction = Input.get_axis("ui_left", "ui_right")
    if direction:
        velocity.x = direction * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)

    move_and_slide()
"""
        script_path = demo_project_copy / "e2e_character_controller.gd"
        script_path.write_text(script_content, encoding="utf-8")

        result = script_attach(
            project_path=str(demo_project_copy),
            scene_path="e2e_character.tscn",
            node_path="root/Character",
            script_path="e2e_character_controller.gd",
        )

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

    def test_script_attach_nonexistent_scene(self, demo_project_copy: Path) -> None:
        """Test that attaching to non-existent scene fails gracefully."""
        script_path = demo_project_copy / "dummy.gd"
        script_path.write_text("extends Node", encoding="utf-8")

        result = script_attach(
            project_path=str(demo_project_copy),
            scene_path="nonexistent.tscn",
            node_path="root",
            script_path="dummy.gd",
        )

        assert "error" in result.lower() or "not exist" in result.lower()

    def test_script_attach_nonexistent_script(self, demo_project_copy: Path) -> None:
        """Test that attaching non-existent script fails gracefully."""
        scene_create(
            project_path=str(demo_project_copy),
            scene_path="e2e_no_script.tscn",
            root_node_type="Node2D",
        )

        result = script_attach(
            project_path=str(demo_project_copy),
            scene_path="e2e_no_script.tscn",
            node_path="root",
            script_path="nonexistent_script.gd",
        )

        # Should handle error gracefully
        assert isinstance(result, str)


class TestScriptWithDemoAssets:
    """E2E tests using existing demo project assets."""

    def test_script_attach_preserves_scene(self, demo_project_copy: Path) -> None:
        """Test that attaching a script preserves existing scene content."""
        # Create a scene with multiple nodes
        scene_create(
            project_path=str(demo_project_copy),
            scene_path="e2e_preserve_test.tscn",
            root_node_type="Node2D",
        )
        node_add(
            project_path=str(demo_project_copy),
            scene_path="e2e_preserve_test.tscn",
            node_type="Sprite2D",
            node_name="Sprite1",
            parent_node_path="root",
        )
        node_add(
            project_path=str(demo_project_copy),
            scene_path="e2e_preserve_test.tscn",
            node_type="Camera2D",
            node_name="Camera",
            parent_node_path="root",
        )

        # Get scene content before attaching script
        scene_file = demo_project_copy / "e2e_preserve_test.tscn"
        original_content = scene_file.read_text(encoding="utf-8")
        assert "Sprite1" in original_content
        assert "Camera" in original_content

        # Create and attach a script
        script_content = """extends Node2D

func _ready():
    pass
"""
        script_path = demo_project_copy / "e2e_preserve_script.gd"
        script_path.write_text(script_content, encoding="utf-8")

        script_attach(
            project_path=str(demo_project_copy),
            scene_path="e2e_preserve_test.tscn",
            node_path="root",
            script_path="e2e_preserve_script.gd",
        )

        # Verify nodes are still present
        new_content = scene_file.read_text(encoding="utf-8")
        assert "Sprite1" in new_content
        assert "Camera" in new_content
        assert "e2e_preserve_script.gd" in new_content


class TestScriptWorkflow:
    """Integration tests for script-related workflows."""

    def test_complete_player_setup_workflow(self, demo_project_copy: Path) -> None:
        """Test a complete player setup workflow with script."""
        # Step 1: Create player scene
        scene_create(
            project_path=str(demo_project_copy),
            scene_path="player/player.tscn",
            root_node_type="CharacterBody2D",
        )

        # Step 2: Add child nodes
        node_add(
            project_path=str(demo_project_copy),
            scene_path="player/player.tscn",
            node_type="Sprite2D",
            node_name="Sprite",
            parent_node_path="root",
        )
        node_add(
            project_path=str(demo_project_copy),
            scene_path="player/player.tscn",
            node_type="CollisionShape2D",
            node_name="Collision",
            parent_node_path="root",
        )
        node_add(
            project_path=str(demo_project_copy),
            scene_path="player/player.tscn",
            node_type="AnimationPlayer",
            node_name="AnimationPlayer",
            parent_node_path="root",
        )

        # Step 3: Create player script
        player_script = """extends CharacterBody2D

@export var speed: float = 200.0
@export var jump_force: float = 400.0

@onready var sprite = $Sprite
@onready var animation_player = $AnimationPlayer

func _physics_process(delta: float) -> void:
    # Add gravity
    if not is_on_floor():
        velocity += get_gravity() * delta

    # Handle jump
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = -jump_force

    # Handle horizontal movement
    var direction := Input.get_axis("move_left", "move_right")
    if direction:
        velocity.x = direction * speed
    else:
        velocity.x = move_toward(velocity.x, 0, speed)

    move_and_slide()
"""
        script_path = demo_project_copy / "player" / "player.gd"
        script_path.parent.mkdir(parents=True, exist_ok=True)
        script_path.write_text(player_script, encoding="utf-8")

        # Step 4: Attach script
        result = script_attach(
            project_path=str(demo_project_copy),
            scene_path="player/player.tscn",
            node_path="root",
            script_path="player/player.gd",
        )

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

        # Verify complete scene
        scene_file = demo_project_copy / "player" / "player.tscn"
        content = scene_file.read_text(encoding="utf-8")
        assert "CharacterBody2D" in content
        assert "Sprite" in content
        assert "player.gd" in content
