from collections.abc import Awaitable, Callable
from http import HTTPStatus
from typing import override

from .base import ResolverResult


class ActivityPubException(Exception):
    """Base exception for ActivityPub-related errors."""

    default_message: str = "Internal server error"
    message: str
    user_message: str | None = None
    status_code: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR

    def __init__(self, message: str, user_facing_message: str | None = None, status_code: HTTPStatus | None = None, *args: object) -> None:
        self.message = message
        if not user_facing_message:
            user_facing_message = self.default_message
        self.user_message = user_facing_message
        if status_code:
            self.status_code = status_code
        super().__init__(user_facing_message, *args)

    @override
    def __str__(self) -> str:
        return self.user_message or "Unknown APException"


def catch_exceptions(exception_class: type[ActivityPubException], message: str):
    def with_exception_handled[**Params, ReturnType](function: Callable[Params, Awaitable[ReturnType]]) -> Callable[Params, Awaitable[ReturnType]]:
        async def wrapper_method(*args_with_exception: Params.args, **kwargs_with_exception: Params.kwargs) -> ReturnType:
            try:
                return await function(*args_with_exception, **kwargs_with_exception)
            except ActivityPubException as e:
                e.message = f"{message}; args={args_with_exception}. {e.message}"
                e.user_message = message
                raise e
            except Exception as e:
                raise exception_class(
                    f"Exception {type(e).__name__}: {message}. Args: {args_with_exception}",
                    user_facing_message=message,
                )

        return wrapper_method

    return with_exception_handled


class InternalLoggingException(ActivityPubException):
    """An exception that should not be transmitted over the network.
    The message is not 'safe' to transmit and show publicly (may contain sensitive data)."""


class ValidationError(InternalLoggingException):
    """Raised when validation fails."""

    default_message: str = "Validation failed"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST


class InvalidURLError(ValidationError):
    """Raised when an invalid URL is provided."""

    default_message: str = "Wrong URL"
    status_code: HTTPStatus = HTTPStatus.BAD_GATEWAY


class DecodingError(ValidationError):
    """Raised when a string cannot be decoded to JSON or from bytes."""

    default_message: str = "Cannot decode"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST


class SignatureError(InternalLoggingException):
    """Raised when signature creation fails."""

    default_message: str = "Wrong signature"
    status_code: HTTPStatus = HTTPStatus.UNAUTHORIZED


class AuthenticationError(InternalLoggingException):
    """Raised when authentication fails."""

    default_message: str = "Not authenticated"
    status_code: HTTPStatus = HTTPStatus.UNAUTHORIZED


class AuthorizationError(InternalLoggingException):
    """Raised when authorization fails."""

    default_message: str = "Not authorized"
    status_code: HTTPStatus = HTTPStatus.UNAUTHORIZED


class RateLimitExceeded(InternalLoggingException):
    """Raised when rate limit is exceeded."""

    default_message: str = "Rate limit exceeded"
    status_code: HTTPStatus = HTTPStatus.TOO_MANY_REQUESTS


class SecurityError(InternalLoggingException):
    """Raised when security-related errors occur."""

    default_message: str = "Security issue"
    status_code: HTTPStatus = HTTPStatus.UNAUTHORIZED


class HandlerError(InternalLoggingException):
    """Raised when handling fails"""

    default_message: str = "Failed to handle activity"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST


class StorageError(InternalLoggingException):
    """Raised when storage-related errors occur."""

    default_message: str = "Failed to load"
    status_code: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR


class DiscoveryError(InternalLoggingException):
    """Raised when discovery fails."""

    default_message: str = "Discovery failed"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST


class ResolverError(InternalLoggingException):
    """Raised when resolver-related errors occur."""

    default_message: str = "Failed to resolve"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST
    response: ResolverResult | None = None

    def __init__(
        self,
        message: str,
        user_facing_message: str | None = None,
        status_code: HTTPStatus | None = None,
        response: ResolverResult | None = None,
        *args: object,
    ) -> None:
        self.response = response
        super().__init__(message, user_facing_message, status_code, *args)

    def get(self, key: str):
        if self.response:
            return self.response.get(key)


class DeliveryError(InternalLoggingException):
    """Raised when delivery-related errors occur."""

    default_message: str = "Failed to deliver"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST


class KeyManagementError(InternalLoggingException):
    """Raised when key manager-related errors occur."""

    default_message: str = "Key management error"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST


class CollectionError(InternalLoggingException):
    """Raised when collection-related errors occur."""

    default_message: str = "Collection error"
    status_code: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR


class IntegrationError(InternalLoggingException):
    """Raised when integration-related errors occur."""

    default_message: str = "Integration error"
    status_code: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR


class MiddlewareError(InternalLoggingException):
    """Raised when middleware-related errors occur."""

    default_message: str = "Middleware failed"
    status_code: HTTPStatus = HTTPStatus.BAD_GATEWAY


class UserError(InternalLoggingException):
    """Raised when a user related error occured."""

    default_message: str = "User not found"
    status_code: HTTPStatus = HTTPStatus.GONE


class APIError(InternalLoggingException):
    """Raised by an API call error."""

    default_message: str = "Failed to resolve API"
    status_code: HTTPStatus = HTTPStatus.BAD_GATEWAY


class MediaError(InternalLoggingException):
    """Raised by a media processing error."""

    default_message: str = "Failed to process media"
    status_code: HTTPStatus = HTTPStatus.BAD_REQUEST


class SettingsError(InternalLoggingException):
    """Raised by the instance if settings are poorly configured."""

    default_message: str = "Instance configuration failed"
    status_code: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR
