from common_logging import get_logger

logger = get_logger(__name__)


class AppException(Exception):

    def __init__(self, message: str, code: str, status_code: int = 400):
        self.message = message
        self.code = code
        self.status_code = status_code
        super().__init__(self.message)
        logger.error(f"Exception created: {code} - {message}", error_code=code)


class KnowledgeBaseNotFoundError(AppException):

    def __init__(self, base_id: int):
        super().__init__(
            message=f"Knowledge base {base_id} not found",
            code="KNOWLEDGE_BASE_NOT_FOUND",
            status_code=404,
        )


class DocumentNotFoundError(AppException):

    def __init__(self, doc_id: int):
        super().__init__(
            message=f"Document {doc_id} not found", code="DOCUMENT_NOT_FOUND", status_code=404
        )


class CategoryNotFoundError(AppException):

    def __init__(self, category_id: int):
        super().__init__(
            message=f"Category {category_id} not found", code="CATEGORY_NOT_FOUND", status_code=404
        )


class TagNotFoundError(AppException):

    def __init__(self, tag_id: int):
        super().__init__(message=f"Tag {tag_id} not found", code="TAG_NOT_FOUND", status_code=404)


class QANotFoundError(AppException):

    def __init__(self, qa_id: int):
        super().__init__(message=f"Q&A {qa_id} not found", code="QA_NOT_FOUND", status_code=404)


class VectorSearchError(AppException):

    def __init__(self, reason: str = "Vector search failed"):
        super().__init__(message=reason, code="VECTOR_SEARCH_ERROR", status_code=500)


class VectorStoreConnectionError(AppException):

    def __init__(self):
        super().__init__(
            message="Failed to connect to vector store",
            code="VECTOR_STORE_CONNECTION_ERROR",
            status_code=503,
        )


class EmbeddingGenerationError(AppException):

    def __init__(self, reason: str = "Failed to generate embeddings"):
        super().__init__(message=reason, code="EMBEDDING_GENERATION_ERROR", status_code=500)


class FileProcessingError(AppException):

    def __init__(self, filename: str, reason: str):
        super().__init__(
            message=f"Failed to process file '{filename}': {reason}",
            code="FILE_PROCESSING_ERROR",
            status_code=400,
        )


class UnsupportedFileTypeError(AppException):

    def __init__(self, file_type: str):
        super().__init__(
            message=f"Unsupported file type: {file_type}",
            code="UNSUPPORTED_FILE_TYPE",
            status_code=400,
        )


class ProviderNotFoundError(AppException):

    def __init__(self, provider_id: int):
        super().__init__(
            message=f"Provider {provider_id} not found", code="PROVIDER_NOT_FOUND", status_code=404
        )


class ModelNotFoundError(AppException):

    def __init__(self, model_id: int):
        super().__init__(
            message=f"Model {model_id} not found", code="MODEL_NOT_FOUND", status_code=404
        )


class ProviderNotConfiguredError(AppException):

    def __init__(self, provider_name: str):
        super().__init__(
            message=f"Provider '{provider_name}' is not configured",
            code="PROVIDER_NOT_CONFIGURED",
            status_code=400,
        )


class InsufficientPermissionError(AppException):

    def __init__(self, resource: str, action: str):
        super().__init__(
            message=f"Insufficient permission to {action} {resource}",
            code="INSUFFICIENT_PERMISSION",
            status_code=403,
        )


class CircularReferenceError(AppException):

    def __init__(self, resource_type: str):
        super().__init__(
            message=f"Circular reference detected in {resource_type}",
            code="CIRCULAR_REFERENCE",
            status_code=400,
        )


class InvalidParentError(AppException):

    def __init__(self, resource_type: str):
        super().__init__(
            message=f"Invalid parent for {resource_type}", code="INVALID_PARENT", status_code=400
        )


class TextChunkingError(AppException):

    def __init__(self, reason: str = "Text chunking failed"):
        super().__init__(message=reason, code="TEXT_CHUNKING_ERROR", status_code=500)


class DocumentUploadError(AppException):

    def __init__(self, filename: str, reason: str):
        super().__init__(
            message=f"Failed to upload document '{filename}': {reason}",
            code="DOCUMENT_UPLOAD_ERROR",
            status_code=400,
        )


class DatabaseError(AppException):

    def __init__(self, message: str = "Database operation failed"):
        super().__init__(message=message, code="DATABASE_ERROR", status_code=500)


class TransactionError(DatabaseError):

    def __init__(self, message: str = "Transaction failed"):
        super().__init__(message=message)
        self.code = "TRANSACTION_ERROR"


class ConstraintViolationError(DatabaseError):

    def __init__(self, constraint: str = "unknown"):
        super().__init__(message=f"Database constraint violation: {constraint}")
        self.code = "CONSTRAINT_VIOLATION"
        self.status_code = 409


class ExternalServiceError(AppException):

    def __init__(self, service: str, message: str = "External service error"):
        super().__init__(
            message=f"{service}: {message}", code="EXTERNAL_SERVICE_ERROR", status_code=502
        )


class ServiceTimeoutError(ExternalServiceError):

    def __init__(self, service: str, timeout: float):
        super().__init__(service=service, message=f"Request timeout after {timeout}s")
        self.code = "SERVICE_TIMEOUT"
        self.status_code = 504


class ServiceUnavailableError(ExternalServiceError):

    def __init__(self, service: str):
        super().__init__(service=service, message="Service unavailable")
        self.code = "SERVICE_UNAVAILABLE"
        self.status_code = 503


class AuthenticationError(AppException):

    def __init__(self, message: str = "Authentication failed"):
        super().__init__(message=message, code="AUTHENTICATION_ERROR", status_code=401)


class TokenExpiredError(AuthenticationError):

    def __init__(self):
        super().__init__(message="Token has expired")
        self.code = "TOKEN_EXPIRED"


class InvalidTokenError(AuthenticationError):

    def __init__(self):
        super().__init__(message="Invalid token")
        self.code = "INVALID_TOKEN"


class RateLimitError(AppException):

    def __init__(self, retry_after: int = 60):
        super().__init__(
            message=f"Rate limit exceeded. Retry after {retry_after} seconds",
            code="RATE_LIMIT_EXCEEDED",
            status_code=429,
        )
        self.retry_after = retry_after


class StorageError(AppException):

    def __init__(self, message: str, bucket: str = None):
        msg = (
            f"Storage error in bucket '{bucket}': {message}"
            if bucket
            else f"Storage error: {message}"
        )
        super().__init__(message=msg, code="STORAGE_ERROR", status_code=500)
