from typing import Optional
from fastapi import Request, HTTPException
from fastapi.responses import JSONResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

from mineru_flow.internal.common.logging import get_logger


class WorkerException(Exception):
    """Base exception for worker-related failures."""

    def __init__(
        self, message: str, phase: Optional[str] = None, job_id: Optional[int] = None
    ):
        self.message = message
        self.phase = phase
        self.job_id = job_id
        super().__init__(message)


class TaskProcessingError(WorkerException):
    """Raised when a task cannot be processed successfully."""

    pass


class PhaseExecutionError(WorkerException):
    """Raised when a pipeline phase fails to execute."""

    pass


class DatabaseError(Exception):
    """Raised when a database operation fails."""

    def __init__(self, message: str, operation: Optional[str] = None):
        self.message = message
        self.operation = operation
        super().__init__(message)


class ConfigurationError(Exception):
    """Raised when configuration validation fails."""

    pass


class ValidationError(Exception):
    """Raised when request data fails validation."""

    def __init__(self, message: str, field: Optional[str] = None):
        self.message = message
        self.field = field
        super().__init__(message)


exception_logger = get_logger("exceptions")


async def worker_exception_handler(request: Request, exc: WorkerException):
    exception_logger.error(
        "Worker exception encountered",
        path=request.url.path,
        error_message=exc.message,
        phase=exc.phase,
        job_id=exc.job_id,
    )
    return JSONResponse(
        status_code=500,
        content={
            "error": "worker_error",
            "message": exc.message,
            "phase": exc.phase,
            "job_id": exc.job_id,
        },
    )


async def database_exception_handler(request: Request, exc: DatabaseError):
    exception_logger.error(
        "Database exception encountered",
        path=request.url.path,
        error_message=exc.message,
        operation=exc.operation,
    )
    return JSONResponse(
        status_code=500,
        content={
            "error": "database_error",
            "message": "Database operation failed",
            "operation": exc.operation,
        },
    )


async def validation_exception_handler(request: Request, exc: ValidationError):
    exception_logger.warning(
        "Validation error encountered",
        path=request.url.path,
        detail=exc.message,
        field=exc.field,
    )
    return JSONResponse(
        status_code=400,
        content={
            "error": "validation_error",
            "message": exc.message,
            "field": exc.field,
        },
    )


async def http_exception_handler(request: Request, exp: StarletteHTTPException):
    detail = getattr(exp, "detail", str(exp))
    status_code = getattr(exp, "status_code", 500)

    exception_logger.error(
        "HTTP exception encountered",
        path=request.url.path,
        status_code=status_code,
        detail=detail,
    )

    return JSONResponse(
        status_code=status_code,
        content={
            "err_code": 10000,
            "detail": detail,
        },
    )


async def general_exception_handler(request: Request, exc: Exception):
    exception_logger.exception(
        "Unhandled exception encountered",
        exception=exc,
        path=request.url.path,
        error=str(exc),
    )
    return JSONResponse(
        status_code=500,
        content={"error": "internal_server_error", "message": "Internal server error"},
    )


def add_exception_handler(app):
    app.add_exception_handler(WorkerException, worker_exception_handler)
    app.add_exception_handler(DatabaseError, database_exception_handler)
    app.add_exception_handler(ValidationError, validation_exception_handler)
    app.add_exception_handler(HTTPException, http_exception_handler)
    app.add_exception_handler(Exception, general_exception_handler)
