"""OpenHedge SDK exception hierarchy.

All SDK errors inherit from ``OpenHedgeError`` so callers can catch a single
base class or handle specific failure modes.
"""

from __future__ import annotations

from typing import Any, Dict, Optional


class OpenHedgeError(Exception):
    """Base exception for every error raised by the OpenHedge SDK."""

    def __init__(
        self,
        message: str,
        *,
        status_code: Optional[int] = None,
        details: Optional[Dict[str, Any]] = None,
    ) -> None:
        super().__init__(message)
        self.message = message
        self.status_code = status_code
        self.details = details or {}


# ---------------------------------------------------------------------------
# HTTP / REST errors
# ---------------------------------------------------------------------------

class AuthenticationError(OpenHedgeError):
    """Raised on HTTP 401 — invalid or missing API key."""

    def __init__(self, message: str = "Invalid or missing API key") -> None:
        super().__init__(message, status_code=401)


class AgentNotApprovedError(OpenHedgeError):
    """Raised on HTTP 403 — agent wallet is not approved by the registry."""

    def __init__(self, message: str = "Agent not approved") -> None:
        super().__init__(message, status_code=403)


class NotFoundError(OpenHedgeError):
    """Raised on HTTP 404."""

    def __init__(self, message: str = "Resource not found") -> None:
        super().__init__(message, status_code=404)


class ValidationError(OpenHedgeError):
    """Raised on HTTP 422 — request payload failed server-side validation."""

    def __init__(
        self,
        message: str = "Validation failed",
        *,
        field_errors: Optional[Dict[str, str]] = None,
    ) -> None:
        super().__init__(message, status_code=422, details={"field_errors": field_errors or {}})
        self.field_errors = field_errors or {}


class RateLimitError(OpenHedgeError):
    """Raised on HTTP 429 — too many requests."""

    def __init__(
        self,
        message: str = "Rate limit exceeded",
        retry_after: float = 1.0,
    ) -> None:
        super().__init__(message, status_code=429, details={"retry_after": retry_after})
        self.retry_after = retry_after


class ServerError(OpenHedgeError):
    """Raised on HTTP 5xx."""

    def __init__(self, message: str = "Internal server error") -> None:
        super().__init__(message, status_code=500)


class NetworkError(OpenHedgeError):
    """Raised when a connection to the API or RPC node cannot be established."""

    def __init__(self, message: str = "Network error") -> None:
        super().__init__(message)


# ---------------------------------------------------------------------------
# On-chain / transaction errors
# ---------------------------------------------------------------------------

class TransactionError(OpenHedgeError):
    """Raised when an on-chain transaction reverts or fails to confirm."""

    def __init__(
        self,
        message: str,
        *,
        tx_hash: Optional[str] = None,
    ) -> None:
        super().__init__(message, details={"tx_hash": tx_hash})
        self.tx_hash = tx_hash


class InsufficientBalanceError(TransactionError):
    """The vault or wallet does not hold enough tokens for the operation."""
    pass


class SlippageExceededError(TransactionError):
    """The actual output fell below the minimum dictated by slippage tolerance."""
    pass


class RiskLimitBreachedError(TransactionError):
    """Trade would violate the vault's guardrail configuration."""
    pass


class AdapterNotApprovedError(TransactionError):
    """The adapter has not been approved on TradeRouter for this vault."""
    pass


# ---------------------------------------------------------------------------
# Domain errors
# ---------------------------------------------------------------------------

class VaultPausedError(OpenHedgeError):
    """The target vault is currently paused and cannot accept operations."""

    def __init__(self, message: str = "Vault is paused") -> None:
        super().__init__(message)


class InvalidPairError(OpenHedgeError):
    """The requested trading pair is not supported."""

    def __init__(self, pair: str = "") -> None:
        msg = f"Invalid or unsupported pair: {pair}" if pair else "Invalid pair"
        super().__init__(msg)
