"""Comprehensive MCP Test — 52 tools, all commands.

Tests every MCP tool via MCPServer.call_tool() (DirectTransport path).
Validates: tool instantiation, status command, key operations.

Run:  python -m pytest tests/test_mcp_comprehensive.py -v
"""

import sys
import os
import pytest

# Ensure project root is on path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from cubesquare.mcp.server import MCPServer


# ---------- Fixtures ----------

@pytest.fixture(scope="module")
def server():
    """Single MCPServer instance for all tests."""
    return MCPServer()


# ---------- Phase 1: Tool Discovery (1 test) ----------

class TestToolDiscovery:
    """Verify all 58 tools are registered and listable."""

    def test_list_tools_returns_58(self, server):
        tools = server.list_tools()
        names = [t["name"] for t in tools]
        assert len(names) == 58, f"Expected 58 tools, got {len(names)}: {names}"

    def test_all_tools_have_schema(self, server):
        tools = server.list_tools()
        for t in tools:
            assert "inputSchema" in t, f"Tool {t['name']} missing inputSchema"
            assert "description" in t, f"Tool {t['name']} missing description"
            assert len(t["description"]) > 10, f"Tool {t['name']} description too short"


# ---------- Phase 2: Status Command (52 tools) ----------

ALL_52_TOOLS = [
    "cst_shell", "c_performance_shell", "primitives_shell", "i_logic_shell",
    "circular_shell", "predict_shell", "beehive_shell", "semantic_chain_shell",
    "language_shell", "time_shell", "distance_shell", "mass_shell",
    "gravity_shell", "world_shell", "ecosystem_shell", "data_analysis_shell",
    "learning_shell", "basin_system", "light_basin_shell", "meaning_shell",
    "manifold_shell", "sql_shell", "gene_shell", "unified_field_tools",
    "new_agent_shell", "twelve_shell", "telemetry_shell", "srl_shell",
    "content_shell", "phi_integration", "cst_chat", "ogawa_shell",
    "vision_shell", "copilot_shell", "data_flip_shell", "gizmo_shell",
    "exploration_shell", "thinking_shell", "thinking_autonomous_shell",
    "validation_shell", "adversarial_shell", "persistence_shell",
    "benchmark_shell", "ticket_shell", "epicycle_shell", "basin_dict_shell",
    "condition_shell", "fml_shell", "cypher_shell", "swarm_shell",
    "memory_shell", "training_shell",
]


class TestStatusCommand:
    """Every tool must respond to 'status' without error."""

    @pytest.mark.parametrize("tool_name", ALL_52_TOOLS)
    def test_status(self, server, tool_name):
        result = server.call_tool(tool_name, {"command": "status"})
        assert "error" not in result, f"{tool_name} status failed: {result.get('error')}"
        # data_flip_shell uses execute() which returns raw dict (no 'result' wrapper)
        if tool_name == "data_flip_shell":
            assert isinstance(result, dict), f"{tool_name} returned non-dict"
        else:
            assert "result" in result, f"{tool_name} status returned no result"


# ---------- Phase 3: Core Operations ----------

class TestILogicOperations:
    """Verify I-Logic algebra through MCP."""

    def test_multiply_spiral_spiral(self, server):
        r = server.call_tool("i_logic_shell", {
            "command": "multiply", "state1": "SPIRAL", "state2": "SPIRAL"
        })
        assert "error" not in r
        result_str = str(r.get("result", ""))
        assert "INVERSE" in result_str, f"SPIRAL×SPIRAL should be INVERSE, got {result_str}"

    def test_multiply_inverse_inverse(self, server):
        r = server.call_tool("i_logic_shell", {
            "command": "multiply", "state1": "INVERSE", "state2": "INVERSE"
        })
        assert "error" not in r
        result_str = str(r.get("result", ""))
        assert "DIRECT" in result_str, f"INVERSE×INVERSE should be DIRECT, got {result_str}"

    def test_multiply_direct_identity(self, server):
        r = server.call_tool("i_logic_shell", {
            "command": "multiply", "state1": "DIRECT", "state2": "SPIRAL"
        })
        assert "error" not in r
        result_str = str(r.get("result", ""))
        assert "SPIRAL" in result_str, f"DIRECT×SPIRAL should be SPIRAL, got {result_str}"

    def test_absorbed_annihilates(self, server):
        r = server.call_tool("i_logic_shell", {
            "command": "multiply", "state1": "ABSORBED", "state2": "SPIRAL"
        })
        assert "error" not in r
        result_str = str(r.get("result", ""))
        assert "ABSORBED" in result_str, f"ABSORBED×SPIRAL should be ABSORBED, got {result_str}"

    def test_table(self, server):
        r = server.call_tool("i_logic_shell", {"command": "table"})
        assert "error" not in r

    def test_compose(self, server):
        r = server.call_tool("i_logic_shell", {
            "command": "compose", "states": "SPIRAL,INVERSE,DIRECT"
        })
        assert "error" not in r


