"""
Project Domain Tools

Tools for managing Godot projects, settings, and exports.
"""

import asyncio
import json
import subprocess
from pathlib import Path

from godot_mcp.core import create_error_response, godot_server, mcp, validate_path

__all__ = [
    "project_list",
    "project_get_info",
    "project_run",
    "project_stop",
    "project_get_debug_output",
    "project_export",
    "project_list_export_presets",
    "project_setting_set",
    "project_setting_get",
    "project_update_uids",
]


@mcp.tool()
def project_list(directory: str, recursive: bool = False) -> str:
    """List Godot projects in a directory.

    Category: Project

    Args:
        directory: Directory to search for Godot projects
        recursive: Whether to search recursively (default: false)
    """
    if not directory:
        return create_error_response("Directory is required")

    if not validate_path(directory):
        return create_error_response("Invalid directory path")

    try:
        godot_server.log_debug(f"Listing Godot projects in directory: {directory}")
        dir_path = Path(directory)

        if not dir_path.exists():
            return create_error_response(f"Directory does not exist: {directory}")

        projects: list[dict] = []

        # Function to search for projects
        def find_projects(current_dir: Path, is_recursive: bool) -> list[dict]:
            found: list[dict] = []
            try:
                for entry in current_dir.iterdir():
                    if entry.is_dir() and not entry.name.startswith("."):
                        project_file = entry / "project.godot"
                        if project_file.exists():
                            found.append({"path": str(entry), "name": entry.name})

                        if is_recursive:
                            found.extend(find_projects(entry, True))
            except (PermissionError, OSError):
                pass

            return found

        projects = find_projects(dir_path, recursive)

        return json.dumps(projects, indent=2)
    except Exception as error:
        return create_error_response(
            f"Failed to list projects: {error}",
            ["Ensure the directory exists and is accessible", "Check permissions"],
        )


