"""Tests for brynq.mcp_router — Phase 79 MCP Microservices."""

import json
import pytest
from pathlib import Path


class TestDomainRegistry:
    def test_registry_has_domains(self):
        from brynq.mcp_router import DOMAIN_REGISTRY
        assert "core" in DOMAIN_REGISTRY
        assert "knowledge" in DOMAIN_REGISTRY
        assert "swarm" in DOMAIN_REGISTRY
        assert "rag" in DOMAIN_REGISTRY

    def test_list_domains(self):
        from brynq.mcp_router import list_domains
        domains = list_domains()
        assert len(domains) >= 4
        names = [d["domain"] for d in domains]
        assert "core" in names
        assert "knowledge" in names
        assert "swarm" in names
        assert "rag" in names

    def test_domain_tool_counts(self):
        from brynq.mcp_router import list_domains
        domains = {d["domain"]: d for d in list_domains()}
        assert domains["core"]["tools"] >= 1
        assert domains["knowledge"]["tools"] >= 5
        assert domains["swarm"]["tools"] >= 5
        assert domains["rag"]["tools"] >= 3


class TestLoadDomain:
    def test_load_core(self):
        from brynq.mcp_router import _load_domain
        tools, handlers = _load_domain("core")
        assert len(tools) >= 1
        assert len(handlers) >= 1

    def test_load_knowledge(self):
        from brynq.mcp_router import _load_domain
        tools, handlers = _load_domain("knowledge")
        assert len(tools) == 7
        assert len(handlers) == 7
        tool_names = {t.name for t in tools}
        assert "knowledge_store" in tool_names
        assert "knowledge_query" in tool_names

    def test_load_swarm(self):
        from brynq.mcp_router import _load_domain
        tools, handlers = _load_domain("swarm")
        assert len(tools) == 7
        assert len(handlers) == 7
        tool_names = {t.name for t in tools}
        assert "swarm_create" in tool_names
        assert "swarm_transition" in tool_names

    def test_load_rag(self):
        from brynq.mcp_router import _load_domain
        tools, handlers = _load_domain("rag")
        assert len(tools) >= 4
        assert len(handlers) >= 4
        tool_names = {t.name for t in tools}
        assert "rag_retrieve" in tool_names
        assert "rag_query_all" in tool_names

    def test_load_unknown_domain(self):
        from brynq.mcp_router import _load_domain
        with pytest.raises(ValueError, match="Unknown domain"):
            _load_domain("nonexistent")


class TestLoadAllDomains:
    def test_loads_all(self):
        from brynq.mcp_router import load_all_domains
        tools, handlers = load_all_domains()
        assert len(tools) >= 20
        assert len(handlers) >= 20

    def test_no_duplicate_tool_names(self):
        from brynq.mcp_router import load_all_domains
        tools, _ = load_all_domains()
        names = [t.name for t in tools]
        assert len(names) == len(set(names)), f"Duplicate tool names: {[n for n in names if names.count(n) > 1]}"

    def test_handlers_match_tools(self):
        from brynq.mcp_router import load_all_domains
        tools, handlers = load_all_domains()
        tool_names = {t.name for t in tools}
        handler_names = set(handlers.keys())
        # Every handler should have a tool
        assert handler_names <= tool_names, f"Handlers without tools: {handler_names - tool_names}"
        # Every tool should have a handler
        assert tool_names <= handler_names, f"Tools without handlers: {tool_names - handler_names}"


class TestCreateDomainServer:
    def test_creates_server(self):
        from brynq.mcp_router import create_domain_server
        server = create_domain_server("core")
        assert server is not None
        assert server.name == "brynq-core"

    def test_creates_knowledge_server(self):
        from brynq.mcp_router import create_domain_server
        server = create_domain_server("knowledge")
        assert server.name == "brynq-knowledge"

    def test_creates_swarm_server(self):
        from brynq.mcp_router import create_domain_server
        server = create_domain_server("swarm")
        assert server.name == "brynq-swarm"

    def test_unknown_domain_raises(self):
        from brynq.mcp_router import create_domain_server
        with pytest.raises(ValueError):
            create_domain_server("fake_domain")