class TestPrimitivesOperations:
    """Verify O(1) primitives through MCP."""

    def test_primitives_status(self, server):
        r = server.call_tool("primitives_shell", {"command": "status"})
        assert "error" not in r

    def test_show_morton(self, server):
        r = server.call_tool("primitives_shell", {
            "command": "show", "primitive": "morton"
        })
        assert "error" not in r

    def test_show_basin(self, server):
        r = server.call_tool("primitives_shell", {
            "command": "show", "primitive": "basin"
        })
        assert "error" not in r

    def test_show_srl(self, server):
        r = server.call_tool("primitives_shell", {
            "command": "show", "primitive": "srl"
        })
        assert "error" not in r

    def test_validate(self, server):
        r = server.call_tool("primitives_shell", {"command": "validate"})
        assert "error" not in r


class TestMemoryOperations:
    """Verify CSTMemory write/read roundtrip through MCP."""

    def test_write(self, server):
        r = server.call_tool("memory_shell", {
            "command": "write", "path": "/test/mcp_comprehensive.py"
        })
        assert "error" not in r

    def test_read(self, server):
        r = server.call_tool("memory_shell", {
            "command": "read", "path": "/test/mcp_comprehensive.py"
        })
        assert "error" not in r

    def test_query(self, server):
        r = server.call_tool("memory_shell", {
            "command": "query", "pattern": "test", "limit": 5
        })
        assert "error" not in r


class TestSwarmOperations:
    """Verify SRL_VERBS + LAYER_TERRITORY routing through MCP."""

    def test_swarm_status(self, server):
        r = server.call_tool("swarm_shell", {"command": "status"})
        assert "error" not in r

    def test_dispatch_direct_task(self, server):
        """Navigator handles navigation tasks (DIRECT group)."""
        r = server.call_tool("swarm_shell", {
            "command": "dispatch", "task": "navigate to the code"
        })
        assert "error" not in r

    def test_dispatch_inverse_task(self, server):
        """Healer handles fix/heal tasks (INVERSE group)."""
        r = server.call_tool("swarm_shell", {
            "command": "dispatch", "task": "fix the broken authentication"
        })
        assert "error" not in r

    def test_dispatch_spiral_task(self, server):
        """Locator handles find/explore tasks (SPIRAL group)."""
        r = server.call_tool("swarm_shell", {
            "command": "dispatch", "task": "explore the spatial coordinates"
        })
        assert "error" not in r

    def test_agents_list(self, server):
        r = server.call_tool("swarm_shell", {"command": "agents"})
        assert "error" not in r

    def test_resolve(self, server):
        r = server.call_tool("swarm_shell", {
            "command": "resolve", "state": "INVERSE"
        })
        assert "error" not in r


class TestTrainingOperations:
    """Verify TrainingLoop through MCP."""

    def test_training_status(self, server):
        r = server.call_tool("training_shell", {"command": "status"})
        assert "error" not in r

    def test_resolve(self, server):
        r = server.call_tool("training_shell", {
            "command": "resolve", "state": "SPIRAL"
        })
        assert "error" not in r


class TestSRLOperations:
    """Verify SRL formula t=sqrt(d) through MCP."""

    def test_compute(self, server):
        r = server.call_tool("srl_shell", {
            "command": "compute", "depth": 100
        })
        assert "error" not in r

    def test_inverse(self, server):
        r = server.call_tool("srl_shell", {
            "command": "inverse", "time": 10.0
        })
        assert "error" not in r


