"""Tool config update command implementation."""

import json
from typing import Any, Optional, cast

import typer

from cli.commands.flag_utils import require_update_flags
from cli.commands.tool_configs.update.presenter import (
    present_tool_config_update_success,
)
from cli.infrastructure.command import async_command, authenticated_client
from cli.infrastructure.file_content import FileContentError, resolve_content
from alloy_runtime_types.dtos.tool_configs import UpdateToolConfigRequest
from alloy_runtime_sdk.exceptions.errors import NotFoundError


def tool_configs_update_command(
    identifier: str = typer.Argument(
        ...,
        help="Tool config UUID or slug",
    ),
    name: Optional[str] = typer.Option(
        None,
        "-n",
        "--name",
        help="New name (will regenerate slug)",
    ),
    config: Optional[str] = typer.Option(
        None,
        "-c",
        "--config",
        help="New configuration as JSON (or @file.json)",
    ),
    description: Optional[str] = typer.Option(
        None,
        "-d",
        "--description",
        help="New description",
    ),
    clear_description: bool = typer.Option(
        False,
        "--clear-description",
        help="Clear the description",
    ),
) -> None:
    """Update an existing tool config.

    Only specified fields are updated; others are preserved.

    Examples:
        alloy tool-configs update my-config -n "New Name"
        alloy tool-configs update my-config -c '{"top_k": 20}'
        alloy tool-configs update my-config -d "Updated description"
        alloy tool-configs update my-config --clear-description
    """
    # Check at least one update flag provided
    require_update_flags(
        ("--name", name),
        ("--config", config),
        ("--description", description),
        ("--clear-description", clear_description),
    )

    _execute_update(
        identifier=identifier,
        name=name,
        config=config,
        description=description,
        clear_description=clear_description,
    )


@async_command
async def _execute_update(
    identifier: str,
    name: Optional[str],
    config: Optional[str],
    description: Optional[str],
    clear_description: bool,
) -> None:
    """Execute the tool config update operation."""
    # Build update request dict
    request_data: dict[str, Any] = {}

    if name is not None:
        request_data["name"] = name

    if config is not None:
        # Resolve @file reference for config
        try:
            config_content = resolve_content(config)
        except FileContentError as e:
            raise typer.BadParameter(str(e))

        if not config_content:
            raise typer.BadParameter("Config cannot be empty")

        # Parse config as JSON
        try:
            config_dict_raw = json.loads(config_content)
        except json.JSONDecodeError as e:
            raise typer.BadParameter(f"Invalid JSON in config: {e}")

        if not isinstance(config_dict_raw, dict):
            raise typer.BadParameter("Config must be a JSON object")

        request_data["config"] = cast(dict[str, Any], config_dict_raw)

    if description is not None:
        request_data["description"] = description
    elif clear_description:
        request_data["description"] = ""

    request = UpdateToolConfigRequest(**request_data)

    async with authenticated_client() as (_, client):
        try:
            # Try to get the tool config first to verify it exists
            await client.get_tool_config(identifier)
        except NotFoundError:
            raise typer.BadParameter(
                f"Tool config '{identifier}' not found. Use 'alloy tool-configs list' to see available configs."
            )

        response = await client.update_tool_config(identifier, request)

    present_tool_config_update_success(response)
