"""Base error classes for the Trax platform.

This module defines the error hierarchy and base classes for all
application-specific exceptions.
"""

import traceback
from datetime import datetime, timezone
from typing import Any, Dict, Optional

from .codes import ErrorCode, ErrorCategory, ErrorSeverity


class TraxError(Exception):
    """Base exception class for all Trax platform errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message)
        self.message = message
        self.error_code = error_code
        self.context = context or {}
        self.original_error = original_error
        self.timestamp = datetime.now(timezone.utc)
        self.traceback = traceback.format_exc()
    
    @property
    def is_retryable(self) -> bool:
        """Check if this error is retryable."""
        return self.error_code.retryable if self.error_code else False
    
    @property
    def severity(self) -> ErrorSeverity:
        """Get the error severity."""
        return self.error_code.severity if self.error_code else ErrorSeverity.MEDIUM
    
    @property
    def category(self) -> ErrorCategory:
        """Get the error category."""
        return self.error_code.category if self.error_code else ErrorCategory.PROCESSING
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert error to dictionary representation."""
        return {
            "error_type": self.__class__.__name__,
            "message": self.message,
            "error_code": str(self.error_code) if self.error_code else None,
            "category": self.category.value,
            "severity": self.severity.value,
            "retryable": self.is_retryable,
            "context": self.context,
            "timestamp": self.timestamp.isoformat(),
            "traceback": self.traceback,
            "original_error": str(self.original_error) if self.original_error else None
        }
    
    def __str__(self) -> str:
        if self.error_code:
            return f"{self.error_code.code}: {self.message}"
        return self.message
    
    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(message='{self.message}', error_code={self.error_code})"


# Network Errors
class NetworkError(TraxError):
    """Base class for network-related errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message, error_code, context, original_error)


class ConnectionError(NetworkError):
    """Error raised when network connection fails."""
    pass


class TimeoutError(NetworkError):
    """Error raised when network request times out."""
    pass


class DNSResolutionError(NetworkError):
    """Error raised when DNS resolution fails."""
    pass


# API Errors
class APIError(TraxError):
    """Base class for API-related errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message, error_code, context, original_error)


class AuthenticationError(APIError):
    """Error raised when API authentication fails."""
    pass


class RateLimitError(APIError):
    """Error raised when API rate limit is exceeded."""
    pass


class QuotaExceededError(APIError):
    """Error raised when API quota is exceeded."""
    pass


class ServiceUnavailableError(APIError):
    """Error raised when API service is unavailable."""
    pass


class InvalidResponseError(APIError):
    """Error raised when API returns invalid response."""
    pass


# File System Errors
class FileSystemError(TraxError):
    """Base class for file system-related errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message, error_code, context, original_error)


class FileNotFoundError(FileSystemError):
    """Error raised when a file is not found."""
    pass


class PermissionError(FileSystemError):
    """Error raised when file permission is denied."""
    pass


class DiskSpaceError(FileSystemError):
    """Error raised when disk space is insufficient."""
    pass


class CorruptedFileError(FileSystemError):
    """Error raised when a file is corrupted."""
    pass


# Validation Errors
class ValidationError(TraxError):
    """Base class for validation-related errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message, error_code, context, original_error)


class InvalidInputError(ValidationError):
    """Error raised when input validation fails."""
    pass


class MissingRequiredFieldError(ValidationError):
    """Error raised when a required field is missing."""
    pass


class FormatError(ValidationError):
    """Error raised when data format is invalid."""
    pass


# Processing Errors
class ProcessingError(TraxError):
    """Base class for processing-related errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message, error_code, context, original_error)


class TranscriptionError(ProcessingError):
    """Error raised when transcription processing fails."""
    pass


class EnhancementError(ProcessingError):
    """Error raised when text enhancement fails."""
    pass


class MediaProcessingError(ProcessingError):
    """Error raised when media processing fails."""
    pass


class AudioConversionError(ProcessingError):
    """Error raised when audio conversion fails."""
    pass


# Configuration Errors
class ConfigurationError(TraxError):
    """Base class for configuration-related errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message, error_code, context, original_error)


