"""
Immutable CatalogSnapshot for orchestration (TZ section 7, NFR-1).

Versioned immutable snapshot of all server/command metadata. Readers observe
either old or new complete snapshot, never a partial update. Stored data
is in-memory only; no lazy fetch.

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

from __future__ import annotations

from dataclasses import dataclass
from types import MappingProxyType
from typing import Mapping, Optional

from mcp_proxy_adapter.core.orchestration.command_record import CommandRecord
from mcp_proxy_adapter.core.orchestration.server_record import ServerRecord


def _freeze_servers(servers: Mapping[str, ServerRecord]) -> Mapping[str, ServerRecord]:
    """Return an immutable view of server_id -> ServerRecord."""
    if isinstance(servers, MappingProxyType):
        return servers
    return MappingProxyType(dict(servers))


def _freeze_commands(
    commands_by_server: Mapping[str, Mapping[str, CommandRecord]],
) -> Mapping[str, Mapping[str, CommandRecord]]:
    """Return an immutable nested view of server_id -> (command_name -> CommandRecord)."""
    if not commands_by_server:
        return MappingProxyType({})
    frozen = {}
    for sid, cmds in commands_by_server.items():
        frozen[sid] = (
            cmds if isinstance(cmds, MappingProxyType) else MappingProxyType(dict(cmds))
        )
    return MappingProxyType(frozen)


@dataclass(frozen=True)
class CatalogSnapshot:
    """
    Versioned immutable snapshot of all server/command metadata (TZ section 7).

    Holds a monotonic version for logging and diagnostics, and read-only
    mappings of servers and commands. No mutable state is exposed; snapshot
    is immutable after construction. NFR-1: readers observe a complete
    consistent snapshot.
    """

    version: int
    """Monotonic version (or logical timestamp+counter) for logging and diagnostics."""

    _servers: Mapping[str, ServerRecord]
    """Immutable mapping server_id -> ServerRecord. Do not mutate."""

    _commands_by_server: Mapping[str, Mapping[str, CommandRecord]]
    """Immutable mapping server_id -> (command_name -> CommandRecord). Do not mutate."""

    def __post_init__(self) -> None:
        """Ensure stored mappings are immutable views."""
        object.__setattr__(self, "_servers", _freeze_servers(self._servers))
        object.__setattr__(
            self, "_commands_by_server", _freeze_commands(self._commands_by_server)
        )

    def get_servers(self) -> Mapping[str, ServerRecord]:
        """
        Return read-only mapping of server_id -> ServerRecord.

        Callers must not modify the returned mapping; it is an immutable view.
        """
        return self._servers

    def get_server(self, server_id: str) -> Optional[ServerRecord]:
        """Return ServerRecord for server_id, or None if not in snapshot."""
        return self._servers.get(server_id)

    def get_commands(self, server_id: str) -> Mapping[str, CommandRecord]:
        """
        Return read-only mapping of command_name -> CommandRecord for server_id.

        Returns an empty immutable mapping if server_id is not in snapshot.
        Callers must not modify the returned mapping.
        """
        return self._commands_by_server.get(server_id, MappingProxyType({}))

    def get_command(self, server_id: str, command_name: str) -> Optional[CommandRecord]:
        """Return CommandRecord for (server_id, command_name), or None if not in snapshot."""
        commands = self._commands_by_server.get(server_id)
        if commands is None:
            return None
        return commands.get(command_name)

    @classmethod
    def create(
        cls,
        version: int,
        servers: Mapping[str, ServerRecord],
        commands_by_server: Mapping[str, Mapping[str, CommandRecord]],
    ) -> CatalogSnapshot:
        """
        Build an immutable CatalogSnapshot from version and mappings.

        Input mappings are copied to immutable views; caller can reuse or
        discard the originals after the call.
        """
        return cls(
            version=version,
            _servers=_freeze_servers(servers),
            _commands_by_server=_freeze_commands(commands_by_server),
        )
