"""
Tool wrapper for managing ScriptableObject assets via Unity MCP.

Unity-side handler: MCPForUnity.Editor.Tools.ManageScriptableObject
Command name: "manage_scriptable_object"
Actions:
  - create: create an SO asset (optionally with patches)
  - modify: apply serialized property patches to an existing SO asset
"""

from __future__ import annotations

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 coerce_bool, parse_json_payload
from transport.unity_transport import send_with_unity_instance
from transport.legacy.unity_connection import async_send_command_with_retry


@mcp_for_unity_tool(
    group="scripting_ext",
    description="Creates and modifies ScriptableObject assets using Unity SerializedObject property paths.",
    annotations=ToolAnnotations(
        title="Manage Scriptable Object",
        destructiveHint=True,
    ),
)
async def manage_scriptable_object(
    ctx: Context,
    action: Annotated[Literal["create", "modify"], "Action to perform: create or modify."],
    # --- create params ---
    type_name: Annotated[str | None,
                         "Namespace-qualified ScriptableObject type name (for create)."] = None,
    folder_path: Annotated[str | None,
                           "Target folder under Assets/... (for create)."] = None,
    asset_name: Annotated[str | None,
                          "Asset file name without extension (for create)."] = None,
    overwrite: Annotated[bool | str | None,
                         "If true, overwrite existing asset at same path (for create)."] = None,
    # --- modify params ---
    target: Annotated[dict[str, Any] | str | None,
                      "Target asset reference {guid|path} (for modify)."] = None,
    # --- shared ---
    patches: Annotated[list[dict[str, Any]] | str | None,
                       "Patch list (or JSON string) to apply. "
                       "For object references: use {\"ref\": {\"guid\": \"...\"}} or {\"value\": {\"guid\": \"...\"}}. "
                       "For Sprite sub-assets: include \"spriteName\" in the ref/value object. "
                       "Single-sprite textures auto-resolve from guid/path alone."] = None,
    # --- validation ---
    dry_run: Annotated[bool | str | None,
                       "If true, validate patches without applying (modify only)."] = None,
) -> dict[str, Any]:
    unity_instance = await get_unity_instance_from_context(ctx)

    # Tolerate JSON-string payloads (LLMs sometimes stringify complex objects)
    parsed_target = parse_json_payload(target)
    parsed_patches = parse_json_payload(patches)

    if parsed_target is not None and not isinstance(parsed_target, dict):
        return {"success": False, "message": "manage_scriptable_object: 'target' must be an object {guid|path} (or JSON string of such)."}

    if parsed_patches is not None and not isinstance(parsed_patches, list):
        return {"success": False, "message": "manage_scriptable_object: 'patches' must be a list (or JSON string of a list)."}

    params: dict[str, Any] = {
        "action": action,
        "typeName": type_name,
        "folderPath": folder_path,
        "assetName": asset_name,
        "overwrite": coerce_bool(overwrite, default=None),
        "target": parsed_target,
        "patches": parsed_patches,
        "dryRun": coerce_bool(dry_run, default=None),
    }

    # Remove None values to keep Unity handler simpler
    params = {k: v for k, v in params.items() if v is not None}

    response = await send_with_unity_instance(
        async_send_command_with_retry,
        unity_instance,
        "manage_scriptable_object",
        params,
    )
    await ctx.info(f"Response {response}")
    return response if isinstance(response, dict) else {"success": False, "message": "Unexpected response from Unity."}
