"""MCPClient — Generic client for the Model Context Protocol."""
from __future__ import annotations

import json
import urllib.request
import urllib.error
from typing import Any


class MCPConnectionError(Exception):
    """Raised when the MCP client cannot connect to the server."""
    pass


class MCPClient:
    def __init__(self, base_url: str, timeout: int = 30):
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout
        self._cached_tools = None

    def discover(self, refresh: bool = False) -> list[dict]:
        """Discover available tools. Cached unless refresh=True."""
        if self._cached_tools is not None and not refresh:
            return self._cached_tools
        data = self._post("/tools/list", {})
        if not isinstance(data, list):
            data = data.get("tools", []) if isinstance(data, dict) else []
        self._cached_tools = data
        return self._cached_tools

    def call(self, tool: str, arguments: dict) -> dict:
        """Call a tool. Returns dict with "result" or "error" key."""
        return self._post("/tools/call", {"tool": tool, "arguments": arguments})

    def _post(self, path: str, body: Any) -> Any:
        url = f"{self.base_url}{path}"
        data = json.dumps(body).encode("utf-8")
        req = urllib.request.Request(
            url, data=data,
            headers={"Content-Type": "application/json"},
            method="POST",
        )
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as resp:
                return json.loads(resp.read().decode("utf-8"))
        except urllib.error.URLError as e:
            raise MCPConnectionError(
                f"Cannot connect to MCP server at {self.base_url}: {e}"
            )
        except Exception as e:
            raise MCPConnectionError(f"MCP request failed: {e}")
