"""MCPClient — Comunicação HTTP com o servidor MCP."""

from __future__ import annotations

import json
from typing import Any

import httpx

from .config import get_server_url, get_token

# Timeout alto para operações longas (analisar map-reduce)
DEFAULT_TIMEOUT = 120.0


class MCPError(Exception):
    """Erro retornado pelo servidor MCP."""

    def __init__(self, code: int, message: str, data: Any = None):
        self.code = code
        self.message = message
        self.data = data
        super().__init__(message)


class AuthError(Exception):
    """Token ausente ou inválido."""


class MCPClient:
    """Cliente HTTP para o protocolo MCP (Streamable HTTP)."""

    def __init__(
        self,
        server_url: str | None = None,
        token: str | None = None,
    ):
        self.server_url = server_url or get_server_url()
        self.token = token or get_token()
        self.session_id: str | None = None
        self._request_id = 0

    def _next_id(self) -> int:
        self._request_id += 1
        return self._request_id

    def _headers(self) -> dict[str, str]:
        if not self.token:
            raise AuthError("Token não configurado. Use: tecjustica auth login --token <TOKEN>")
        headers = {
            "Content-Type": "application/json",
            "Accept": "application/json, text/event-stream",
            "Authorization": f"Bearer {self.token}",
        }
        if self.session_id:
            headers["mcp-session-id"] = self.session_id
        return headers

    def _parse_sse(self, response: httpx.Response) -> dict[str, Any]:
        """Extrai JSON-RPC response de um SSE stream ou JSON direto."""
        content_type = response.headers.get("content-type", "")

        if "text/event-stream" in content_type:
            for line in response.text.splitlines():
                if line.startswith("data:"):
                    data_str = line[5:].strip()
                    if data_str:
                        return json.loads(data_str)
            raise MCPError(-1, "Resposta SSE vazia do servidor")

        # JSON direto
        return response.json()

    def connect(self) -> None:
        """Inicializa sessão MCP (initialize + notifications/initialized)."""
        with httpx.Client(timeout=DEFAULT_TIMEOUT) as http:
            # 1. Initialize
            resp = http.post(
                self.server_url,
                headers=self._headers(),
                json={
                    "jsonrpc": "2.0",
                    "id": self._next_id(),
                    "method": "initialize",
                    "params": {
                        "protocolVersion": "2025-03-26",
                        "capabilities": {},
                        "clientInfo": {"name": "tecjustica-cli", "version": "0.1.0"},
                    },
                },
            )
            self._check_http_error(resp)
            self.session_id = resp.headers.get("mcp-session-id")

            # 2. notifications/initialized
            resp = http.post(
                self.server_url,
                headers=self._headers(),
                json={
                    "jsonrpc": "2.0",
                    "method": "notifications/initialized",
                    "params": {},
                },
            )
            self._check_http_error(resp)

    def call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> Any:
        """Chama uma tool MCP. Conecta automaticamente se necessário."""
        if not self.session_id:
            self.connect()

        with httpx.Client(timeout=DEFAULT_TIMEOUT) as http:
            resp = http.post(
                self.server_url,
                headers=self._headers(),
                json={
                    "jsonrpc": "2.0",
                    "id": self._next_id(),
                    "method": "tools/call",
                    "params": {
                        "name": name,
                        "arguments": arguments or {},
                    },
                },
            )
            self._check_http_error(resp)
            result = self._parse_sse(resp)

            # Verificar erro JSON-RPC
            if "error" in result:
                err = result["error"]
                raise MCPError(err.get("code", -1), err.get("message", "Erro desconhecido"))

            return self._extract_content(result)

    def _extract_content(self, result: dict[str, Any]) -> Any:
        """Extrai conteúdo útil da resposta MCP."""
        content_list = result.get("result", {}).get("content", [])
        if not content_list:
            return result.get("result", {})

        # Se tem um único item de texto, parse como JSON se possível
        if len(content_list) == 1 and content_list[0].get("type") == "text":
            text = content_list[0]["text"]
            try:
                return json.loads(text)
            except (json.JSONDecodeError, TypeError):
                return text

        # Múltiplos itens — retorna lista de textos
        texts = [item.get("text", "") for item in content_list if item.get("type") == "text"]
        if len(texts) == 1:
            try:
                return json.loads(texts[0])
            except (json.JSONDecodeError, TypeError):
                return texts[0]
        return texts

    def _check_http_error(self, resp: httpx.Response) -> None:
        """Trata erros HTTP com mensagens amigáveis."""
        if resp.status_code == 401:
            raise AuthError(
                "Token inválido ou expirado. Use: tecjustica auth login --token <TOKEN>"
            )
        if resp.status_code >= 400:
            raise MCPError(
                resp.status_code,
                f"Erro HTTP {resp.status_code}: {resp.text[:200]}",
            )
