from typing import Annotated, Any, Literal, Optional

from fastmcp import Context
from mcp.types import ToolAnnotations

from services.registry import mcp_for_unity_tool
from services.tools import get_unity_instance_from_context
from transport.unity_transport import send_with_unity_instance
from transport.legacy.unity_connection import async_send_command_with_retry

# All possible actions grouped by component type
PARTICLE_ACTIONS = [
    "particle_create", "particle_get_info", "particle_set_main", "particle_set_emission", "particle_set_shape",
    "particle_set_color_over_lifetime", "particle_set_size_over_lifetime",
    "particle_set_velocity_over_lifetime", "particle_set_noise", "particle_set_renderer",
    "particle_enable_module", "particle_play", "particle_stop", "particle_pause",
    "particle_restart", "particle_clear", "particle_add_burst", "particle_clear_bursts"
]

VFX_ACTIONS = [
    # Asset management
    "vfx_create_asset", "vfx_assign_asset", "vfx_list_templates", "vfx_list_assets",
    # Runtime control
    "vfx_get_info", "vfx_set_float", "vfx_set_int", "vfx_set_bool",
    "vfx_set_vector2", "vfx_set_vector3", "vfx_set_vector4", "vfx_set_color",
    "vfx_set_gradient", "vfx_set_texture", "vfx_set_mesh", "vfx_set_curve",
    "vfx_send_event", "vfx_play", "vfx_stop", "vfx_pause", "vfx_reinit",
    "vfx_set_playback_speed", "vfx_set_seed"
]

LINE_ACTIONS = [
    "line_get_info", "line_set_positions", "line_add_position", "line_set_position",
    "line_set_width", "line_set_color", "line_set_material", "line_set_properties",
    "line_clear", "line_create_line", "line_create_circle", "line_create_arc", "line_create_bezier"
]

TRAIL_ACTIONS = [
    "trail_get_info", "trail_set_time", "trail_set_width", "trail_set_color",
    "trail_set_material", "trail_set_properties", "trail_clear", "trail_emit"
]

ALL_ACTIONS = ["ping"] + PARTICLE_ACTIONS + VFX_ACTIONS + LINE_ACTIONS + TRAIL_ACTIONS


@mcp_for_unity_tool(
    group="vfx",
    description=(
        "Manage Unity VFX components (ParticleSystem, VisualEffect, LineRenderer, TrailRenderer). "
        "Action prefixes: particle_*, vfx_*, line_*, trail_*. "
        "Action-specific parameters go in `properties` (keys match ManageVFX.cs)."
    ),
    annotations=ToolAnnotations(
        title="Manage VFX",
        destructiveHint=True,
    ),
)
async def manage_vfx(
    ctx: Context,
    action: Annotated[str, "Action to perform (prefix: particle_, vfx_, line_, trail_)."],
    target: Annotated[str | None, "Target GameObject (name/path/id)."] = None,
    search_method: Annotated[
        Literal["by_id", "by_name", "by_path", "by_tag", "by_layer"] | None,
        "How to find the target GameObject.",
    ] = None,
    properties: Annotated[
        dict[str, Any] | str | None,
        "Action-specific parameters (dict or JSON string).",
    ] = None,
    component_index: Annotated[
        Optional[int],
        "Zero-based index to select which component when multiple of the same type exist (e.g., multiple ParticleSystems). "
        "If omitted, targets the first instance."
    ] = None,
) -> dict[str, Any]:
    """Unified VFX management tool."""

    # Normalize action to lowercase to match Unity-side behavior
    action_normalized = action.lower()

    # Validate action against known actions using normalized value
    if action_normalized not in ALL_ACTIONS:
        # Provide helpful error with closest matches by prefix
        prefix = action_normalized.split(
            "_")[0] + "_" if "_" in action_normalized else ""
        available_by_prefix = {
            "particle_": PARTICLE_ACTIONS,
            "vfx_": VFX_ACTIONS,
            "line_": LINE_ACTIONS,
            "trail_": TRAIL_ACTIONS,
        }
        suggestions = available_by_prefix.get(prefix, [])
        if suggestions:
            return {
                "success": False,
                "message": f"Unknown action '{action}'. Available {prefix}* actions: {', '.join(suggestions)}",
            }
        else:
            return {
                "success": False,
                "message": (
                    f"Unknown action '{action}'. Use prefixes: "
                    "particle_*, vfx_*, line_*, trail_*. Run with action='ping' to test connection."
                ),
            }

    unity_instance = await get_unity_instance_from_context(ctx)

    params_dict: dict[str, Any] = {"action": action_normalized}
    if properties is not None:
        params_dict["properties"] = properties
    if target is not None:
        params_dict["target"] = target
    if search_method is not None:
        params_dict["searchMethod"] = search_method
    if component_index is not None:
        params_dict["componentIndex"] = component_index

    params_dict = {k: v for k, v in params_dict.items() if v is not None}

    # Send to Unity
    result = await send_with_unity_instance(
        async_send_command_with_retry,
        unity_instance,
        "manage_vfx",
        params_dict,
    )

    return result if isinstance(result, dict) else {"success": False, "message": str(result)}