@mcp.tool()
def project_get_info(project_path: str) -> str:
    """Retrieve metadata about a Godot project.

    Category: Project

    Args:
        project_path: Path to the Godot project directory
    """
    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:
        # Ensure godot_path is set
        godot_path = godot_server.detect_godot_path()

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

        godot_server.log_debug(f"Getting project info for: {project_path}")

        # Get Godot version
        stdout, stderr = godot_server.run_subprocess(f'"{godot_path}" --version')

        # Extract project name from project.godot file
        project_name = Path(project_path).name
        try:
            project_file_content = project_file.read_text(encoding="utf-8")
            import re

            match = re.search(r'config/name="([^"]+)"', project_file_content)
            if match and match.group(1):
                project_name = match.group(1)
                godot_server.log_debug(f"Found project name in config: {project_name}")
        except Exception as error:
            godot_server.log_debug(f"Error reading project file: {error}")

        # Count files in project
        structure = {"scenes": 0, "scripts": 0, "assets": 0, "other": 0}

        def count_files(current_dir: Path) -> None:
            try:
                for entry in current_dir.iterdir():
                    if entry.name.startswith("."):
                        continue

                    if entry.is_dir():
                        count_files(entry)
                    elif entry.is_file():
                        ext = entry.suffix.lower()
                        if ext == ".tscn":
                            structure["scenes"] += 1
                        elif ext in [".gd", ".cs"]:
                            structure["scripts"] += 1
                        elif ext in [
                            ".png",
                            ".jpg",
                            ".jpeg",
                            ".webp",
                            ".svg",
                            ".ttf",
                            ".wav",
                            ".mp3",
                            ".ogg",
                        ]:
                            structure["assets"] += 1
                        else:
                            structure["other"] += 1
            except (PermissionError, OSError):
                pass

        count_files(Path(project_path))

        return json.dumps(
            {
                "name": project_name,
                "path": project_path,
                "godotVersion": stdout.strip(),
                "structure": structure,
            },
            indent=2,
        )
    except Exception as error:
        return create_error_response(
            f"Failed to get project info: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )


@mcp.tool()
def project_run(project_path: str, scene: str = None) -> str:
    """Run the Godot project and capture output.

    Category: Project

    Args:
        project_path: Path to the Godot project directory
        scene: Optional specific scene to run
    """
    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:
        # Ensure godot_path is set
        godot_path = godot_server.detect_godot_path()

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

        # Kill any existing process
        if godot_server.active_process:
            godot_server.log_debug("Killing existing Godot process before starting a new one")
            godot_server.active_process.kill()

        cmd = f'"{godot_path}" -d --path "{project_path}"'
        if scene and validate_path(scene):
            godot_server.log_debug(f"Adding scene parameter: {scene}")
            cmd += f' "{scene}"'

        godot_server.log_debug(f"Running Godot project: {project_path}")

        proc = subprocess.Popen(
            cmd,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
        )
        godot_server.active_process = proc

        return "Godot project started in debug mode. Use project_get_debug_output to see output."
    except Exception as error:
        return create_error_response(
            f"Failed to run Godot project: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )


@mcp.tool()
def project_stop() -> str:
    """Stop the currently running Godot project.

    Category: Project
    """
    if not godot_server.active_process:
        return create_error_response(
            "No active Godot process to stop.",
            ["Use project_run to start a Godot project first"],
        )

    godot_server.log_debug("Stopping active Godot process")
    godot_server.active_process.kill()
    godot_server.active_process = None

    return json.dumps({"message": "Godot project stopped"}, indent=2)


@mcp.tool()
def project_get_debug_output() -> str:
    """Get the current debug output and errors.

    Category: Project
    """
    if not godot_server.active_process:
        return create_error_response(
            "No active Godot process.",
            ["Use project_run to start a Godot project first"],
        )

    # For simplicity, return a message about active process
    # In production, you'd want to read from stdout/stderr buffers
    return json.dumps(
        {
            "output": [],
            "errors": [],
            "message": "Debug output is being captured from the active Godot process.",
        },
        indent=2,
    )


@mcp.tool()
def project_export(
    project_path: str,
    preset_name: str,
    output_path: str,
    debug: bool = False,
    export_pack: bool = False,
) -> str:
    """Export a Godot project using the specified export preset.

    Category: Project

    Args:
        project_path: Path to the Godot project directory
        preset_name: Name of the export preset (as defined in export_presets.cfg)
        output_path: Path where the exported file will be saved
        debug: If True, export a debug build (default: False for release)
        export_pack: If True, export only a PCK file (default: False for full export)
    """
    if not project_path or not preset_name or not output_path:
        return create_error_response("Project path, preset name, and output path are required")

    if not validate_path(project_path):
        return create_error_response("Invalid project 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 export_presets.cfg exists
        presets_file = Path(project_path) / "export_presets.cfg"
        if not presets_file.exists():
            return create_error_response(
                f"No export presets found in project: {project_path}",
                [
                    "Create export presets in the Godot editor first",
                    "Or create export_presets.cfg manually",
                ],
            )

        # Ensure godot_path is set
        godot_path = godot_server.detect_godot_path()

        # Determine export flag
        if export_pack:
            export_flag = "--export-pack"
        elif debug:
            export_flag = "--export-debug"
        else:
            export_flag = "--export-release"

        # Build the export command
        import shlex

        cmd = f'"{godot_path}" --headless --path {shlex.quote(project_path)} {export_flag} {shlex.quote(preset_name)} {shlex.quote(output_path)}'

        godot_server.log_debug(f"Export command: {cmd}")

        # Create output directory if it doesn't exist
        output_dir = Path(output_path).parent
        if not output_dir.exists():
            output_dir.mkdir(parents=True, exist_ok=True)

        # Execute the export command
        stdout, stderr = godot_server.run_subprocess(cmd, project_path)

        # Check for errors
        if stderr:
            # Check for common export errors
            if "No export template found" in stderr or "export template" in stderr.lower():
                return create_error_response(
                    f"Export failed: Export templates not installed for preset '{preset_name}'",
                    [
                        "Install export templates from Godot editor: Editor > Manage Export Templates",
                        "Or download templates from https://godotengine.org/download",
                    ],
                )
            if (
                "Invalid preset" in stderr
                or "preset" in stderr.lower()
                and "not found" in stderr.lower()
            ):
                return create_error_response(
                    f"Export failed: Preset '{preset_name}' not found",
                    [
                        "Check export_presets.cfg for available presets",
                        "Use project_list_export_presets to see available presets",
                    ],
                )
            # Generic error
            return create_error_response(
                f"Export failed: {stderr}",
                [
                    "Check if export templates are installed",
                    "Verify the preset name is correct",
                ],
            )

        # Check if output file was created
        if Path(output_path).exists():
            return f"Project exported successfully to: {output_path}"
        else:
            return f"Export command completed. Output: {stdout}\n\nNote: Output file may not exist if export templates are not installed."

    except Exception as error:
        return create_error_response(
            f"Failed to export project: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )


@mcp.tool()
def project_list_export_presets(project_path: str) -> str:
    """List available export presets for a Godot project.

    Category: Project

    Args:
        project_path: Path to the Godot project directory
    """
    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:
        # 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 export_presets.cfg exists
        presets_file = Path(project_path) / "export_presets.cfg"
        if not presets_file.exists():
            return json.dumps({"presets": [], "message": "No export_presets.cfg found"}, indent=2)

        # Parse the export_presets.cfg file
        presets: list[dict] = []
        try:
            content = presets_file.read_text(encoding="utf-8")
            import re

            # Find all preset names
            preset_pattern = r'\[preset\.(\d+)\].*?name="([^"]+)".*?platform="([^"]+)"'
            matches = re.findall(preset_pattern, content, re.DOTALL)

            for idx, name, platform in matches:
                presets.append(
                    {
                        "index": int(idx),
                        "name": name,
                        "platform": platform,
                    }
                )

        except Exception as parse_error:
            godot_server.log_debug(f"Error parsing export_presets.cfg: {parse_error}")

        return json.dumps({"presets": presets}, indent=2)

    except Exception as error:
        return create_error_response(
            f"Failed to list export presets: {error}",
            [
                "Ensure the project path is accessible",
            ],
        )


@mcp.tool()
def project_setting_set(
    project_path: str,
    setting_key: str,
    setting_value: int | float | str | bool,
) -> str:
    """Set a project setting in project.godot.

    Category: Project

    Args:
        project_path: Path to the Godot project directory
        setting_key: The setting key (e.g., "display/window/size/viewport_width")
        setting_value: The value to set (int, float, string, or bool)
    """
    if not project_path or not setting_key:
        return create_error_response("Project path and setting key are required")

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

        # Prepare parameters for the operation
        params = {
            "settingKey": setting_key,
            "settingValue": setting_value,
        }

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

        if stderr and "Failed to" in stderr:
            return create_error_response(
                f"Failed to set project setting: {stderr}",
                [
                    "Check if the setting key is valid",
                    "Ensure you have write permissions",
                ],
            )

        return f"Project setting '{setting_key}' set successfully to '{setting_value}'."
    except Exception as error:
        return create_error_response(
            f"Failed to set project setting: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )


@mcp.tool()
def project_setting_get(
    project_path: str,
    setting_key: str,
) -> str:
    """Get a project setting value from project.godot.

    Category: Project

    Args:
        project_path: Path to the Godot project directory
        setting_key: The setting key (e.g., "display/window/size/viewport_width")
    """
    if not project_path or not setting_key:
        return create_error_response("Project path and setting key are required")

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

        # Prepare parameters for the operation
        params = {
            "settingKey": setting_key,
        }

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

        if stderr and "Failed to" in stderr:
            return create_error_response(
                f"Failed to get project setting: {stderr}",
                [
                    "Check if the setting key is valid",
                ],
            )

        return stdout
    except Exception as error:
        return create_error_response(
            f"Failed to get project setting: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )


@mcp.tool()
def project_update_uids(project_path: str) -> str:
    """Update UID references in a Godot project by resaving resources (for Godot 4.4+).

    Category: Project

    Args:
        project_path: Path to the Godot project directory
    """
    if not project_path:
        return create_error_response("Project path is required")

    if not validate_path(project_path):
        return create_error_response("Invalid project")

    try:
        # Prepare parameters for the operation
        params: dict = {}

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

        if stderr:
            return create_error_response(
                f"Failed to update project UIDs: {stderr}",
                ["Ensure the project is valid"],
            )

        return "Project UIDs updated successfully."
    except Exception as error:
        return create_error_response(
            f"Failed to update project UIDs: {error}",
            [
                "Ensure Godot is installed correctly",
                "Verify the project path is accessible",
            ],
        )
