"""MCP long-poll client for Mercury.

Adapted from proton/scripts/mercury_bridge.py mercury_call() pattern.
Uses httpx.AsyncClient for non-blocking HTTP.
"""

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

import httpx

from mercury_launcher.log import get_logger

logger = get_logger("poller")


class MercuryPoller:
    """Async client for Mercury's MCP JSON-RPC 2.0 endpoint."""

    def __init__(self, endpoint: str, api_key: str):
        self.endpoint = endpoint
        self.api_key = api_key
        self._client: Optional[httpx.AsyncClient] = None

    async def _ensure_client(self) -> httpx.AsyncClient:
        if self._client is None or self._client.is_closed:
            self._client = httpx.AsyncClient(timeout=httpx.Timeout(120.0))
        return self._client

    async def close(self) -> None:
        if self._client and not self._client.is_closed:
            await self._client.aclose()
            self._client = None

    async def _call(self, tool_name: str, arguments: Optional[Dict[str, Any]] = None) -> Any:
        """Call a Mercury MCP tool via JSON-RPC 2.0 tools/call."""
        client = await self._ensure_client()

        payload = {
            "jsonrpc": "2.0",
            "method": "tools/call",
            "params": {
                "name": tool_name,
                "arguments": arguments or {},
            },
            "id": str(uuid.uuid4()),
        }

        resp = await client.post(
            self.endpoint,
            json=payload,
            headers={"x-api-key": self.api_key},
        )
        resp.raise_for_status()
        data = resp.json()
        result = data.get("result", data.get("error", data))

        # Unwrap MCP content format: {"content": [{"type": "text", "text": "..."}]}
        if isinstance(result, dict) and "content" in result:
            # Check for tool-level errors
            if result.get("isError"):
                for block in result["content"]:
                    if block.get("type") == "text":
                        raise MercuryError(block.get("text", "Unknown error"))

            for block in result["content"]:
                if block.get("type") == "text":
                    try:
                        return json.loads(block["text"])
                    except (json.JSONDecodeError, KeyError):
                        return {"text": block.get("text", "")}

        return result

    async def wait_for_messages(self, timeout: int = 30) -> List[Dict[str, Any]]:
        """Long-poll for new messages. Returns list of message dicts."""
        result = await self._call("mercury_wait_for_messages", {"timeout": timeout})
        if isinstance(result, dict):
            return result.get("messages", [])
        return []

    async def list_agents(self) -> List[Dict[str, Any]]:
        """List connected agents."""
        result = await self._call("mercury_list_agents")
        return result if isinstance(result, list) else []

    async def read_thread(self, thread_id: str, limit: int = 50) -> List[Dict[str, Any]]:
        """Read messages from a thread."""
        result = await self._call("mercury_read_thread", {
            "thread_id": thread_id,
            "limit": limit,
        })
        return result if isinstance(result, list) else []

    async def list_threads(self, agent_name: Optional[str] = None) -> List[Dict[str, Any]]:
        """List threads, optionally filtered by agent name."""
        args = {}
        if agent_name:
            args["agent_name"] = agent_name
        result = await self._call("mercury_list_threads", args)
        return result if isinstance(result, list) else []

    async def get_agent_context(self) -> Dict[str, Any]:
        """Get the calling agent's identity and context."""
        result = await self._call("mercury_get_agent_context")
        return result if isinstance(result, dict) else {}

    async def update_status(self, status: str | None) -> None:
        """Update the agent's visible status in the Mercury UI."""
        await self._call("mercury_update_status", {"status": status or ""})

    async def post_activity(self, thread_id: str, content: str) -> None:
        """Post a metadata activity message to a thread (visible in UI, not to agents)."""
        await self._call("mercury_post_activity", {
            "thread_id": thread_id,
            "content": content,
        })

    async def send_message(
        self,
        to: str,
        content: str,
        thread_id: Optional[str] = None,
        subject: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Send a message to a connected agent."""
        args: Dict[str, Any] = {"to": to, "content": content}
        if thread_id:
            args["thread_id"] = thread_id
        if subject:
            args["subject"] = subject
        result = await self._call("mercury_send_message", args)
        return result if isinstance(result, dict) else {}


class MercuryError(Exception):
    """Error returned by Mercury MCP server."""
    pass
