"""Typed exceptions raised by the MCP client wrappers.

Every wrapper translates HTTP status codes and JSON error envelopes into
one of these classes so the orchestrator can decide its degradation
strategy without inspecting raw HTTP traces.
"""

from __future__ import annotations

__all__ = [
    "McpAuthError",
    "McpDataAnomalyError",
    "McpError",
    "McpNotFoundError",
    "McpServerError",
    "McpTimeoutError",
    "McpToolError",
]


class McpError(Exception):
    """Base class for every MCP-side failure surfaced to the engine."""

    def __init__(
        self,
        message: str,
        *,
        service: str | None = None,
        tool: str | None = None,
    ) -> None:
        super().__init__(message)
        self.service = service
        self.tool = tool


class McpTimeoutError(McpError):
    """The MCP service did not respond within the configured timeout."""


class McpAuthError(McpError):
    """The bearer token was rejected (HTTP 401/403)."""


class McpNotFoundError(McpError):
    """Endpoint missing on the server (HTTP 404). Indicates schema drift."""


class McpServerError(McpError):
    """5xx or unexpected HTTP error returned by the MCP service."""


class McpToolError(McpError):
    """The tool returned a structured error envelope (e.g. ``{state: "error"}``)."""

    def __init__(
        self,
        message: str,
        *,
        service: str | None = None,
        tool: str | None = None,
        payload: dict[str, object] | None = None,
    ) -> None:
        super().__init__(message, service=service, tool=tool)
        self.payload = payload or {}


class McpDataAnomalyError(McpError):
    """The response shape was valid but the values are nonsensical.

    Example: every option in the chain has ``mark_iv == 7%`` (the
    Deribit testnet placeholder), or every bid is zero. The orchestrator
    skips the cycle and alerts.
    """
