"""
Defines the manage_material tool for interacting with Unity materials.
"""
import json
from typing import Annotated, Any, Literal

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 services.tools.utils import parse_json_payload, coerce_int, normalize_properties, normalize_color
from transport.unity_transport import send_with_unity_instance
from transport.legacy.unity_connection import async_send_command_with_retry


@mcp_for_unity_tool(
    description="Manages Unity materials (set properties, colors, shaders, etc). Read-only actions: ping, get_material_info. Modifying actions: create, set_material_shader_property, set_material_color, assign_material_to_renderer, set_renderer_color.",
    annotations=ToolAnnotations(
        title="Manage Material",
        destructiveHint=True,
    ),
)
async def manage_material(
    ctx: Context,
    action: Annotated[Literal[
        "ping",
        "create",
        "set_material_shader_property",
        "set_material_color",
        "assign_material_to_renderer",
        "set_renderer_color",
        "get_material_info"
    ], "Action to perform."],

    # Common / Shared
    material_path: Annotated[str,
                             "Path to material asset (Assets/...)"] | None = None,
    property: Annotated[str,
                        "Shader property name (e.g., _BaseColor, _MainTex)"] | None = None,

    # create
    shader: Annotated[str, "Shader name (default: Standard)"] | None = None,
    properties: Annotated[dict[str, Any] | str,
                          "Initial properties to set as {name: value} dict."] | None = None,

    # set_material_shader_property
    value: Annotated[list | float | int | str | bool | None,
                     "Value to set (color array, float, texture path/instruction)"] | None = None,

    # set_material_color / set_renderer_color
    color: Annotated[list[float] | dict[str, float] | str,
                     "Color as [r, g, b] or [r, g, b, a] array, {r, g, b, a} object, or JSON string."] | None = None,

    # assign_material_to_renderer / set_renderer_color
    target: Annotated[str,
                      "Target GameObject (name, path, or find instruction)"] | None = None,
    search_method: Annotated[Literal["by_id", "by_name", "by_path", "by_tag",
                                     "by_layer", "by_component"], "Search method for target"] | None = None,
    slot: Annotated[int, "Material slot index (0-based)"] | None = None,
    mode: Annotated[Literal["shared", "instance", "property_block", "create_unique"],
                    "Assignment/modification mode; behavior when omitted is action-specific on the Unity side."] | None = None,

) -> dict[str, Any]:
    unity_instance = await get_unity_instance_from_context(ctx)

    # --- Normalize color with validation ---
    color, color_error = normalize_color(color, output_range="float")
    if color_error:
        return {"success": False, "message": color_error}

    # --- Normalize properties with validation ---
    properties, props_error = normalize_properties(properties)
    if props_error:
        return {"success": False, "message": props_error}

    # --- Normalize value (parse JSON if string) ---
    value = parse_json_payload(value)
    if isinstance(value, str) and value in ("[object Object]", "undefined"):
        return {"success": False, "message": f"value received invalid input: '{value}'"}

    # --- Normalize slot to int ---
    slot = coerce_int(slot)

    # Prepare parameters for the C# handler
    params_dict = {
        "action": action.lower(),
        "materialPath": material_path,
        "shader": shader,
        "properties": properties,
        "property": property,
        "value": value,
        "color": color,
        "target": target,
        "searchMethod": search_method,
        "slot": slot,
        "mode": mode
    }

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

    # Use centralized async retry helper with instance routing
    result = await send_with_unity_instance(
        async_send_command_with_retry,
        unity_instance,
        "manage_material",
        params_dict,
    )

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