"""
Error handling middleware for ClaudeTools API.

This module provides custom exception classes and global exception handlers
for consistent error responses across the FastAPI application.
"""

from typing import Any, Dict, Optional

from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from sqlalchemy.exc import DisconnectionError, OperationalError, SQLAlchemyError


class ClaudeToolsException(Exception):
    """Base exception class for ClaudeTools application."""

    def __init__(
        self,
        message: str,
        status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR,
        details: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize the exception.

        Args:
            message: Human-readable error message
            status_code: HTTP status code for the error
            details: Optional dictionary with additional error details
        """
        self.message = message
        self.status_code = status_code
        self.details = details or {}
        super().__init__(self.message)


class AuthenticationError(ClaudeToolsException):
    """
    Exception raised for authentication failures.

    This includes invalid credentials, expired tokens, or missing authentication.
    """

    def __init__(
        self, message: str = "Authentication failed", details: Optional[Dict[str, Any]] = None
    ):
        """
        Initialize authentication error.

        Args:
            message: Error message
            details: Optional additional details
        """
        super().__init__(
            message=message,
            status_code=status.HTTP_401_UNAUTHORIZED,
            details=details,
        )


class AuthorizationError(ClaudeToolsException):
    """
    Exception raised for authorization failures.

    This occurs when an authenticated user lacks permission for an action.
    """

    def __init__(
        self, message: str = "Insufficient permissions", details: Optional[Dict[str, Any]] = None
    ):
        """
        Initialize authorization error.

        Args:
            message: Error message
            details: Optional additional details
        """
        super().__init__(
            message=message,
            status_code=status.HTTP_403_FORBIDDEN,
            details=details,
        )


class NotFoundError(ClaudeToolsException):
    """
    Exception raised when a requested resource is not found.

    This should be used for missing users, organizations, tools, etc.
    """

    def __init__(
        self,
        message: str = "Resource not found",
        resource_type: Optional[str] = None,
        resource_id: Optional[str] = None,
    ):
        """
        Initialize not found error.

        Args:
            message: Error message
            resource_type: Optional type of resource (e.g., "User", "Tool")
            resource_id: Optional ID of the missing resource
        """
        details = {}
        if resource_type:
            details["resource_type"] = resource_type
        if resource_id:
            details["resource_id"] = resource_id

        super().__init__(
            message=message,
            status_code=status.HTTP_404_NOT_FOUND,
            details=details,
        )


class ValidationError(ClaudeToolsException):
    """
    Exception raised for business logic validation failures.

    This is separate from FastAPI's RequestValidationError and should be used
    for application-level validation (e.g., duplicate usernames, invalid state transitions).
    """

    def __init__(
        self,
        message: str = "Validation failed",
        field: Optional[str] = None,
        details: Optional[Dict[str, Any]] = None,
    ):
        """
        Initialize validation error.

        Args:
            message: Error message
            field: Optional field name that failed validation
            details: Optional additional details
        """
        error_details = details or {}
        if field:
            error_details["field"] = field

        super().__init__(
            message=message,
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            details=error_details,
        )


class ConflictError(ClaudeToolsException):
    """
    Exception raised when a request conflicts with existing data.

    This includes duplicate entries, concurrent modifications, etc.
    """

    def __init__(
        self, message: str = "Resource conflict", details: Optional[Dict[str, Any]] = None
    ):
        """
        Initialize conflict error.

        Args:
            message: Error message
            details: Optional additional details
        """
        super().__init__(
            message=message,
            status_code=status.HTTP_409_CONFLICT,
            details=details,
        )


class DatabaseError(ClaudeToolsException):
    """
    Exception raised for database operation failures.

    This wraps SQLAlchemy errors with a consistent interface.
    """

    def __init__(
        self, message: str = "Database operation failed", details: Optional[Dict[str, Any]] = None
    ):
        """
        Initialize database error.

        Args:
            message: Error message
            details: Optional additional details
        """
        super().__init__(
            message=message,
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            details=details,
        )


async def claudetools_exception_handler(
    request: Request, exc: ClaudeToolsException
) -> JSONResponse:
    """
    Handler for custom ClaudeTools exceptions.

    Args:
        request: The FastAPI request object
        exc: The ClaudeTools exception

    Returns:
        JSONResponse: Formatted error response
    """
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error": exc.message,
            "details": exc.details,
            "path": str(request.url.path),
        },
    )


async def validation_exception_handler(
    request: Request, exc: RequestValidationError
) -> JSONResponse:
    """
    Handler for FastAPI request validation errors.

    Args:
        request: The FastAPI request object
        exc: The validation error

    Returns:
        JSONResponse: Formatted error response
    """
    errors = []
    for error in exc.errors():
        errors.append(
            {
                "field": ".".join(str(loc) for loc in error["loc"]),
                "message": error["msg"],
                "type": error["type"],
            }
        )

    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content={
            "error": "Request validation failed",
            "details": {"validation_errors": errors},
            "path": str(request.url.path),
        },
    )


async def sqlalchemy_exception_handler(
    request: Request, exc: SQLAlchemyError
) -> JSONResponse:
    """
    Handler for SQLAlchemy database errors.

    Args:
        request: The FastAPI request object
        exc: The SQLAlchemy exception

    Returns:
        JSONResponse: Formatted error response
    """
    return JSONResponse(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        content={
            "error": "Database operation failed",
            "details": {"type": type(exc).__name__},
            "path": str(request.url.path),
        },
    )


async def db_unavailable_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    return JSONResponse(
        status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
        content={
            "error": "Database unavailable. Retry after 30 seconds.",
            "path": str(request.url.path),
        },
        headers={"Retry-After": "30"},
    )


async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    """
    Handler for unhandled exceptions.

    Args:
        request: The FastAPI request object
        exc: The exception

    Returns:
        JSONResponse: Formatted error response
    """
    return JSONResponse(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        content={
            "error": "Internal server error",
            "details": {"type": type(exc).__name__},
            "path": str(request.url.path),
        },
    )


def register_exception_handlers(app: FastAPI) -> None:
    """
    Register all exception handlers with the FastAPI application.

    This should be called during application startup to ensure all exceptions
    are handled consistently.

    Args:
        app: The FastAPI application instance

    Example:
        ```python
        from fastapi import FastAPI
        from api.middleware.error_handler import register_exception_handlers

        app = FastAPI()
        register_exception_handlers(app)
        ```
    """
    app.add_exception_handler(ClaudeToolsException, claudetools_exception_handler)
    app.add_exception_handler(RequestValidationError, validation_exception_handler)
    app.add_exception_handler(SQLAlchemyError, sqlalchemy_exception_handler)
    app.add_exception_handler(OperationalError, db_unavailable_exception_handler)
    app.add_exception_handler(DisconnectionError, db_unavailable_exception_handler)
    app.add_exception_handler(Exception, generic_exception_handler)
