"""Custom exceptions for the MCP Proxy.

This module defines the exception hierarchy for error handling
throughout the proxy system.
"""

from typing import Optional, Any


class McpProxyError(Exception):
    """Base exception for all MCP Proxy errors."""
    
    def __init__(self, message: str, details: Optional[Any] = None):
        """Initialize exception with message and optional details.
        
        Args:
            message: Error message
            details: Additional error details
        """
        super().__init__(message)
        self.message = message
        self.details = details


# ============================================================================
# Protocol Translation Errors
# ============================================================================


class ProtocolError(McpProxyError):
    """Base exception for protocol translation errors."""
    pass


class InvalidJsonRpcError(ProtocolError):
    """Raised when JSON-RPC message is invalid."""
    pass


class MessageCorrelationError(ProtocolError):
    """Raised when message correlation fails."""
    pass


class UnsupportedMethodError(ProtocolError):
    """Raised when an unsupported MCP method is called."""
    
    def __init__(self, method: str, details: Optional[Any] = None):
        super().__init__(f"Unsupported method: {method}", details)
        self.method = method


# ============================================================================
# Authentication Errors
# ============================================================================


class AuthenticationError(McpProxyError):
    """Base exception for authentication errors."""
    pass


class DcrError(AuthenticationError):
    """Raised when OAuth Dynamic Client Registration fails."""
    pass


class TokenError(AuthenticationError):
    """Raised when token operations fail."""
    pass


class TokenExpiredError(TokenError):
    """Raised when access token has expired."""
    pass


class TokenRefreshError(TokenError):
    """Raised when token refresh fails."""
    pass


class InvalidCredentialsError(AuthenticationError):
    """Raised when client credentials are invalid."""
    pass


# ============================================================================
# Network and Connection Errors
# ============================================================================


class NetworkError(McpProxyError):
    """Base exception for network-related errors."""
    pass


class ConnectionError(NetworkError):
    """Raised when connection to backend server fails."""
    pass


class ConnectionTimeoutError(NetworkError):
    """Raised when connection times out."""
    pass


class StreamError(NetworkError):
    """Raised when SSE stream encounters an error."""
    pass


class HttpError(NetworkError):
    """Raised when HTTP request fails."""
    
    def __init__(self, status_code: int, message: str, details: Optional[Any] = None):
        super().__init__(f"HTTP {status_code}: {message}", details)
        self.status_code = status_code


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


class ConfigurationError(McpProxyError):
    """Base exception for configuration errors."""
    pass


class InvalidConfigError(ConfigurationError):
    """Raised when configuration is invalid."""
    pass


class MissingConfigError(ConfigurationError):
    """Raised when required configuration is missing."""
    
    def __init__(self, parameter: str, details: Optional[Any] = None):
        super().__init__(f"Missing required configuration: {parameter}", details)
        self.parameter = parameter


# ============================================================================
# Utility Functions
# ============================================================================


def to_jsonrpc_error(error: Exception, default_code: int = -32603) -> dict:
    """Convert exception to JSON-RPC error format.
    
    Args:
        error: Exception to convert
        default_code: Default error code if not specified
        
    Returns:
        JSON-RPC error dictionary
    """
    # Map exception types to JSON-RPC error codes
    error_code_map = {
        InvalidJsonRpcError: -32700,  # Parse error
        UnsupportedMethodError: -32601,  # Method not found
        InvalidConfigError: -32602,  # Invalid params
        AuthenticationError: -32000,  # Server error (custom)
        NetworkError: -32001,  # Server error (custom)
        ConnectionError: -32002,  # Server error (custom)
    }
    
    # Determine error code
    code = default_code
    for exc_type, exc_code in error_code_map.items():
        if isinstance(error, exc_type):
            code = exc_code
            break
    
    # Build error object
    error_obj = {
        "code": code,
        "message": str(error),
    }
    
    # Add details if available
    if isinstance(error, McpProxyError) and error.details:
        error_obj["data"] = error.details
    
    return error_obj
