"""
Unit tests for orchestration.tool_context_snapshot (Step 05).

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

import pytest

from mcp_proxy_adapter.core.orchestration import (
    CatalogSnapshot,
    CommandRecord,
    ServerRecord,
    ToolContextSnapshot,
)


S1_ID = "550e8400-e29b-41d4-a716-446655440001"


def _make_catalog(version: int = 1):
    """Build a minimal CatalogSnapshot with one server and two commands."""
    s1 = ServerRecord(
        server_id=S1_ID,
        host="localhost",
        port=8080,
        base_url="http://localhost:8080",
        status="ok",
    )
    cmd1 = CommandRecord(
        command_name="echo",
        summary="Echo command",
        parameter_definitions={"message": {"type": "string"}},
        metadata={"category": "test"},
        schema={"type": "object", "properties": {"message": {"type": "string"}}},
        required_parameters=(),
    )
    cmd2 = CommandRecord(
        command_name="help",
        summary="Help command",
        parameter_definitions={},
        metadata=None,
        schema={"type": "object", "properties": {}},
        required_parameters=(),
    )
    servers = {S1_ID: s1}
    commands_by_server = {S1_ID: {"echo": cmd1, "help": cmd2}}
    return CatalogSnapshot.create(
        version=version,
        servers=servers,
        commands_by_server=commands_by_server,
    )


class TestToolContextSnapshotFromCatalog:
    """Tests for ToolContextSnapshot.from_catalog."""

    def test_version_taken_from_catalog(self):
        """Version is aligned with catalog snapshot (NFR-1)."""
        catalog = _make_catalog(version=42)
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        assert snapshot.get_version() == 42

    def test_version_override(self):
        """Explicit version overrides catalog version."""
        catalog = _make_catalog(version=1)
        snapshot = ToolContextSnapshot.from_catalog(catalog, version=99)
        assert snapshot.get_version() == 99

    def test_tools_sequence_from_catalog(self):
        """get_tools returns one descriptor per (server, command)."""
        catalog = _make_catalog()
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        tools = snapshot.get_tools()
        assert len(tools) == 2
        names = {t.command_name for t in tools}
        assert names == {"echo", "help"}
        for t in tools:
            assert t.server_id == S1_ID
            assert t.host == "localhost"
            assert t.port == 8080

    def test_get_tool_found(self):
        """get_tool returns descriptor for (server_id, command_name)."""
        catalog = _make_catalog()
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        t = snapshot.get_tool(S1_ID, "echo")
        assert t is not None
        assert t.command_name == "echo"
        assert t.summary == "Echo command"
        assert t.required_parameters == ()

    def test_get_tool_not_found(self):
        """get_tool returns None when command or server missing."""
        catalog = _make_catalog()
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        assert snapshot.get_tool(S1_ID, "unknown") is None
        assert snapshot.get_tool("unknown_server", "echo") is None

    def test_get_tools_by_server(self):
        """get_tools_by_server returns only tools for that server."""
        catalog = _make_catalog()
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        tools = snapshot.get_tools_by_server(S1_ID)
        assert len(tools) == 2
        assert snapshot.get_tools_by_server("other") == ()


class TestToolDescriptorContent:
    """Tests for ToolDescriptor fields built from catalog."""

    def test_descriptor_has_schema_and_params(self):
        """Descriptor carries schema, parameter_definitions, required_parameters."""
        catalog = _make_catalog()
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        t = snapshot.get_tool(S1_ID, "echo")
        assert t is not None
        assert t.schema.get("type") == "object"
        assert "message" in t.parameter_definitions
        assert t.required_parameters == ()
        assert t.metadata == {"category": "test"}

    def test_descriptor_immutable_sequence(self):
        """get_tools returns immutable sequence (tuple)."""
        catalog = _make_catalog()
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        tools = snapshot.get_tools()
        assert isinstance(tools, tuple)
        with pytest.raises((TypeError, AttributeError)):
            tools.append(None)  # type: ignore


class TestToolContextSnapshotEmptyCatalog:
    """Tests with empty or multi-server catalog."""

    def test_empty_catalog_yields_empty_tools(self):
        """Empty catalog produces snapshot with zero tools."""
        catalog = CatalogSnapshot.create(
            version=1,
            servers={},
            commands_by_server={},
        )
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        assert len(snapshot.get_tools()) == 0
        assert snapshot.get_version() == 1

    def test_two_servers_yield_all_commands(self):
        """Multiple servers contribute all their commands to tools."""
        s1 = ServerRecord("id1", "host1", 8001, None, None)
        s2 = ServerRecord("id2", "host2", 8002, None, None)
        cmd = CommandRecord(
            command_name="run",
            summary="Run",
            parameter_definitions={},
            metadata=None,
            schema={"type": "object", "properties": {}},
            required_parameters=(),
        )
        catalog = CatalogSnapshot.create(
            version=2,
            servers={"id1": s1, "id2": s2},
            commands_by_server={
                "id1": {"run": cmd},
                "id2": {"run": cmd},
            },
        )
        snapshot = ToolContextSnapshot.from_catalog(catalog)
        tools = snapshot.get_tools()
        assert len(tools) == 2
        assert {t.server_id for t in tools} == {"id1", "id2"}
        id1_tools = snapshot.get_tools_by_server("id1")
        assert len(id1_tools) == 1
        assert id1_tools[0].server_id == "id1"
