"""Camera Domain Tools for Godot MCP.

This module provides MCP tools for camera-related operations in Godot projects,
including adding cameras, setting properties, and managing camera state.
"""

import asyncio
from pathlib import Path

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

__all__ = [
    "camera_add",
    "camera_set_current",
    "camera_set_properties",
    "camera_get_info",
]


@mcp.tool()
def camera_add(
    project_path: str,
    scene_path: str,
    parent_node_path: str,
    camera_name: str,
    camera_type: str = "Camera2D",
    make_current: bool = True,
) -> str:
    """Add a camera node to a scene.

    Category: Camera

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        parent_node_path: Path to the parent node
        camera_name: Name for the new camera node
        camera_type: Type of camera (Camera2D or Camera3D, default: Camera2D)
        make_current: Whether to make this the current/active camera (default: True)

    Returns:
        Success message or error description
    """
    if not all([project_path, scene_path, parent_node_path, camera_name]):
        return create_error_response("project_path, scene_path, parent_node_path, and camera_name are required")

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

    if camera_type not in ["Camera2D", "Camera3D"]:
        return create_error_response(
            f"Invalid camera type: {camera_type}",
            ["Valid types are: Camera2D, Camera3D"],
        )

    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}")

        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}")

        params = {
            "scenePath": scene_path,
            "parentNodePath": parent_node_path,
            "cameraName": camera_name,
            "cameraType": camera_type,
            "makeCurrent": make_current,
        }

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

        if stderr and ("Failed to" in stderr or "[ERROR]" in stderr):
            return create_error_response(f"Failed to add camera: {stderr}")

        return f"Camera '{camera_name}' ({camera_type}) added to '{parent_node_path}'."

    except Exception as error:
        return create_error_response(f"Failed to add camera: {error}")


@mcp.tool()
def camera_set_current(
    project_path: str,
    scene_path: str,
    camera_path: str,
) -> str:
    """Set a camera as the current/active camera.

    Category: Camera

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        camera_path: Path to the camera node

    Returns:
        Success message or error description
    """
    if not all([project_path, scene_path, camera_path]):
        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}")

        params = {
            "scenePath": scene_path,
            "cameraPath": camera_path,
        }

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

        if stderr and ("Failed to" in stderr or "[ERROR]" in stderr):
            return create_error_response(f"Failed to set current camera: {stderr}")

        return f"Camera '{camera_path}' is now the current camera."

    except Exception as error:
        return create_error_response(f"Failed to set current camera: {error}")


@mcp.tool()
def camera_set_properties(
    project_path: str,
    scene_path: str,
    camera_path: str,
    zoom: str | None = None,
    offset: str | None = None,
    fov: float | None = None,
    near: float | None = None,
    far: float | None = None,
) -> str:
    """Set properties for a camera.

    Category: Camera

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        camera_path: Path to the camera node
        zoom: Zoom level as JSON array [x, y] (Camera2D only)
        offset: Offset as JSON array [x, y] (Camera2D) or [x, y, z] (Camera3D)
        fov: Field of view in degrees (Camera3D only, default: 75)
        near: Near clipping plane distance (Camera3D only, default: 0.05)
        far: Far clipping plane distance (Camera3D only, default: 4000)

    Returns:
        Success message or error description
    """
    if not all([project_path, scene_path, camera_path]):
        return create_error_response("project_path, scene_path, and camera_path are required")

    if all(param is None for param in [zoom, offset, fov, near, far]):
        return create_error_response("At least one property parameter must be specified")

    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}")

        params = {
            "scenePath": scene_path,
            "cameraPath": camera_path,
        }

        if zoom:
            params["zoom"] = zoom
        if offset:
            params["offset"] = offset
        if fov is not None:
            params["fov"] = fov
        if near is not None:
            params["near"] = near
        if far is not None:
            params["far"] = far

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

        if stderr and ("Failed to" in stderr or "[ERROR]" in stderr):
            return create_error_response(f"Failed to set camera properties: {stderr}")

        return f"Camera properties set for '{camera_path}'."

    except Exception as error:
        return create_error_response(f"Failed to set camera properties: {error}")


@mcp.tool()
def camera_get_info(
    project_path: str,
    scene_path: str,
    camera_path: str,
) -> str:
    """Get information about a camera.

    Category: Camera

    Args:
        project_path: Path to the Godot project directory
        scene_path: Path to the scene file (relative to project)
        camera_path: Path to the camera node

    Returns:
        JSON string containing camera information or error description
    """
    if not all([project_path, scene_path, camera_path]):
        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}")

        params = {
            "scenePath": scene_path,
            "cameraPath": camera_path,
        }

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

        if stderr and ("Failed to" in stderr or "[ERROR]" in stderr):
            return create_error_response(f"Failed to get camera info: {stderr}")

        json_output = extract_json_from_output(stdout)
        if json_output:
            return json_output

        return stdout

    except Exception as error:
        return create_error_response(f"Failed to get camera info: {error}")
