"""Central error handling for Meta Ads MCP Server."""

from __future__ import annotations

from typing import Any, Callable, Dict, Optional, Type
import traceback

from .errors import (
    MCPError,
    MetaApiError,
    ValidationError,
    AuthenticationError,
    RateLimitError,
    ConfigurationError,
    TransportError,
    EngineError,
    CacheError,
    CircuitBreakerError,
    TimeoutError,
)
from .logging import get_logger
from .schemas import ToolResult

logger = get_logger(__name__)


ErrorHandlerFunc = Callable[[Exception], ToolResult]


class ErrorHandler:
    """Central error handling registry.
    
    Manages error handlers for different exception types and provides
    consistent error response formatting.
    """
    
    def __init__(self) -> None:
        """Initialize the error handler."""
        self._handlers: Dict[Type[Exception], ErrorHandlerFunc] = {}
        self._register_default_handlers()
    
    def register(self, exc_type: Type[Exception], handler: ErrorHandlerFunc) -> None:
        """Register an error handler for an exception type.
        
        Args:
            exc_type: The exception type to handle
            handler: Function that converts exception to ToolResult
        """
        self._handlers[exc_type] = handler
        logger.info("error_handler_registered", exception_type=exc_type.__name__)
    
    async def handle(self, exc: Exception, context: Optional[Dict[str, Any]] = None) -> ToolResult:
        """Handle an exception and return a ToolResult.
        
        Args:
            exc: The exception to handle
            context: Additional context about the error
            
        Returns:
            ToolResult with error information
        """
        # Find the most specific handler
        for exc_type in type(exc).__mro__:
            if exc_type in self._handlers:
                try:
                    result = self._handlers[exc_type](exc)
                    # Add context if provided
                    if context and isinstance(result.error, dict):
                        result.error.update({"context": context})
                    return result
                except Exception as handler_exc:
                    logger.error(
                        "error_handler_failed",
                        original_error=str(exc),
                        handler_error=str(handler_exc),
                    )
                    break
        
        # Fall back to generic handler
        return self._handle_generic_error(exc, context)
    
    def _register_default_handlers(self) -> None:
        """Register default error handlers."""
        
        # MCP errors
        self.register(MCPError, self._handle_mcp_error)
        self.register(ValidationError, self._handle_validation_error)
        self.register(AuthenticationError, self._handle_authentication_error)
        self.register(RateLimitError, self._handle_rate_limit_error)
        self.register(ConfigurationError, self._handle_configuration_error)
        self.register(TransportError, self._handle_transport_error)
        self.register(EngineError, self._handle_engine_error)
        self.register(CacheError, self._handle_cache_error)
        self.register(CircuitBreakerError, self._handle_circuit_breaker_error)
        self.register(TimeoutError, self._handle_timeout_error)
        
        # Meta API errors
        self.register(MetaApiError, self._handle_meta_api_error)
        
        # Standard Python errors
        self.register(ValueError, self._handle_value_error)
        self.register(KeyError, self._handle_key_error)
        self.register(TypeError, self._handle_type_error)
    
    def _handle_mcp_error(self, exc: MCPError) -> ToolResult:
        """Handle MCPError."""
        logger.error(
            "mcp_error",
            code=exc.code,
            message=exc.message,
            details=exc.details,
        )
        return ToolResult(ok=False, error=exc.to_dict())
    
    def _handle_validation_error(self, exc: ValidationError) -> ToolResult:
        """Handle ValidationError."""
        logger.warning(
            "validation_error",
            message=exc.message,
            field=exc.details.get("field"),
        )
        return ToolResult(
            ok=False,
            error={
                "code": "VALIDATION_ERROR",
                "message": exc.message,
                "details": exc.details,
            },
        )
    
    def _handle_authentication_error(self, exc: AuthenticationError) -> ToolResult:
        """Handle AuthenticationError."""
        logger.error("authentication_error", message=exc.message)
        return ToolResult(
            ok=False,
            error={
                "code": "AUTHENTICATION_ERROR",
                "message": "Authentication failed. Please check your credentials.",
                "details": {"hint": "Verify META_ACCESS_TOKEN or OAuth configuration"},
            },
        )
    
    def _handle_rate_limit_error(self, exc: RateLimitError) -> ToolResult:
        """Handle RateLimitError."""
        retry_after = exc.details.get("retry_after")
        logger.warning(
            "rate_limit_error",
            message=exc.message,
            retry_after=retry_after,
        )
        return ToolResult(
            ok=False,
            error={
                "code": "RATE_LIMIT_ERROR",
                "message": exc.message,
                "retry_after": retry_after,
                "details": {"hint": "Wait before retrying the request"},
            },
        )
    
    def _handle_configuration_error(self, exc: ConfigurationError) -> ToolResult:
        """Handle ConfigurationError."""
        logger.error(
            "configuration_error",
            message=exc.message,
            setting=exc.details.get("setting"),
        )
        return ToolResult(
            ok=False,
            error={
                "code": "CONFIGURATION_ERROR",
                "message": exc.message,
                "details": exc.details,
            },
        )
    
    def _handle_transport_error(self, exc: TransportError) -> ToolResult:
        """Handle TransportError."""
        logger.error(
            "transport_error",
            message=exc.message,
            transport_type=exc.details.get("transport_type"),
        )
        return ToolResult(
            ok=False,
            error={
                "code": "TRANSPORT_ERROR",
                "message": "Transport layer error occurred",
                "details": {"hint": "Check server logs for details"},
            },
        )
    
    def _handle_engine_error(self, exc: EngineError) -> ToolResult:
        """Handle EngineError."""
        logger.error(
            "engine_error",
            message=exc.message,
            engine=exc.details.get("engine"),
            operation=exc.details.get("operation"),
        )
        return ToolResult(
            ok=False,
            error={
                "code": "ENGINE_ERROR",
                "message": exc.message,
                "details": exc.details,
            },
        )
    
    def _handle_cache_error(self, exc: CacheError) -> ToolResult:
        """Handle CacheError."""
        logger.warning(
            "cache_error",
            message=exc.message,
            operation=exc.details.get("operation"),
        )
        # Cache errors shouldn't fail the request - just log and continue
        return ToolResult(
            ok=True,
            data={
                "cache": "degraded",
                "message": "Cache operation failed; continued without cache",
                "details": {"hint": "Request may be slower than usual"},
            },
        )
    
    def _handle_circuit_breaker_error(self, exc: CircuitBreakerError) -> ToolResult:
        """Handle CircuitBreakerError."""
        logger.error(
            "circuit_breaker_open",
            message=exc.message,
            service=exc.details.get("service"),
        )
        return ToolResult(
            ok=False,
            error={
                "code": "SERVICE_UNAVAILABLE",
                "message": "Service temporarily unavailable",
                "details": {"hint": "The service is recovering, please try again later"},
            },
        )
    
    def _handle_timeout_error(self, exc: TimeoutError) -> ToolResult:
        """Handle TimeoutError."""
        logger.error(
            "timeout_error",
            message=exc.message,
            timeout=exc.details.get("timeout"),
        )
        return ToolResult(
            ok=False,
            error={
                "code": "TIMEOUT_ERROR",
                "message": "Operation timed out",
                "details": {"hint": "Try again or increase timeout settings"},
            },
        )
    
    def _handle_meta_api_error(self, exc: MetaApiError) -> ToolResult:
        """Handle MetaApiError."""
        logger.error("meta_api_error", **exc.to_dict())
        return ToolResult(
            ok=False,
            error={
                "code": f"META_API_ERROR_{exc.context.code}" if exc.context.code else "META_API_ERROR",
                "message": exc.context.message,
                "meta_error_type": exc.context.type,
                "meta_error_code": exc.context.code,
                "meta_error_subcode": exc.context.error_subcode,
                "fbtrace_id": exc.context.fbtrace_id,
                "status_code": exc.status_code,
            },
        )
    
    def _handle_value_error(self, exc: ValueError) -> ToolResult:
        """Handle ValueError."""
        logger.warning("value_error", message=str(exc))
        return ToolResult(
            ok=False,
            error={
                "code": "VALIDATION_ERROR",
                "message": str(exc),
            },
        )
    
    def _handle_key_error(self, exc: KeyError) -> ToolResult:
        """Handle KeyError."""
        logger.warning("key_error", message=str(exc))
        return ToolResult(
            ok=False,
            error={
                "code": "INVALID_PARAMETER",
                "message": f"Missing required parameter: {str(exc)}",
            },
        )
    
    def _handle_type_error(self, exc: TypeError) -> ToolResult:
        """Handle TypeError."""
        logger.warning("type_error", message=str(exc))
        return ToolResult(
            ok=False,
            error={
                "code": "INVALID_TYPE",
                "message": str(exc),
            },
        )
    
    def _handle_generic_error(self, exc: Exception, context: Optional[Dict[str, Any]] = None) -> ToolResult:
        """Handle any unhandled exception."""
        error_id = id(exc)  # Use object ID as error reference
        
        logger.error(
            "unhandled_error",
            error_id=error_id,
            error_type=type(exc).__name__,
            message=str(exc),
            traceback=traceback.format_exc(),
            context=context,
        )
        
        return ToolResult(
            ok=False,
            error={
                "code": "INTERNAL_ERROR",
                "message": "An unexpected error occurred",
                "error_id": error_id,
                "details": {
                    "hint": "Check server logs for details",
                    "error_type": type(exc).__name__,
                },
            },
        )


# Global error handler instance
_error_handler: Optional[ErrorHandler] = None


def get_error_handler() -> ErrorHandler:
    """Get the global error handler instance.
    
    Returns:
        ErrorHandler: The global error handler
    """
    global _error_handler
    if _error_handler is None:
        _error_handler = ErrorHandler()
    return _error_handler


def reset_error_handler() -> None:
    """Reset the global error handler (mainly for testing)."""
    global _error_handler
    _error_handler = None

