"""Script Domain Tools for Godot MCP.

This module provides MCP tools for creating, listing, attaching, and detaching
GDScript files in Godot projects.

Tools:
    - script_create: Create a new GDScript file
    - script_list: List all GDScript files in a project
    - script_attach: Attach a script to a node in a scene
    - script_detach: Detach a script from a node in a scene
"""

import asyncio
from pathlib import Path

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

__all__ = [
    "script_create",
    "script_list",
    "script_attach",
    "script_detach",
]


@mcp.tool()
def script_create(
    project_path: str,
    script_path: str,
    extends_type: str = "Node",
    content: str = None,
) -> str:
    """Create a new GDScript file.

    Category: Script

    Args:
        project_path: Path to the Godot project directory
        script_path: Path for the new script (relative to project, e.g., "scripts/player.gd")
        extends_type: Base class the script extends (default: "Node")
        content: Optional full script content. If not provided, creates a template.

    Returns:
        Success message or error description
    """
    if not project_path or not script_path:
        return create_error_response("Project path and script path are required")

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

    if not script_path.endswith(".gd"):
        script_path += ".gd"

    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"],
            )

        script_full_path = Path(project_path) / script_path

        # Create parent directories if needed
        script_full_path.parent.mkdir(parents=True, exist_ok=True)

        # Generate content if not provided
        if content is None:
            content = f"""extends {extends_type}


func _ready() -> void:
\tpass


func _process(delta: float) -> void:
\tpass
"""

        # Write the script file
        script_full_path.write_text(content, encoding="utf-8")

        return f"Script created successfully at: {script_path}"
    except Exception as error:
        return create_error_response(
            f"Failed to create script: {error}",
            ["Ensure you have write permissions"],
        )


@mcp.tool()
def script_list(
    project_path: str,
    directory: str = "",
    recursive: bool = True,
) -> str:
    """List all GDScript files (.gd) in a Godot project.

    Category: Script

    Args:
        project_path: Path to the Godot project directory
        directory: Optional subdirectory to search in (relative to project root)
        recursive: Whether to search recursively (default: True)

    Returns:
        JSON array containing script file paths relative to project root
    """
    if not project_path:
        return create_error_response("Project path is required")

    if not validate_path(project_path):
        return create_error_response("Invalid project 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"],
            )

        params = {
            "directory": directory,
            "recursive": recursive,
        }

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

        if stderr and "Failed to" in stderr:
            return create_error_response(
                f"Failed to list scripts: {stderr}",
                ["Ensure the directory exists"],
            )

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


@mcp.tool()
def script_attach(project_path: str, scene_path: str, node_path: str, script_path: str) -> str:
    """Attach a GDScript file to a node in a scene.

    Category: Script

    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/Player")
        script_path: Path to the script file (relative to project)
    """
    if not project_path or not scene_path or not node_path or not script_path:
        return create_error_response(
            "Project path, scene path, node path, and script path are required"
        )

    if (
        not validate_path(project_path)
        or not validate_path(scene_path)
        or not validate_path(script_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",
                ],
            )

        # Check if the script file exists
        script_full_path = Path(project_path) / script_path
        if not script_full_path.exists():
            return create_error_response(
                f"Script file does not exist: {script_path}",
                [
                    "Ensure the script path is correct",
                    "Create the script file first",
                ],
            )

        # Prepare parameters for the operation
        params = {
            "scenePath": scene_path,
            "nodePath": node_path,
            "scriptPath": script_path,
        }

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

        if stderr and "Failed to" in stderr:
            return create_error_response(
                f"Failed to attach script: {stderr}",
                [
                    "Check if the node path is correct",
                    "Ensure the script extends the correct node type",
                ],
            )

        return f"Script '{script_path}' attached successfully to node '{node_path}' in scene '{scene_path}'."
    except Exception as error:
        return create_error_response(
            f"Failed to attach script: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )


@mcp.tool()
def script_detach(
    project_path: str,
    scene_path: str,
    node_path: str,
) -> str:
    """Detach a script from a node in a scene.

    Category: Script

    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/Player")

    Returns:
        Success message or error description
    """
    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("detach_script", params, project_path)
        )

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

        return f"Script detached successfully from node '{node_path}'."
    except Exception as error:
        return create_error_response(
            f"Failed to detach script: {error}",
            ["Ensure Godot is installed correctly"],
        )
