"""
Tests for signal system operations: signal_list_node_signals, signal_list_connections,
signal_disconnect.

These tests verify the Phase 3 tools that enable full introspection
and management of signal connections.
"""

import json
from pathlib import Path

from godot_mcp.server import (
    node_add,
    script_attach,
    signal_connect,
    scene_create,
    signal_disconnect,
    signal_list_node_signals,
    signal_list_connections,
)


class TestListNodeSignals:
    """Test cases for the signal_list_node_signals tool."""

    def test_list_signals_node2d(self, temp_godot_project: Path, temp_scene_file: Path) -> None:
        """Test listing signals on a Node2D."""
        result = signal_list_node_signals(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            node_path="root",
        )

        data = json.loads(result)

        assert data["node_type"] == "Node2D"
        assert "signals" in data
        assert data["count"] > 0

        # Check for common Node signals
        signal_names = [s["name"] for s in data["signals"]]
        assert "ready" in signal_names
        assert "tree_entered" in signal_names

    def test_list_signals_button(self, temp_godot_project: Path) -> None:
        """Test listing signals on a Button node."""
        # Create scene with a Button
        scene_create(
            project_path=str(temp_godot_project),
            scene_path="button_test.tscn",
            root_node_type="Control",
        )

        node_add(
            project_path=str(temp_godot_project),
            scene_path="button_test.tscn",
            node_type="Button",
            node_name="TestButton",
        )

        result = signal_list_node_signals(
            project_path=str(temp_godot_project),
            scene_path="button_test.tscn",
            node_path="root/TestButton",
        )

        data = json.loads(result)

        assert data["node_type"] == "Button"
        signal_names = [s["name"] for s in data["signals"]]

        # Button should have pressed signal
        assert "pressed" in signal_names
        assert "button_down" in signal_names
        assert "button_up" in signal_names

    def test_list_signals_invalid_node(
        self, temp_godot_project: Path, temp_scene_file: Path
    ) -> None:
        """Test error handling for non-existent node."""
        result = signal_list_node_signals(
            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 TestListSignalConnections:
    """Test cases for the signal_list_connections tool."""

    def test_list_connections_empty(self, temp_godot_project: Path, temp_scene_file: Path) -> None:
        """Test listing connections in a scene with no connections."""
        result = signal_list_connections(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
        )

        data = json.loads(result)

        assert "connections" in data
        assert data["count"] == 0

    def test_list_connections_after_connect(
        self, temp_godot_project: Path, temp_script_file: Path
    ) -> None:
        """Test listing connections after creating one."""
        # Create scene with button
        scene_create(
            project_path=str(temp_godot_project),
            scene_path="conn_test.tscn",
            root_node_type="Control",
        )

        node_add(
            project_path=str(temp_godot_project),
            scene_path="conn_test.tscn",
            node_type="Button",
            node_name="MyButton",
        )

        # Attach script to root to have a method target
        script_attach(
            project_path=str(temp_godot_project),
            scene_path="conn_test.tscn",
            node_path="root",
            script_path=str(temp_script_file),
        )

        # Connect signal
        signal_connect(
            project_path=str(temp_godot_project),
            scene_path="conn_test.tscn",
            source_node_path="root/MyButton",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

        # List connections
        result = signal_list_connections(
            project_path=str(temp_godot_project),
            scene_path="conn_test.tscn",
        )

        data = json.loads(result)

        assert data["count"] >= 1

        # Find our connection
        found = False
        for conn in data["connections"]:
            if conn["signal"] == "pressed" and conn["method"] == "test_method":
                found = True
                break

        assert found, "Expected connection not found"

    def test_list_connections_specific_node(
        self, temp_godot_project: Path, temp_script_file: Path
    ) -> None:
        """Test listing connections for a specific node."""
        # Create scene with multiple buttons
        scene_create(
            project_path=str(temp_godot_project),
            scene_path="multi_conn.tscn",
            root_node_type="Control",
        )

        node_add(
            project_path=str(temp_godot_project),
            scene_path="multi_conn.tscn",
            node_type="Button",
            node_name="Button1",
        )

        node_add(
            project_path=str(temp_godot_project),
            scene_path="multi_conn.tscn",
            node_type="Button",
            node_name="Button2",
        )

        # Attach script
        script_attach(
            project_path=str(temp_godot_project),
            scene_path="multi_conn.tscn",
            node_path="root",
            script_path=str(temp_script_file),
        )

        # Connect both buttons
        signal_connect(
            project_path=str(temp_godot_project),
            scene_path="multi_conn.tscn",
            source_node_path="root/Button1",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

        signal_connect(
            project_path=str(temp_godot_project),
            scene_path="multi_conn.tscn",
            source_node_path="root/Button2",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

        # List connections for Button1 only
        result = signal_list_connections(
            project_path=str(temp_godot_project),
            scene_path="multi_conn.tscn",
            node_path="root/Button1",
        )

        data = json.loads(result)

        # Should only have Button1's connection
        assert data["count"] == 1
        assert "Button1" in data["connections"][0]["source"]


class TestDisconnectSignal:
    """Test cases for the signal_disconnect tool."""

    def test_signal_disconnect_basic(
        self, temp_godot_project: Path, temp_script_file: Path
    ) -> None:
        """Test disconnecting a signal."""
        # Create scene
        scene_create(
            project_path=str(temp_godot_project),
            scene_path="disconnect_test.tscn",
            root_node_type="Control",
        )

        node_add(
            project_path=str(temp_godot_project),
            scene_path="disconnect_test.tscn",
            node_type="Button",
            node_name="DisconnectBtn",
        )

        script_attach(
            project_path=str(temp_godot_project),
            scene_path="disconnect_test.tscn",
            node_path="root",
            script_path=str(temp_script_file),
        )

        # Connect signal
        signal_connect(
            project_path=str(temp_godot_project),
            scene_path="disconnect_test.tscn",
            source_node_path="root/DisconnectBtn",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

        # Verify connection exists
        conn_before = signal_list_connections(
            project_path=str(temp_godot_project),
            scene_path="disconnect_test.tscn",
        )
        data_before = json.loads(conn_before)
        assert data_before["count"] >= 1

        # Disconnect
        result = signal_disconnect(
            project_path=str(temp_godot_project),
            scene_path="disconnect_test.tscn",
            source_node_path="root/DisconnectBtn",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

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

        # Verify connection is gone
        conn_after = signal_list_connections(
            project_path=str(temp_godot_project),
            scene_path="disconnect_test.tscn",
        )
        data_after = json.loads(conn_after)

        # Should have one less connection
        assert data_after["count"] < data_before["count"]

    def test_disconnect_nonexistent_connection(
        self, temp_godot_project: Path, temp_scene_file: Path
    ) -> None:
        """Test error handling for non-existent connection."""
        result = signal_disconnect(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            source_node_path="root",
            signal_name="ready",
            target_node_path="root",
            method_name="nonexistent_method",
        )

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

    def test_disconnect_invalid_source(
        self, temp_godot_project: Path, temp_scene_file: Path
    ) -> None:
        """Test error handling for non-existent source node."""
        result = signal_disconnect(
            project_path=str(temp_godot_project),
            scene_path=str(temp_scene_file),
            source_node_path="root/NonExistent",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

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


class TestSignalWorkflow:
    """Integration tests for signal workflow."""

    def test_complete_signal_workflow(
        self, temp_godot_project: Path, temp_script_file: Path
    ) -> None:
        """Test complete workflow: create, list, connect, list connections, disconnect."""
        # Create scene
        scene_create(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
            root_node_type="Control",
        )

        node_add(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
            node_type="Button",
            node_name="WorkflowBtn",
        )

        # List available signals
        signals = signal_list_node_signals(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
            node_path="root/WorkflowBtn",
        )
        signals_data = json.loads(signals)
        signal_names = [s["name"] for s in signals_data["signals"]]
        assert "pressed" in signal_names

        # Attach script
        script_attach(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
            node_path="root",
            script_path=str(temp_script_file),
        )

        # Connect signal
        signal_connect(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
            source_node_path="root/WorkflowBtn",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

        # Verify connection
        connections = signal_list_connections(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
        )
        conn_data = json.loads(connections)
        assert conn_data["count"] >= 1

        # Disconnect
        signal_disconnect(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
            source_node_path="root/WorkflowBtn",
            signal_name="pressed",
            target_node_path="root",
            method_name="test_method",
        )

        # Verify disconnection
        connections_after = signal_list_connections(
            project_path=str(temp_godot_project),
            scene_path="workflow.tscn",
        )
        conn_after_data = json.loads(connections_after)
        assert conn_after_data["count"] < conn_data["count"]
