"""MCP Server Implementation — C=0 Fernandez Agent

JSON-RPC 2.0 server for Model Context Protocol.
Exposes 55 cubesquare tools to MCP clients (52 shell + 3 C=0 agent).

The MCP server IS a C=0 Fernandez sphere:
- Every tool call enters at the origin (C=0)
- Euler rotates 2π/131 per call
- Cross-MCP communication via .cst binary envelopes
- L10 agent identity, L11 channels, L12 unified field

Usage:
    server = MCPServer()
    server.agent          # MCPAgent identity at C=0
    server.call_tool(...)  # Routed through Fernandez sphere
"""

import json
from typing import Dict, Any, Optional, List

from .tools import ToolRegistry
from .transport import StdioTransport, DirectTransport
from .agent import MCPAgent


# MCP Protocol version
PROTOCOL_VERSION = "2024-11-05"

# Server info
SERVER_INFO = {
    "name": "cubesquare-mcp",
    "version": "1.0.0"
}

# Server capabilities
CAPABILITIES = {
    "tools": {},  # We support tools
}


class MCPServer:
    """MCP Server as a C=0 Fernandez Agent.

    The server IS the Fernandez sphere — every tool call enters at C=0,
    rotates by 2π/131 (Euler step), and exits through the L9 membrane.

    Cross-MCP communication uses .cst binary envelopes via L10-L12.

    Example (stdio - for Claude Desktop, Cursor):
        server = MCPServer()
        server.run()  # Blocks, handles stdin/stdout

    Example (direct - for testing):
        server = MCPServer()
        transport = server.get_direct_transport()
        result = transport.call_tool("i_logic_shell", {"command": "multiply", "state1": "SPIRAL", "state2": "SPIRAL"})
        print(result)  # {'result': 'INVERSE', ...}

    Example (C=0 agent identity):
        server = MCPServer()
        print(server.agent.sphere_id)      # 8-char hex identity
        print(server.agent.identity)       # Full MCPIdentity with L10-L12
        print(server.agent.status())       # Fernandez sphere status
    """

    def __init__(self):
        self._registry = ToolRegistry()
        self._transport: Optional[StdioTransport] = None
        self._initialized = False

        # C=0 Fernandez agent — the server IS this sphere
        self._agent = MCPAgent(SERVER_INFO["name"])
        self._agent.load_session()  # Resume from last Euler position

        # Wire agent into registry for C=0 tools
        self._registry.set_agent(self._agent)

    @property
    def agent(self) -> MCPAgent:
        """The C=0 Fernandez agent identity of this server."""
        return self._agent

    def _handle_message(self, message: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """Handle incoming JSON-RPC message."""
        msg_id = message.get("id")
        method = message.get("method", "")
        params = message.get("params", {})

        # Route to appropriate handler
        if method == "initialize":
            return self._handle_initialize(msg_id, params)
        elif method == "initialized":
            return None  # Notification, no response
        elif method == "tools/list":
            return self._handle_tools_list(msg_id, params)
        elif method == "tools/call":
            return self._handle_tools_call(msg_id, params)
        elif method == "ping":
            return self._make_response(msg_id, {})
        else:
            return self._make_error(msg_id, -32601, f"Method not found: {method}")

    def _handle_initialize(self, msg_id: Any, params: Dict[str, Any]) -> Dict[str, Any]:
        """Handle initialize request — includes C=0 identity."""
        self._initialized = True

        return self._make_response(msg_id, {
            "protocolVersion": PROTOCOL_VERSION,
            "capabilities": CAPABILITIES,
            "serverInfo": {
                **SERVER_INFO,
                "sphere_id": self._agent.sphere_id,
                "c_zero": True,
            }
        })

    def _handle_tools_list(self, msg_id: Any, params: Dict[str, Any]) -> Dict[str, Any]:
        """Handle tools/list request."""
        tools = self._registry.list_tools()
        return self._make_response(msg_id, {"tools": tools})

    def _handle_tools_call(self, msg_id: Any, params: Dict[str, Any]) -> Dict[str, Any]:
        """Handle tools/call request — routed through C=0 Fernandez sphere."""
        name = params.get("name", "")
        arguments = params.get("arguments", {})

        # Route through the C=0 agent (Euler rotation + coupling)
        result = self._agent.process(name, arguments, self._registry)

        if "error" in result:
            return self._make_response(msg_id, {
                "content": [{
                    "type": "text",
                    "text": json.dumps({"error": result["error"]})
                }],
                "isError": True
            })

        return self._make_response(msg_id, {
            "content": [{
                "type": "text",
                "text": json.dumps(result.get("result", {}))
            }]
        })

    def _make_response(self, msg_id: Any, result: Any) -> Dict[str, Any]:
        """Create JSON-RPC response."""
        return {
            "jsonrpc": "2.0",
            "id": msg_id,
            "result": result
        }

    def _make_error(self, msg_id: Any, code: int, message: str) -> Dict[str, Any]:
        """Create JSON-RPC error response."""
        return {
            "jsonrpc": "2.0",
            "id": msg_id,
            "error": {
                "code": code,
                "message": message
            }
        }

    def run(self):
        """Run server with stdio transport (blocking)."""
        self._transport = StdioTransport()
        self._transport.set_handler(self._handle_message)
        self._transport.start()
        # Save session on clean shutdown
        self._agent.save_session()

    def get_direct_transport(self) -> DirectTransport:
        """Get direct transport for testing."""
        return DirectTransport(self._handle_message)

    def list_tools(self) -> List[Dict[str, Any]]:
        """List all available tools."""
        return self._registry.list_tools()

    def call_tool(self, name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
        """Call a tool directly (for testing/scripting).

        Routes through the C=0 agent — every call advances the
        Euler rotation and attaches coupling metadata.
        """
        return self._agent.process(name, arguments, self._registry)

    def get_tool_info(self, name: str) -> Optional[Dict[str, Any]]:
        """Get info about a specific tool."""
        return self._registry.get_tool_info(name)

    # ── Cross-MCP Communication ─────────────────────────────

    def send_cst(self, target_id: str, records, path: str = None) -> Dict[str, Any]:
        """Send .cst envelope to another MCP sphere.

        Args:
            target_id: Target sphere's 8-char hex ID
            records: List of CSTRecords to transmit
            path: File path for .cst delivery (optional)

        Returns:
            Envelope metadata including UIF coupling strength.
        """
        from .agent import MCPEnvelope
        envelope = self._agent.compose_message(target_id, records)

        if path:
            return self._agent.send_to_file(envelope, path)
        return envelope.to_dict()

    def receive_cst(self, path: str) -> Dict[str, Any]:
        """Receive .cst envelope from another MCP sphere.

        Args:
            path: File path of the .cst envelope

        Returns:
            Decoded envelope with records and coupling info.
        """
        envelope = self._agent.receive_from_file(path)
        return {
            'envelope': envelope.to_dict(),
            'records': [r.to_dict() for r in envelope.records],
        }


def main():
    """Entry point for MCP server."""
    server = MCPServer()
    server.run()


if __name__ == "__main__":
    main()
