"""Call a Limma MCP tool the way an attached agent does — over the MCP protocol — in-process.

An agent launches ``python -m epistem.mcp.server`` with ``LIMMA_API_BASE_URL`` and
``LIMMA_API_KEY`` (the quickstart's client config) and calls tools through an MCP client
session. The docs examples cannot start a server on a port, so :func:`call_tool` runs the
SAME server object (``epistem.mcp.server.mcp``) behind an in-memory MCP client session
(``mcp.shared.memory``) and points its HTTP client at the example app through an ASGI
transport. Nothing between the tool and the route is replaced: the tool's own ``_post``
and ``_raise_for_status`` run, and what comes back is the ``CallToolResult`` an agent's
client receives — ``isError`` and the text content exactly as the protocol carries them.

The server keeps ONE pooled HTTP client and reads its base URL and key from the
environment; :func:`call_tool` sets all three for the one call and puts back what was
there, so no other example or test sees them changed.
"""
from __future__ import annotations

import asyncio
import os
from typing import Any, Dict

#: The environment the MCP server reads on every call (``epistem/mcp/server.py``).
BASE_URL_ENV = "LIMMA_API_BASE_URL"
API_KEY_ENV = "LIMMA_API_KEY"
#: Any host: the ASGI transport never opens a socket, it hands the request to the app.
IN_PROCESS_BASE_URL = "http://limma.docs"


async def _call(client: Any, tool: str, arguments: Dict[str, Any]) -> Any:
    import httpx
    from mcp.shared.memory import create_connected_server_and_client_session

    from epistem.mcp import server

    saved_client = server._client
    saved_env = {name: os.environ.get(name) for name in (BASE_URL_ENV, API_KEY_ENV)}
    pooled = httpx.AsyncClient(transport=httpx.ASGITransport(app=client.app))
    server._client = pooled
    os.environ[BASE_URL_ENV] = IN_PROCESS_BASE_URL
    key = client.headers.get("X-API-Key")
    if key:
        os.environ[API_KEY_ENV] = key
    else:
        os.environ.pop(API_KEY_ENV, None)
    try:
        async with create_connected_server_and_client_session(server.mcp) as session:
            return await session.call_tool(tool, arguments)
    finally:
        await pooled.aclose()
        server._client = saved_client
        for name, value in saved_env.items():
            if value is None:
                os.environ.pop(name, None)
            else:
                os.environ[name] = value


def call_tool(client: Any, tool: str, arguments: Dict[str, Any]) -> Any:
    """``tool(**arguments)`` over an MCP client session against ``client``'s app, with
    ``client``'s API key: the ``mcp.types.CallToolResult`` the agent receives."""
    return asyncio.run(_call(client, tool, arguments))


__all__ = ["call_tool"]
