"""
Build CatalogSnapshot and ToolContextSnapshot from filtered server/command data.

Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com

Purpose: From a filtered active list (servers with commands/schemas), build
parameter graph and required-parameter set per command (FR-4), then construct
CatalogSnapshot and ToolContextSnapshot with the same version (NFR-1, FR-7 step 5).
No I/O, no discovery, no mutex; caller (e.g. polling_loop) passes in-memory data.
"""

from __future__ import annotations

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

from mcp_proxy_adapter.core.orchestration.catalog_snapshot import CatalogSnapshot
from mcp_proxy_adapter.core.orchestration.command_record import CommandRecord
from mcp_proxy_adapter.core.orchestration.parameter_graph import build_parameter_graph
from mcp_proxy_adapter.core.orchestration.server_record import ServerRecord
from mcp_proxy_adapter.core.orchestration.tool_context_snapshot import (
    ToolContextSnapshot,
)


class CommandInput(TypedDict, total=False):
    """Input shape for one command: name and schema required; metadata optional."""

    name: str
    schema: Dict[str, Any]
    metadata: Optional[Dict[str, Any]]


class ServerInput(TypedDict, total=False):
    """Input shape for one server: server_id, host, port, commands."""

    server_id: str
    host: str
    port: int
    commands: Sequence[CommandInput]


def _required_list(
    commands: Sequence[CommandInput],
) -> List[Tuple[str, Dict[str, Any], Optional[Dict[str, Any]]]]:
    """Normalize commands to list of (name, schema, metadata)."""
    out: List[Tuple[str, Dict[str, Any], Optional[Dict[str, Any]]]] = []
    for c in commands:
        name = c.get("name")
        schema = c.get("schema")
        if not name or not isinstance(schema, dict):
            continue
        out.append((name, schema, c.get("metadata")))
    return out


def _build_command_record(
    command_name: str,
    schema: Dict[str, Any],
    metadata: Optional[Dict[str, Any]],
) -> CommandRecord:
    """
    Build CommandRecord with graph-derived required parameters.

    Uses parameter_graph to compute required-parameter set from schema;
    parameter_definitions are taken from schema["properties"].
    """
    _graph, required_names = build_parameter_graph(schema)
    properties = schema.get("properties")
    parameter_definitions = dict(properties) if isinstance(properties, dict) else {}
    summary = None
    if isinstance(metadata, dict):
        summary = metadata.get("summary")
        if not isinstance(summary, str):
            summary = None
    return CommandRecord(
        command_name=command_name,
        summary=summary,
        parameter_definitions=parameter_definitions,
        metadata=metadata,
        schema=schema,
        required_parameters=tuple(required_names),
    )


def build_catalog_and_tool_context(
    filtered_servers: Sequence[ServerInput],
    previous_version: Optional[int] = None,
) -> Tuple[CatalogSnapshot, ToolContextSnapshot]:
    """
    Build CatalogSnapshot and ToolContextSnapshot from filtered server/command data.

    For each command schema, builds parameter graph and required-parameter set
    (FR-4); constructs CommandRecords and ServerRecords; creates CatalogSnapshot
    with a new monotonic version; builds ToolContextSnapshot from that catalog
    (same version). No I/O; all input is in-memory. Caller must pass already
    filtered active list (FR-7 step 5).

    Versioning: If previous_version is provided, new version is previous_version + 1;
    otherwise version is 1. Same version is set on both snapshots (NFR-1).

    Args:
        filtered_servers: List of server entries, each with server_id, host, port,
                          and list of commands with name, schema, and optional metadata.
        previous_version: Optional previous snapshot version for monotonic increment.

    Returns:
        Tuple of (CatalogSnapshot, ToolContextSnapshot) with the same version.
    """
    next_version = 1 if previous_version is None else previous_version + 1
    servers: Dict[str, ServerRecord] = {}
    commands_by_server: Dict[str, Dict[str, CommandRecord]] = {}

    for s in filtered_servers:
        server_id = s.get("server_id")
        host = s.get("host")
        port = s.get("port")
        raw_commands = s.get("commands") or ()
        if not server_id or not isinstance(host, str) or not isinstance(port, int):
            continue
        server = ServerRecord(server_id=server_id, host=host, port=port)
        servers[server_id] = server
        command_records: Dict[str, CommandRecord] = {}
        for cmd_name, cmd_schema, cmd_meta in _required_list(list(raw_commands)):
            command_records[cmd_name] = _build_command_record(
                cmd_name, cmd_schema, cmd_meta
            )
        commands_by_server[server_id] = command_records

    catalog = CatalogSnapshot.create(
        version=next_version,
        servers=servers,
        commands_by_server=commands_by_server,
    )
    tool_context = ToolContextSnapshot.from_catalog(catalog, version=next_version)
    return (catalog, tool_context)