class TestTimeOperations:
    """Verify time shell through MCP."""

    def test_get(self, server):
        r = server.call_tool("time_shell", {
            "command": "get", "depth": 25
        })
        assert "error" not in r

    def test_trajectory(self, server):
        r = server.call_tool("time_shell", {
            "command": "trajectory", "depth": 25
        })
        assert "error" not in r


class TestGizmoOperations:
    """Verify GizmoShell 48³ operations through MCP."""

    def test_probe(self, server):
        r = server.call_tool("gizmo_shell", {
            "command": "probe", "x": 24, "y": 24, "z": 24
        })
        assert "error" not in r

    def test_volume(self, server):
        r = server.call_tool("gizmo_shell", {
            "command": "volume", "x": 24, "y": 24, "z": 24
        })
        assert "error" not in r

    def test_validate(self, server):
        r = server.call_tool("gizmo_shell", {
            "command": "validate", "x": 24, "y": 24, "z": 24
        })
        assert "error" not in r

    def test_field(self, server):
        r = server.call_tool("gizmo_shell", {
            "command": "field", "layer": 0, "basin": -1
        })
        assert "error" not in r


class TestExplorationOperations:
    """Verify ExplorationShell L10-L12 operations through MCP."""

    def test_uif_bridge(self, server):
        r = server.call_tool("exploration_shell", {
            "command": "uif_bridge", "basin_a": 0, "basin_b": 65
        })
        assert "error" not in r

    def test_frequency(self, server):
        r = server.call_tool("exploration_shell", {
            "command": "frequency", "basin": 42
        })
        assert "error" not in r

    def test_deploy(self, server):
        r = server.call_tool("exploration_shell", {
            "command": "deploy", "agent_count": 5
        })
        assert "error" not in r


class TestValidationShell:
    """Verify ValidationShell passes all checks."""

    def test_validate(self, server):
        r = server.call_tool("validation_shell", {"command": "validate"})
        assert "error" not in r

    def test_all(self, server):
        r = server.call_tool("validation_shell", {"command": "all"})
        assert "error" not in r


class TestCircularOperations:
    """Verify circular loop detection through MCP."""

    def test_status(self, server):
        r = server.call_tool("circular_shell", {"command": "status"})
        assert "error" not in r


class TestPredictOperations:
    """Verify route prediction through MCP."""

    def test_status(self, server):
        r = server.call_tool("predict_shell", {"command": "status"})
        assert "error" not in r


class TestBeehiveOperations:
    """Verify beehive index through MCP."""

    def test_status(self, server):
        r = server.call_tool("beehive_shell", {"command": "status"})
        assert "error" not in r

    def test_basin(self, server):
        r = server.call_tool("beehive_shell", {
            "command": "basin", "basin_id": 42, "limit": 5
        })
        assert "error" not in r


class TestPhysicsShells:
    """Verify physics shells (distance, mass, gravity, light)."""

    def test_distance_status(self, server):
        r = server.call_tool("distance_shell", {"command": "status"})
        assert "error" not in r

    def test_mass_status(self, server):
        r = server.call_tool("mass_shell", {"command": "status"})
        assert "error" not in r

    def test_gravity_status(self, server):
        r = server.call_tool("gravity_shell", {"command": "status"})
        assert "error" not in r

    def test_light_status(self, server):
        r = server.call_tool("light_basin_shell", {"command": "status"})
        assert "error" not in r


class TestSemanticShells:
    """Verify semantic/language shells."""

    def test_semantic_status(self, server):
        r = server.call_tool("semantic_chain_shell", {"command": "status"})
        assert "error" not in r

    def test_language_status(self, server):
        r = server.call_tool("language_shell", {"command": "status"})
        assert "error" not in r

    def test_content_status(self, server):
        r = server.call_tool("content_shell", {"command": "status"})
        assert "error" not in r

    def test_meaning_status(self, server):
        r = server.call_tool("meaning_shell", {"command": "status"})
        assert "error" not in r


