"""
Custom exceptions for the RAG system.

This module defines all custom exceptions used throughout the RAG system
to provide better error handling and debugging capabilities.
"""

from typing import Any, Optional


class RAGSystemError(Exception):
    """Base exception for all RAG system errors."""
    def __init__(self, message: str, details: Optional[dict] = None):
        super().__init__(message)
        self.message = message
        self.details = details or {}


class ConfigurationError(RAGSystemError):
    """Raised when there's a configuration-related error."""
    pass


class ModelError(RAGSystemError):
    """Raised when there's an error with language model operations."""
    pass


class VectorStoreError(RAGSystemError):
    """Raised when there's an error with vector store operations."""
    pass


class DocumentProcessingError(RAGSystemError):
    """Raised when there's an error processing documents."""
    pass


class SearchError(RAGSystemError):
    """Raised when there's an error during search operations."""
    pass


class ValidationError(RAGSystemError):
    """Raised when input validation fails."""
    pass


class ResourceError(RAGSystemError):
    """Raised when there are resource-related errors (memory, disk space, etc.)."""
    pass


class SecurityError(RAGSystemError):
    """Raised when there are security-related errors."""
    pass


class APILimitError(RAGSystemError):
    """Raised when API rate limits are exceeded."""
    pass


# Specific error classes for better error handling

class ModelLoadError(ModelError):
    """Raised when model loading fails."""
    def __init__(self, model_name: str, details: Optional[dict] = None):
        message = f"Failed to load model: {model_name}"
        super().__init__(message, details)


class EmbeddingError(ModelError):
    """Raised when embedding generation fails."""
    def __init__(self, text_preview: str, details: Optional[dict] = None):
        message = f"Failed to generate embeddings for text: {text_preview[:100]}..."
        super().__init__(message, details)


class IndexLoadError(VectorStoreError):
    """Raised when loading FAISS index fails."""
    def __init__(self, index_path: str, details: Optional[dict] = None):
        message = f"Failed to load FAISS index from: {index_path}"
        super().__init__(message, details)


class IndexSaveError(VectorStoreError):
    """Raised when saving FAISS index fails."""
    def __init__(self, index_path: str, details: Optional[dict] = None):
        message = f"Failed to save FAISS index to: {index_path}"
        super().__init__(message, details)


class PDFParseError(DocumentProcessingError):
    """Raised when PDF parsing fails."""
    def __init__(self, file_path: str, details: Optional[dict] = None):
        message = f"Failed to parse PDF file: {file_path}"
        super().__init__(message, details)


class ChunkingError(DocumentProcessingError):
    """Raised when document chunking fails."""
    def __init__(self, details: Optional[dict] = None):
        message = "Failed to chunk document"
        super().__init__(message, details)


class InvalidQueryError(ValidationError):
    """Raised when query validation fails."""
    def __init__(self, query: str, reason: str, details: Optional[dict] = None):
        message = f"Invalid query '{query[:50]}...': {reason}"
        super().__init__(message, details)


class FileUploadError(ValidationError):
    """Raised when file upload validation fails."""
    def __init__(self, filename: str, reason: str, details: Optional[dict] = None):
        message = f"File upload failed for '{filename}': {reason}"
        super().__init__(message, details)


class MemoryLimitError(ResourceError):
    """Raised when memory limits are exceeded."""
    def __init__(self, current_usage: int, limit: int, details: Optional[dict] = None):
        message = f"Memory limit exceeded: {current_usage}MB used, {limit}MB limit"
        super().__init__(message, details)


class DiskSpaceError(ResourceError):
    """Raised when disk space is insufficient."""
    def __init__(self, required_space: int, available_space: int, details: Optional[dict] = None):
        message = f"Insufficient disk space: {required_space}MB required, {available_space}MB available"
        super().__init__(message, details)

