"""
Base Exception Classes

Foundation exceptions for the Nexa platform.
"""

from typing import Any, Optional, List, Dict


class NexaException(Exception):
    """
    Base exception for all Nexa platform errors.
    
    Attributes:
        message: Human-readable error message
        code: Machine-readable error code
        details: Additional error details
        http_status: Suggested HTTP status code
    """

    message: str = "An unexpected error occurred"
    code: str = "NEXA_ERROR"
    http_status: int = 500

    def __init__(
        self,
        message: Optional[str] = None,
        code: Optional[str] = None,
        details: Optional[Dict[str, Any]] = None,
        http_status: Optional[int] = None,
    ):
        self.message = message or self.__class__.message
        self.code = code or self.__class__.code
        self.details = details or {}
        self.http_status = http_status or self.__class__.http_status
        super().__init__(self.message)

    def to_dict(self) -> Dict[str, Any]:
        """Convert exception to dictionary for API responses."""
        return {
            "error": {
                "code": self.code,
                "message": self.message,
                "details": self.details,
            }
        }

    def __str__(self) -> str:
        return f"[{self.code}] {self.message}"

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}(code='{self.code}', message='{self.message}')"


class ValidationException(NexaException):
    """
    Raised when input validation fails.
    
    Attributes:
        errors: List of validation errors by field
    """

    message = "Validation failed"
    code = "VALIDATION_ERROR"
    http_status = 400

    def __init__(
        self,
        message: Optional[str] = None,
        errors: Optional[Dict[str, List[str]]] = None,
        **kwargs,
    ):
        self.errors = errors or {}
        super().__init__(message=message, details={"errors": self.errors}, **kwargs)

    def add_error(self, field: str, message: str) -> None:
        """Add a validation error for a field."""
        if field not in self.errors:
            self.errors[field] = []
        self.errors[field].append(message)
        self.details["errors"] = self.errors


class AuthenticationException(NexaException):
    """Raised when authentication fails."""

    message = "Authentication required"
    code = "AUTHENTICATION_ERROR"
    http_status = 401


class AuthorizationException(NexaException):
    """Raised when user lacks required permissions."""

    message = "Permission denied"
    code = "AUTHORIZATION_ERROR"
    http_status = 403


class NotFoundException(NexaException):
    """Raised when a requested resource is not found."""

    message = "Resource not found"
    code = "NOT_FOUND"
    http_status = 404

    def __init__(
        self,
        resource: str = "Resource",
        identifier: Optional[str] = None,
        **kwargs,
    ):
        message = f"{resource} not found"
        if identifier:
            message = f"{resource} with identifier '{identifier}' not found"
        details = {"resource": resource}
        if identifier:
            details["identifier"] = identifier
        super().__init__(message=message, details=details, **kwargs)


class ConflictException(NexaException):
    """Raised when there's a conflict with existing data."""

    message = "Resource conflict"
    code = "CONFLICT"
    http_status = 409


class RateLimitException(NexaException):
    """Raised when rate limit is exceeded."""

    message = "Rate limit exceeded"
    code = "RATE_LIMIT_EXCEEDED"
    http_status = 429

    def __init__(
        self,
        retry_after: Optional[int] = None,
        **kwargs,
    ):
        details = {}
        if retry_after:
            details["retry_after"] = retry_after
        super().__init__(details=details, **kwargs)
