"""
SMF Exception Hierarchy.

Provides a structured exception hierarchy for consistent error handling
throughout the SMF framework.
"""

from __future__ import annotations

from typing import Any


class SMFError(Exception):
    """Base exception for all SMF errors.

    Attributes:
        message: Human-readable error message.
        code: Machine-readable error code (defaults to class name).
        details: Additional context about the error.
    """

    def __init__(
        self,
        message: str,
        *,
        code: str | None = None,
        details: dict[str, Any] | None = None,
    ) -> None:
        super().__init__(message)
        self.message = message
        self.code = code or self.__class__.__name__
        self.details = details or {}

    def __str__(self) -> str:
        if self.details:
            return f"{self.message} (code={self.code}, details={self.details})"
        return f"{self.message} (code={self.code})"

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}({self.message!r}, code={self.code!r})"


# =============================================================================
# Configuration Errors
# =============================================================================


class ConfigurationError(SMFError):
    """Raised when configuration is invalid or incomplete."""

    pass


class MissingConfigurationError(ConfigurationError):
    """Raised when required configuration is missing."""

    pass


class InvalidConfigurationError(ConfigurationError):
    """Raised when configuration values are invalid."""

    pass


# =============================================================================
# Connection Errors
# =============================================================================


class SMFConnectionError(SMFError):
    """Base class for connection-related errors."""

    pass


class ConnectionTimeoutError(SMFConnectionError):
    """Raised when a connection times out."""

    pass


class ConnectionRefusedError(SMFConnectionError):
    """Raised when a connection is refused."""

    pass


# =============================================================================
# Plugin Errors
# =============================================================================


class PluginError(SMFError):
    """Base class for plugin-related errors."""

    pass


class PluginNotFoundError(PluginError):
    """Raised when a required plugin is not found."""

    pass


class PluginLoadError(PluginError):
    """Raised when a plugin fails to load."""

    pass


class PluginInitializationError(PluginError):
    """Raised when a plugin fails to initialize."""

    pass


# =============================================================================
# Elasticsearch-specific Errors
# =============================================================================


class ElasticsearchError(PluginError):
    """Base class for Elasticsearch-related errors."""

    pass


class ElasticsearchConnectionError(ElasticsearchError, SMFConnectionError):
    """Raised when Elasticsearch connection fails."""

    pass


class ElasticsearchQueryError(ElasticsearchError):
    """Raised when an Elasticsearch query fails."""

    def __init__(
        self,
        message: str,
        *,
        query: dict[str, Any] | None = None,
        index: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(message, **kwargs)
        self.query = query
        self.index = index


class ElasticsearchTimeoutError(ElasticsearchError, ConnectionTimeoutError):
    """Raised when an Elasticsearch operation times out."""

    pass


class ElasticsearchBulkError(ElasticsearchError):
    """Raised when a bulk operation has failures.

    Attributes:
        succeeded: Number of successful operations.
        failed: Number of failed operations.
        errors: List of individual error details.
    """

    def __init__(
        self,
        message: str,
        *,
        succeeded: int = 0,
        failed: int = 0,
        errors: list[dict[str, Any]] | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(message, **kwargs)
        self.succeeded = succeeded
        self.failed = failed
        self.errors = errors or []


class CircuitBreakerOpenError(ElasticsearchError):
    """Raised when circuit breaker is open and blocking requests.

    Attributes:
        retry_after: Suggested seconds to wait before retrying.
    """

    def __init__(
        self,
        message: str = "Circuit breaker is open",
        *,
        retry_after: float | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(message, **kwargs)
        self.retry_after = retry_after


# =============================================================================
# Registry Errors
# =============================================================================


class RegistryError(SMFError):
    """Base class for registry-related errors."""

    pass


class DuplicateRegistrationError(RegistryError):
    """Raised when attempting to register a duplicate component.

    Attributes:
        component_name: Name of the component being registered.
        component_type: Type of component (e.g., 'tool', 'resource').
    """

    def __init__(
        self,
        message: str,
        *,
        component_name: str | None = None,
        component_type: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(message, **kwargs)
        self.component_name = component_name
        self.component_type = component_type


class ComponentNotFoundError(RegistryError):
    """Raised when a component is not found in the registry."""

    pass


# =============================================================================
# Authentication & Authorization Errors
# =============================================================================


class AuthenticationError(SMFError):
    """Raised when authentication fails."""

    pass


class AuthorizationError(SMFError):
    """Raised when authorization fails."""

    pass


class InsufficientScopesError(AuthorizationError):
    """Raised when token lacks required scopes.

    Attributes:
        required_scopes: Scopes that were required.
        provided_scopes: Scopes that were provided.
    """

    def __init__(
        self,
        message: str,
        *,
        required_scopes: set[str] | None = None,
        provided_scopes: set[str] | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(message, **kwargs)
        self.required_scopes = required_scopes or set()
        self.provided_scopes = provided_scopes or set()


class TokenExpiredError(AuthenticationError):
    """Raised when an authentication token has expired."""

    pass


class InvalidTokenError(AuthenticationError):
    """Raised when an authentication token is invalid."""

    pass


# =============================================================================
# Transport Errors
# =============================================================================


class TransportError(SMFError):
    """Base class for transport-related errors."""

    pass


class UnsupportedTransportError(TransportError):
    """Raised when an unsupported transport is requested."""

    pass


class TransportInitializationError(TransportError):
    """Raised when transport fails to initialize."""

    pass


# =============================================================================
# Tool Errors
# =============================================================================


class ToolError(SMFError):
    """Base class for tool execution errors."""

    pass


class ToolExecutionError(ToolError):
    """Raised when a tool fails during execution."""

    pass


class ToolValidationError(ToolError):
    """Raised when tool input validation fails."""

    pass


__all__ = [
    # Base
    "SMFError",
    # Configuration
    "ConfigurationError",
    "MissingConfigurationError",
    "InvalidConfigurationError",
    # Connection
    "SMFConnectionError",
    "ConnectionTimeoutError",
    "ConnectionRefusedError",
    # Plugin
    "PluginError",
    "PluginNotFoundError",
    "PluginLoadError",
    "PluginInitializationError",
    # Elasticsearch
    "ElasticsearchError",
    "ElasticsearchConnectionError",
    "ElasticsearchQueryError",
    "ElasticsearchTimeoutError",
    "ElasticsearchBulkError",
    "CircuitBreakerOpenError",
    # Registry
    "RegistryError",
    "DuplicateRegistrationError",
    "ComponentNotFoundError",
    # Auth
    "AuthenticationError",
    "AuthorizationError",
    "InsufficientScopesError",
    "TokenExpiredError",
    "InvalidTokenError",
    # Transport
    "TransportError",
    "UnsupportedTransportError",
    "TransportInitializationError",
    # Tool
    "ToolError",
    "ToolExecutionError",
    "ToolValidationError",
]
