"""
Unit tests for orchestration.catalog_snapshot (CatalogSnapshot).

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

import pytest

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.server_record import ServerRecord


def _sample_server(
    server_id: str = "s1", host: str = "localhost", port: int = 8080
) -> ServerRecord:
    return ServerRecord(server_id=server_id, host=host, port=port)


def _sample_command(
    command_name: str = "cmd1",
    required: tuple = ("a",),
) -> CommandRecord:
    return CommandRecord(
        command_name=command_name,
        summary="Summary",
        parameter_definitions={"a": {"type": "string"}},
        metadata=None,
        schema={"type": "object", "properties": {"a": {}}, "required": list(required)},
        required_parameters=required,
    )


class TestCatalogSnapshot:
    """Tests for CatalogSnapshot."""

    def test_create_empty(self):
        """create() with empty mappings yields snapshot with no servers/commands."""
        snap = CatalogSnapshot.create(version=1, servers={}, commands_by_server={})
        assert snap.version == 1
        assert snap.get_servers() == {}
        assert snap.get_server("x") is None
        assert snap.get_commands("x") == {}
        assert snap.get_command("x", "y") is None

    def test_create_with_servers(self):
        """get_servers and get_server return correct data."""
        s1 = _sample_server("s1")
        s2 = _sample_server("s2", "other", 9090)
        servers = {"s1": s1, "s2": s2}
        snap = CatalogSnapshot.create(version=2, servers=servers, commands_by_server={})
        assert snap.version == 2
        got = snap.get_servers()
        assert len(got) == 2
        assert got["s1"] is s1
        assert got["s2"] is s2
        assert snap.get_server("s1") is s1
        assert snap.get_server("s2") is s2
        assert snap.get_server("s3") is None

    def test_create_with_commands(self):
        """get_commands and get_command return correct data."""
        s1 = _sample_server("s1")
        cmd1 = _sample_command("cmd1")
        cmd2 = _sample_command("cmd2", ("b",))
        commands_by_server = {"s1": {"cmd1": cmd1, "cmd2": cmd2}}
        snap = CatalogSnapshot.create(
            version=3,
            servers={"s1": s1},
            commands_by_server=commands_by_server,
        )
        cmds = snap.get_commands("s1")
        assert len(cmds) == 2
        assert cmds["cmd1"] is cmd1
        assert cmds["cmd2"] is cmd2
        assert snap.get_command("s1", "cmd1") is cmd1
        assert snap.get_command("s1", "cmd2") is cmd2
        assert snap.get_command("s1", "cmd3") is None
        assert snap.get_commands("s2") == {}
        assert snap.get_command("s2", "cmd1") is None

    def test_get_commands_returns_empty_for_unknown_server(self):
        """get_commands(unknown_server) returns empty immutable mapping."""
        snap = CatalogSnapshot.create(version=1, servers={}, commands_by_server={})
        empty = snap.get_commands("unknown")
        assert empty == {}
        # Read-only: no assign
        with pytest.raises(TypeError):
            empty["x"] = _sample_command()  # type: ignore

    def test_snapshot_immutable_frozen(self):
        """CatalogSnapshot is frozen (no new attributes or field reassignment)."""
        snap = CatalogSnapshot.create(version=1, servers={}, commands_by_server={})
        with pytest.raises(AttributeError):
            snap.version = 2  # type: ignore
        with pytest.raises(AttributeError):
            snap.foo = 1  # type: ignore

    def test_create_freezes_inputs(self):
        """create() does not allow mutation of stored mappings via returned get_servers()."""
        servers = {"s1": _sample_server("s1")}
        snap = CatalogSnapshot.create(version=1, servers=servers, commands_by_server={})
        got = snap.get_servers()
        with pytest.raises(TypeError):
            got["s2"] = _sample_server("s2")  # type: ignore
        with pytest.raises(TypeError):
            del got["s1"]  # type: ignore