class TestDataShells:
    """Verify data/analysis shells."""

    def test_data_analysis_status(self, server):
        r = server.call_tool("data_analysis_shell", {"command": "status"})
        assert "error" not in r

    def test_ecosystem_status(self, server):
        r = server.call_tool("ecosystem_shell", {"command": "status"})
        assert "error" not in r

    def test_basin_dict_status(self, server):
        r = server.call_tool("basin_dict_shell", {"command": "status"})
        assert "error" not in r

    def test_condition_status(self, server):
        r = server.call_tool("condition_shell", {"command": "status"})
        assert "error" not in r

    def test_fml_status(self, server):
        r = server.call_tool("fml_shell", {"command": "status"})
        assert "error" not in r

    def test_sql_status(self, server):
        r = server.call_tool("sql_shell", {"command": "status"})
        assert "error" not in r


class TestAgentShells:
    """Verify agent-facing shells."""

    def test_agent_status(self, server):
        r = server.call_tool("new_agent_shell", {"command": "status"})
        assert "error" not in r

    def test_gene_status(self, server):
        r = server.call_tool("gene_shell", {"command": "status"})
        assert "error" not in r

    def test_unified_status(self, server):
        r = server.call_tool("unified_field_tools", {"command": "status"})
        assert "error" not in r

    def test_manifold_status(self, server):
        r = server.call_tool("manifold_shell", {"command": "status"})
        assert "error" not in r


class TestInfraShells:
    """Verify infrastructure shells."""

    def test_twelve_status(self, server):
        r = server.call_tool("twelve_shell", {"command": "status"})
        assert "error" not in r

    def test_telemetry_status(self, server):
        r = server.call_tool("telemetry_shell", {"command": "status"})
        assert "error" not in r

    def test_learning_status(self, server):
        r = server.call_tool("learning_shell", {"command": "status"})
        assert "error" not in r

    def test_basin_system_status(self, server):
        r = server.call_tool("basin_system", {"command": "status"})
        assert "error" not in r

    def test_world_status(self, server):
        r = server.call_tool("world_shell", {"command": "status"})
        assert "error" not in r

    def test_cypher_status(self, server):
        r = server.call_tool("cypher_shell", {"command": "status"})
        assert "error" not in r


class TestNewShells:
    """Verify newly added shells (thinking, adversarial, etc.)."""

    def test_thinking_status(self, server):
        r = server.call_tool("thinking_shell", {"command": "status"})
        assert "error" not in r

    def test_thinking_autonomous_status(self, server):
        r = server.call_tool("thinking_autonomous_shell", {"command": "status"})
        assert "error" not in r

    def test_adversarial_status(self, server):
        r = server.call_tool("adversarial_shell", {"command": "status"})
        assert "error" not in r

    def test_persistence_status(self, server):
        r = server.call_tool("persistence_shell", {"command": "status"})
        assert "error" not in r

    def test_benchmark_status(self, server):
        r = server.call_tool("benchmark_shell", {"command": "status"})
        assert "error" not in r

    def test_ticket_status(self, server):
        r = server.call_tool("ticket_shell", {"command": "status"})
        assert "error" not in r

    def test_epicycle_status(self, server):
        r = server.call_tool("epicycle_shell", {"command": "status"})
        assert "error" not in r


class TestVisualShells:
    """Verify visual/copilot shells."""

    def test_vision_status(self, server):
        r = server.call_tool("vision_shell", {"command": "status"})
        assert "error" not in r

    def test_copilot_status(self, server):
        r = server.call_tool("copilot_shell", {"command": "status"})
        assert "error" not in r

    def test_ogawa_status(self, server):
        r = server.call_tool("ogawa_shell", {"command": "status"})
        assert "error" not in r

    def test_phi_status(self, server):
        r = server.call_tool("phi_integration", {"command": "status"})
        assert "error" not in r

    def test_chat_status(self, server):
        r = server.call_tool("cst_chat", {"command": "status"})
        assert "error" not in r

    def test_data_flip_status(self, server):
        r = server.call_tool("data_flip_shell", {"command": "status"})
        assert "error" not in r


# ---------- Phase 4: Swarm Routing Accuracy (19/19) ----------

