"""Signal Domain Tools for Godot MCP.

This module provides MCP tools for managing signals in Godot scenes,
including connecting signals, disconnecting signals, listing node signals,
and listing signal connections.
"""

import asyncio
from pathlib import Path

from godot_mcp.core import (
    create_error_response,
    extract_json_from_output,
    godot_server,
    mcp,
    validate_path,
)

__all__ = [
    "signal_connect",
    "signal_disconnect",
    "signal_list_node_signals",
    "signal_list_connections",
]


# ============================================================================
# Signal Domain Tools
# ============================================================================


@mcp.tool()
def signal_connect(
    project_path: str,
    scene_path: str,
    source_node_path: str,
    signal_name: str,
    target_node_path: str,
    method_name: str,
) -> str:
    """Connect a signal from one node to a method on another node.

    Category: Signal

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        source_node_path: Path to the source node that emits the signal (e.g., "root/Button")
        signal_name: Name of the signal to connect (e.g., "pressed")
        target_node_path: Path to the target node that receives the signal (e.g., "root/Handler")
        method_name: Name of the method to call on the target node (e.g., "_on_button_pressed")
    """
    if not all(
        [project_path, scene_path, source_node_path, signal_name, target_node_path, method_name]
    ):
        return create_error_response(
            "All parameters are required: project_path, scene_path, source_node_path, signal_name, target_node_path, method_name"
        )

    if not validate_path(project_path) or not validate_path(scene_path):
        return create_error_response("Invalid path")

    try:
        # Check if the project directory exists and contains a project.godot file
        project_file = Path(project_path) / "project.godot"
        if not project_file.exists():
            return create_error_response(
                f"Not a valid Godot project: {project_path}",
                ["Ensure the path points to a directory containing a project.godot file"],
            )

        # Check if the scene file exists
        scene_full_path = Path(project_path) / scene_path
        if not scene_full_path.exists():
            return create_error_response(
                f"Scene file does not exist: {scene_path}",
                [
                    "Ensure the scene path is correct",
                    "Use scene_create to create a new scene first",
                ],
            )

        # Prepare parameters for the operation
        params = {
            "scenePath": scene_path,
            "sourceNodePath": source_node_path,
            "signalName": signal_name,
            "targetNodePath": target_node_path,
            "methodName": method_name,
        }

        # Execute the operation
        stdout, stderr = asyncio.run(
            godot_server.execute_operation("connect_signal", params, project_path)
        )

        if stderr and "Failed to" in stderr:
            return create_error_response(
                f"Failed to connect signal: {stderr}",
                [
                    "Check if the source and target node paths are correct",
                    "Ensure the signal name is valid for the source node type",
                    "Verify the method exists on the target node's script",
                ],
            )

        return f"Signal '{signal_name}' connected successfully from '{source_node_path}' to '{target_node_path}.{method_name}'."
    except Exception as error:
        return create_error_response(
            f"Failed to connect signal: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )


@mcp.tool()
def signal_disconnect(
    project_path: str,
    scene_path: str,
    source_node_path: str,
    signal_name: str,
    target_node_path: str,
    method_name: str,
) -> str:
    """Disconnect a signal connection in a scene.

    Category: Signal

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        source_node_path: Path to the node that emits the signal
        signal_name: Name of the signal to disconnect
        target_node_path: Path to the node that receives the signal
        method_name: Name of the method to disconnect

    Returns:
        Success message or error description
    """
    if not all(
        [project_path, scene_path, source_node_path, signal_name, target_node_path, method_name]
    ):
        return create_error_response("All parameters are required")

    if not validate_path(project_path) or not validate_path(scene_path):
        return create_error_response("Invalid path")

    try:
        project_file = Path(project_path) / "project.godot"
        if not project_file.exists():
            return create_error_response(
                f"Not a valid Godot project: {project_path}",
                ["Ensure the path points to a directory containing a project.godot file"],
            )

        scene_full_path = Path(project_path) / scene_path
        if not scene_full_path.exists():
            return create_error_response(
                f"Scene file does not exist: {scene_path}",
                ["Ensure the scene path is correct"],
            )

        params = {
            "scenePath": scene_path,
            "sourceNodePath": source_node_path,
            "signalName": signal_name,
            "targetNodePath": target_node_path,
            "methodName": method_name,
        }

        stdout, stderr = asyncio.run(
            godot_server.execute_operation("disconnect_signal", params, project_path)
        )

        if stderr and ("Failed to" in stderr or "not found" in stderr.lower()):
            return create_error_response(
                f"Failed to disconnect signal: {stderr}",
                ["Check if the signal connection exists"],
            )

        return f"Signal '{signal_name}' disconnected from '{target_node_path}.{method_name}'."
    except Exception as error:
        return create_error_response(
            f"Failed to disconnect signal: {error}",
            ["Ensure Godot is installed correctly"],
        )


@mcp.tool()
def signal_list_node_signals(
    project_path: str,
    scene_path: str,
    node_path: str,
) -> str:
    """List all signals available on a node.

    Category: Signal

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        node_path: Path to the node (e.g., "root" or "root/Button")

    Returns:
        JSON containing the node's available signals with their parameters
    """
    if not project_path or not scene_path or not node_path:
        return create_error_response("Missing required parameters")

    if not validate_path(project_path) or not validate_path(scene_path):
        return create_error_response("Invalid path")

    try:
        project_file = Path(project_path) / "project.godot"
        if not project_file.exists():
            return create_error_response(
                f"Not a valid Godot project: {project_path}",
                ["Ensure the path points to a directory containing a project.godot file"],
            )

        scene_full_path = Path(project_path) / scene_path
        if not scene_full_path.exists():
            return create_error_response(
                f"Scene file does not exist: {scene_path}",
                ["Ensure the scene path is correct"],
            )

        params = {
            "scenePath": scene_path,
            "nodePath": node_path,
        }

        stdout, stderr = asyncio.run(
            godot_server.execute_operation("list_node_signals", params, project_path)
        )

        if stderr and ("Failed to" in stderr or "not found" in stderr.lower()):
            return create_error_response(
                f"Failed to list node signals: {stderr}",
                ["Check if the node path is correct"],
            )

        return extract_json_from_output(stdout)
    except Exception as error:
        return create_error_response(
            f"Failed to list node signals: {error}",
            ["Ensure Godot is installed correctly"],
        )


@mcp.tool()
def signal_list_connections(
    project_path: str,
    scene_path: str,
    node_path: str | None = None,
) -> str:
    """List all signal connections in a scene or for a specific node.

    Category: Signal

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        node_path: Optional path to a specific node (if None, lists all connections in scene)

    Returns:
        JSON containing signal connections with source, signal name, target, and method
    """
    if not project_path or not scene_path:
        return create_error_response("Project path and scene path are required")

    if not validate_path(project_path) or not validate_path(scene_path):
        return create_error_response("Invalid path")

    try:
        project_file = Path(project_path) / "project.godot"
        if not project_file.exists():
            return create_error_response(
                f"Not a valid Godot project: {project_path}",
                ["Ensure the path points to a directory containing a project.godot file"],
            )

        scene_full_path = Path(project_path) / scene_path
        if not scene_full_path.exists():
            return create_error_response(
                f"Scene file does not exist: {scene_path}",
                ["Ensure the scene path is correct"],
            )

        params = {
            "scenePath": scene_path,
        }
        if node_path:
            params["nodePath"] = node_path

        stdout, stderr = asyncio.run(
            godot_server.execute_operation("list_signal_connections", params, project_path)
        )

        if stderr and ("Failed to" in stderr or "not found" in stderr.lower()):
            return create_error_response(
                f"Failed to list signal connections: {stderr}",
                ["Check if the scene and node paths are correct"],
            )

        return extract_json_from_output(stdout)
    except Exception as error:
        return create_error_response(
            f"Failed to list signal connections: {error}",
            ["Ensure Godot is installed correctly"],
        )