class MissingConfigError(ConfigurationError):
    """Error raised when required configuration is missing."""
    pass


class InvalidConfigError(ConfigurationError):
    """Error raised when configuration is invalid."""
    pass


class EnvironmentError(ConfigurationError):
    """Error raised when environment configuration is invalid."""
    pass


# Resource Errors
class ResourceError(TraxError):
    """Base class for resource-related errors."""
    
    def __init__(
        self,
        message: str,
        error_code: Optional[ErrorCode] = None,
        context: Optional[Dict[str, Any]] = None,
        original_error: Optional[Exception] = None
    ):
        super().__init__(message, error_code, context, original_error)


class MemoryError(ResourceError):
    """Error raised when memory is insufficient."""
    pass


class CPUError(ResourceError):
    """Error raised when CPU is overloaded."""
    pass


# Utility functions for error creation
def create_network_error(
    message: str,
    error_code: Optional[ErrorCode] = None,
    context: Optional[Dict[str, Any]] = None,
    original_error: Optional[Exception] = None
) -> NetworkError:
    """Create a network error with appropriate classification."""
    if "timeout" in message.lower():
        return TimeoutError(message, error_code, context, original_error)
    elif "dns" in message.lower() or "resolve" in message.lower():
        return DNSResolutionError(message, error_code, context, original_error)
    else:
        return ConnectionError(message, error_code, context, original_error)


def create_api_error(
    message: str,
    error_code: Optional[ErrorCode] = None,
    context: Optional[Dict[str, Any]] = None,
    original_error: Optional[Exception] = None
) -> APIError:
    """Create an API error with appropriate classification."""
    message_lower = message.lower()
    
    if "auth" in message_lower or "unauthorized" in message_lower:
        return AuthenticationError(message, error_code, context, original_error)
    elif "rate limit" in message_lower or "too many requests" in message_lower:
        return RateLimitError(message, error_code, context, original_error)
    elif "quota" in message_lower or "limit exceeded" in message_lower:
        return QuotaExceededError(message, error_code, context, original_error)
    elif "unavailable" in message_lower or "service" in message_lower:
        return ServiceUnavailableError(message, error_code, context, original_error)
    elif "invalid" in message_lower or "response" in message_lower:
        return InvalidResponseError(message, error_code, context, original_error)
    else:
        return APIError(message, error_code, context, original_error)


def create_filesystem_error(
    message: str,
    error_code: Optional[ErrorCode] = None,
    context: Optional[Dict[str, Any]] = None,
    original_error: Optional[Exception] = None
) -> FileSystemError:
    """Create a file system error with appropriate classification."""
    message_lower = message.lower()
    
    if "not found" in message_lower or "no such file" in message_lower:
        return FileNotFoundError(message, error_code, context, original_error)
    elif "permission" in message_lower or "denied" in message_lower:
        return PermissionError(message, error_code, context, original_error)
    elif "disk space" in message_lower or "no space" in message_lower:
        return DiskSpaceError(message, error_code, context, original_error)
    elif "corrupt" in message_lower or "invalid" in message_lower:
        return CorruptedFileError(message, error_code, context, original_error)
    else:
        return FileSystemError(message, error_code, context, original_error)


def create_validation_error(
    message: str,
    error_code: Optional[ErrorCode] = None,
    context: Optional[Dict[str, Any]] = None,
    original_error: Optional[Exception] = None
) -> ValidationError:
    """Create a validation error with appropriate classification."""
    message_lower = message.lower()
    
    if "required" in message_lower or "missing" in message_lower:
        return MissingRequiredFieldError(message, error_code, context, original_error)
    elif "format" in message_lower or "invalid format" in message_lower:
        return FormatError(message, error_code, context, original_error)
    else:
        return InvalidInputError(message, error_code, context, original_error)