class TestSwarmRoutingAccuracy:
    """Validate FML coordinate + I-Logic algebraic routing: 19/19 deterministic.

    Uses FML encoding (basin coordinate + K5×K11 state) for Pass 1,
    algebraic resolution (task_state × agent_state = DIRECT) for
    disambiguation, and basin proximity for specialist selection.

    The expected routing is DETERMINISTIC — same task always routes to
    the same agent because FML encoding is a pure function of the text.
    """

    # fmt: off
    EXPECTED_ROUTING = {
        # Pure math routing: FML coordinate + I-Logic algebra + basin proximity
        # No LLM in the hot path — deterministic by construction
        # Tasks that collapse to ABSORBED use -8th channel recovery
        "find all 52 tools and navigate the shell architecture":   "Generator",
        "create a structure showing how agents connect to basins":  "Guardian",   # -8th channel
        "interpret what the I-Logic algebra means and classify it": "Generator",
        "translate the basin coordinate into layer meaning":       "Healer",
        "convert the basin coordinate into layer format":           "Stabilizer",
        "generate a report of system capabilities":                "Generator",
        "refactor the routing to show algebraic paths":            "Guardian",   # -8th channel
        "identify who we are as a swarm collective":               "Generator",
        "fix any broken connections in the agent graph":           "Healer",     # -8th channel
        "monitor the health of all thinking cycles":               "Stabilizer",
        "guard the L9 membrane boundary from corruption":          "Generator",
        "transmit findings from L9 across to L10-L12":             "Guardian",   # -8th channel
        "where are the 19 agents positioned in Morton space":      "Guardian",
        "coordinate agents across L1-L9 layers":                   "Stabilizer",
        "schedule the 5-phase thinking sequence":                  "Stabilizer",
        "sync frequencies between L7 and L8 agents":               "Healer",
        "validate that all 131 basins are populated":              "Stabilizer",
        "balance load across the 4 I-Logic state groups":          "Generator",
        "stabilize the membrane boundary at L9":                   "Stabilizer",
        "amplify weak signals from L1 geometric layer":            "Stabilizer",
    }
    # fmt: on

    @pytest.fixture(scope="class")
    def swarm(self):
        from cubesquare import Swarm
        return Swarm()

    @pytest.mark.parametrize(
        "task,expected_agent",
        list(EXPECTED_ROUTING.items()),
        ids=list(EXPECTED_ROUTING.keys()),
    )
    def test_routing(self, swarm, task, expected_agent):
        result = swarm.dispatch(task)
        actual = result.agent.name
        assert actual == expected_agent, (
            f"Task '{task}' routed to {actual}, expected {expected_agent}"
        )


# ---------- Phase 5: JSON-RPC Protocol ----------

class TestJSONRPCProtocol:
    """Verify MCP JSON-RPC 2.0 protocol handling."""

    def test_direct_transport_initialize(self, server):
        transport = server.get_direct_transport()
        resp = transport.call("initialize", {"protocolVersion": "2024-11-05"})
        assert resp["result"]["protocolVersion"] == "2024-11-05"
        assert resp["result"]["serverInfo"]["name"] == "cubesquare-mcp"

    def test_direct_transport_tools_list(self, server):
        transport = server.get_direct_transport()
        resp = transport.call("tools/list")
        tools = resp["result"]["tools"]
        assert len(tools) == 58

    def test_direct_transport_tools_call(self, server):
        transport = server.get_direct_transport()
        result = transport.call_tool(
            "i_logic_shell",
            {"command": "multiply", "state1": "SPIRAL", "state2": "SPIRAL"}
        )
        # call_tool returns parsed JSON result
        assert isinstance(result, dict)

    def test_direct_transport_ping(self, server):
        transport = server.get_direct_transport()
        resp = transport.call("ping")
        assert resp["result"] == {}

    def test_unknown_method(self, server):
        transport = server.get_direct_transport()
        resp = transport.call("nonexistent/method")
        assert "error" in resp
        assert resp["error"]["code"] == -32601

    def test_unknown_tool(self, server):
        r = server.call_tool("nonexistent_tool", {"command": "status"})
        assert "error" in r