class TestPluginModules:
    """Verify each plugin module has the correct structure."""

    def test_knowledge_tools_structure(self):
        from brynq.mcp_plugins.knowledge_tools import TOOLS, HANDLERS
        assert isinstance(TOOLS, list)
        assert isinstance(HANDLERS, dict)
        for tool in TOOLS:
            assert tool.name in HANDLERS

    def test_swarm_tools_structure(self):
        from brynq.mcp_plugins.swarm_tools import TOOLS, HANDLERS
        assert isinstance(TOOLS, list)
        assert isinstance(HANDLERS, dict)
        for tool in TOOLS:
            assert tool.name in HANDLERS

    def test_rag_tools_structure(self):
        from brynq.mcp_plugins.rag_tools import TOOLS, HANDLERS
        assert isinstance(TOOLS, list)
        assert isinstance(HANDLERS, dict)
        for tool in TOOLS:
            assert tool.name in HANDLERS

    def test_core_system_structure(self):
        from brynq.mcp_plugins.core_system import TOOLS, HANDLERS
        assert isinstance(TOOLS, list)
        assert isinstance(HANDLERS, dict)
        for tool in TOOLS:
            assert tool.name in HANDLERS


class TestKnowledgeHandlers:
    """Test knowledge MCP handlers end-to-end."""

    @pytest.fixture(autouse=True)
    def _isolate_knowledge(self, tmp_path, monkeypatch):
        kb_dir = tmp_path / "knowledge"
        kb_dir.mkdir()
        monkeypatch.setattr("brynq.knowledge_mcp.KNOWLEDGE_DIR", kb_dir)
        monkeypatch.setattr("brynq.knowledge_mcp.INDEX_PATH", kb_dir / "index.jsonl")

    @pytest.mark.asyncio(loop_scope="function")
    async def test_store_and_query(self):
        from brynq.mcp_plugins.knowledge_tools import HANDLERS
        # Store
        result = await HANDLERS["knowledge_store"]({
            "topic": "test topic",
            "content": "test content about Python",
            "author_id": "test-agent",
            "category": "codebase",
        })
        assert "Stored" in result[0].text

        # Query
        result = await HANDLERS["knowledge_query"]({"query": "Python"})
        data = json.loads(result[0].text)
        assert len(data) >= 1

    @pytest.mark.asyncio(loop_scope="function")
    async def test_stats(self):
        from brynq.mcp_plugins.knowledge_tools import HANDLERS
        result = await HANDLERS["knowledge_stats"]({})
        data = json.loads(result[0].text)
        assert "total_entries" in data


class TestSwarmHandlers:
    """Test swarm MCP handlers end-to-end."""

    @pytest.fixture(autouse=True)
    def _isolate_swarm(self, tmp_path, monkeypatch):
        sessions_dir = tmp_path / "swarm_sessions"
        sessions_dir.mkdir()
        monkeypatch.setattr("brynq.swarm_state_machine.SESSIONS_DIR", sessions_dir)
        monkeypatch.setattr("brynq.swarm_state_machine.HISTORY_PATH", sessions_dir / "history.jsonl")

    @pytest.mark.asyncio(loop_scope="function")
    async def test_create_and_list(self):
        from brynq.mcp_plugins.swarm_tools import HANDLERS
        # Create
        result = await HANDLERS["swarm_create"]({
            "name": "test-swarm",
            "task_description": "Build a feature",
        })
        assert "created" in result[0].text

        # List
        result = await HANDLERS["swarm_list"]({})
        data = json.loads(result[0].text)
        assert len(data) >= 1
        assert data[0]["name"] == "test-swarm"

    @pytest.mark.asyncio(loop_scope="function")
    async def test_stats(self):
        from brynq.mcp_plugins.swarm_tools import HANDLERS
        result = await HANDLERS["swarm_stats"]({})
        data = json.loads(result[0].text)
        assert "total_sessions" in data


class TestRagHandlers:
    """Test RAG MCP handlers end-to-end (source-based API)."""

    @pytest.fixture(autouse=True)
    def _isolate_rag(self, tmp_path, monkeypatch):
        rag_dir = tmp_path / "rag"
        rag_dir.mkdir(parents=True)
        monkeypatch.setattr("brynq.rag_pipeline.RAG_DIR", rag_dir)
        monkeypatch.setattr("brynq.rag_pipeline.SOURCES_PATH", rag_dir / "sources.json")
        broker_dir = tmp_path / "broker"
        (broker_dir / "channels").mkdir(parents=True)
        monkeypatch.setattr("brynq.rag_pipeline.BROKER_DIR", broker_dir)

    @pytest.mark.asyncio(loop_scope="function")
    async def test_handlers_registered(self):
        """Verify all expected handler keys are present in the HANDLERS dict."""
        from brynq.mcp_plugins.rag_tools import HANDLERS
        expected = {"rag_index", "rag_retrieve", "rag_context", "rag_query_all", "rag_stats", "rag_clear"}
        assert expected == set(HANDLERS.keys())

    @pytest.mark.asyncio(loop_scope="function")
    async def test_handlers_are_callable(self):
        from brynq.mcp_plugins.rag_tools import HANDLERS
        for name, handler in HANDLERS.items():
            assert callable(handler), f"Handler {name} is not callable"
