"""
Custom exception classes for ThrillWiki.
Provides domain-specific exceptions with proper error codes and messages.
"""

from typing import Any


class ThrillWikiException(Exception):
    """Base exception for all ThrillWiki-specific errors."""

    default_message = "An error occurred"
    error_code = "THRILLWIKI_ERROR"
    status_code = 500

    def __init__(
        self,
        message: str | None = None,
        error_code: str | None = None,
        details: dict[str, Any] | None = None,
    ):
        self.message = message or self.default_message
        self.error_code = error_code or self.error_code
        self.details = details or {}
        super().__init__(self.message)

    def to_dict(self) -> dict[str, Any]:
        """Convert exception to dictionary for API responses."""
        return {
            "error_code": self.error_code,
            "message": self.message,
            "details": self.details,
        }


class ValidationException(ThrillWikiException):
    """Raised when data validation fails."""

    default_message = "Validation failed"
    error_code = "VALIDATION_ERROR"
    status_code = 400


class NotFoundError(ThrillWikiException):
    """Raised when a requested resource is not found."""

    default_message = "Resource not found"
    error_code = "NOT_FOUND"
    status_code = 404


class PermissionDeniedError(ThrillWikiException):
    """Raised when user lacks permission for an operation."""

    default_message = "Permission denied"
    error_code = "PERMISSION_DENIED"
    status_code = 403


class BusinessLogicError(ThrillWikiException):
    """Raised when business logic constraints are violated."""

    default_message = "Business logic violation"
    error_code = "BUSINESS_LOGIC_ERROR"
    status_code = 400


class ServiceError(ThrillWikiException):
    """Raised when a service operation fails."""

    default_message = "Service operation failed"
    error_code = "SERVICE_ERROR"
    status_code = 500


class ExternalServiceError(ThrillWikiException):
    """Raised when external service calls fail."""

    default_message = "External service error"
    error_code = "EXTERNAL_SERVICE_ERROR"
    status_code = 502


# Domain-specific exceptions


class ParkError(ThrillWikiException):
    """Base exception for park-related errors."""

    error_code = "PARK_ERROR"


class ParkNotFoundError(NotFoundError):
    """Raised when a park is not found."""

    default_message = "Park not found"
    error_code = "PARK_NOT_FOUND"

    def __init__(self, park_slug: str | None = None, **kwargs):
        if park_slug:
            kwargs["details"] = {"park_slug": park_slug}
            kwargs["message"] = f"Park with slug '{park_slug}' not found"
        super().__init__(**kwargs)


class ParkOperationError(BusinessLogicError):
    """Raised when park operation constraints are violated."""

    default_message = "Invalid park operation"
    error_code = "PARK_OPERATION_ERROR"


class RideError(ThrillWikiException):
    """Base exception for ride-related errors."""

    error_code = "RIDE_ERROR"


class RideNotFoundError(NotFoundError):
    """Raised when a ride is not found."""

    default_message = "Ride not found"
    error_code = "RIDE_NOT_FOUND"

    def __init__(self, ride_slug: str | None = None, **kwargs):
        if ride_slug:
            kwargs["details"] = {"ride_slug": ride_slug}
            kwargs["message"] = f"Ride with slug '{ride_slug}' not found"
        super().__init__(**kwargs)


class RideOperationError(BusinessLogicError):
    """Raised when ride operation constraints are violated."""

    default_message = "Invalid ride operation"
    error_code = "RIDE_OPERATION_ERROR"


class LocationError(ThrillWikiException):
    """Base exception for location-related errors."""

    error_code = "LOCATION_ERROR"


class InvalidCoordinatesError(ValidationException):
    """Raised when geographic coordinates are invalid."""

    default_message = "Invalid geographic coordinates"
    error_code = "INVALID_COORDINATES"

    def __init__(
        self,
        latitude: float | None = None,
        longitude: float | None = None,
        **kwargs,
    ):
        if latitude is not None or longitude is not None:
            kwargs["details"] = {"latitude": latitude, "longitude": longitude}
        super().__init__(**kwargs)


class GeolocationError(ExternalServiceError):
    """Raised when geolocation services fail."""

    default_message = "Geolocation service unavailable"
    error_code = "GEOLOCATION_ERROR"


class ReviewError(ThrillWikiException):
    """Base exception for review-related errors."""

    error_code = "REVIEW_ERROR"


class ReviewModerationError(BusinessLogicError):
    """Raised when review moderation constraints are violated."""

    default_message = "Review moderation error"
    error_code = "REVIEW_MODERATION_ERROR"


class DuplicateReviewError(BusinessLogicError):
    """Raised when user tries to create duplicate reviews."""

    default_message = "User has already reviewed this item"
    error_code = "DUPLICATE_REVIEW"


class AccountError(ThrillWikiException):
    """Base exception for account-related errors."""

    error_code = "ACCOUNT_ERROR"


class InsufficientPermissionsError(PermissionDeniedError):
    """Raised when user lacks required permissions."""

    default_message = "Insufficient permissions"
    error_code = "INSUFFICIENT_PERMISSIONS"

    def __init__(self, required_permission: str | None = None, **kwargs):
        if required_permission:
            kwargs["details"] = {"required_permission": required_permission}
            kwargs["message"] = f"Permission '{required_permission}' required"
        super().__init__(**kwargs)


class EmailError(ExternalServiceError):
    """Raised when email operations fail."""

    default_message = "Email service error"
    error_code = "EMAIL_ERROR"


class CacheError(ThrillWikiException):
    """Raised when cache operations fail."""

    default_message = "Cache operation failed"
    error_code = "CACHE_ERROR"
    status_code = 500


class RoadTripError(ExternalServiceError):
    """Raised when road trip planning fails."""

    default_message = "Road trip planning error"
    error_code = "ROADTRIP_ERROR"

    def __init__(self, service_name: str | None = None, **kwargs):
        if service_name:
            kwargs["details"] = {"service": service_name}
        super().__init__(**kwargs)
