"""
Expose orchestration tool context to model runtime from ToolContextSnapshot (TZ FR-8, NFR-1).

Reads current ToolContextSnapshot from SnapshotHolder under mutex and returns tool
descriptors in the shape expected by the framework tool layer. No construction from
proxy payloads; only from ToolContextSnapshot. Model tool context and catalog are
version-aligned when read under the same holder.

Integration: When orchestration discovery is enabled, the framework tool layer (e.g.
api/tools.py or the handler that serves GET /tools or tool list to the model) should
call get_tool_descriptors(holder) with the application's SnapshotHolder instance and
merge the returned list with local tools (e.g. available_tools). The holder is
typically provided by the app after polling has been wired; when holder is None or
get_tool_context() returns None, get_tool_descriptors returns an empty list.

Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional, Sequence

from mcp_proxy_adapter.core.orchestration.snapshot_holder import SnapshotHolder
from mcp_proxy_adapter.core.orchestration.tool_context_snapshot import (
    ToolContextSnapshot,
    ToolDescriptor,
)


def _descriptor_to_dict(descriptor: ToolDescriptor) -> Dict[str, Any]:
    """
    Convert one ToolDescriptor to a dict for the framework tool layer.

    Shape: name, description, parameters (full schema), server_binding,
    plus optional parameter_definitions and metadata for consumers that need them.
    """
    # Unique name for model: server_id:command_name (avoids clash with local tools)
    name = f"{descriptor.server_id}:{descriptor.command_name}"
    return {
        "name": name,
        "description": descriptor.summary
        or f"Remote command {descriptor.command_name} on server {descriptor.server_id}",
        "parameters": dict(descriptor.schema) if descriptor.schema else {},
        "server_binding": {
            "server_id": descriptor.server_id,
            "host": descriptor.host,
            "port": descriptor.port,
        },
        "command_name": descriptor.command_name,
        "parameter_definitions": (
            dict(descriptor.parameter_definitions)
            if descriptor.parameter_definitions
            else {}
        ),
        "metadata": dict(descriptor.metadata) if descriptor.metadata else None,
        "required_parameters": list(descriptor.required_parameters),
    }


def get_tool_descriptors(holder: SnapshotHolder) -> List[Dict[str, Any]]:
    """
    Read current ToolContextSnapshot from holder and return tool descriptors for the model.

    Uses holder's mutex via get_tool_context(); no direct lock in this module.
    When snapshot is None (no orchestration data yet), returns empty list.
    Model-visible tool context and internal catalog snapshot are version-aligned
    when read under the same holder (NFR-1).

    Args:
        holder: Thread-safe SnapshotHolder that stores CatalogSnapshot and
               ToolContextSnapshot; must not be None.

    Returns:
        List of tool descriptor dicts, each with name, description, parameters,
        server_binding, command_name, parameter_definitions, metadata,
        required_parameters. Empty list if no tool context snapshot is set.
    """
    snapshot: Optional[ToolContextSnapshot] = holder.get_tool_context()
    if snapshot is None:
        return []
    tools: Sequence[ToolDescriptor] = snapshot.get_tools()
    return [_descriptor_to_dict(t) for t in tools]
