from django.utils.translation import gettext_lazy as _
from rest_framework import exceptions, serializers, status


class ProjectBaseException(exceptions.APIException):
    """Base exception"""
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Bad request')

    def __init__(self, data=None):
        if data:
            self.default_detail = {
                'detail': self.default_detail,
                **data
            }
        super().__init__()


class AuthErrorMixin(exceptions.APIException):
    """Authentication exception error mixin."""
    status_code = status.HTTP_401_UNAUTHORIZED


class ServiceError(ProjectBaseException):
    """Service error."""
    status_code = status.HTTP_503_SERVICE_UNAVAILABLE
    default_detail = _('Service is temporarily unavailable')


class UserNotFoundError(AuthErrorMixin, ProjectBaseException):
    """The exception should be thrown when the user cannot get"""
    default_detail = _('User not found')


class EmailSendingError(exceptions.APIException):
    """The exception should be thrown when unable to send an email"""
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Unable to send message to mailbox %s')

    def __init__(self, recipient=None):
        if recipient:
            self.default_detail = {
                'detail': self.default_detail % recipient,
            }
        super().__init__()


class LocaleNotExisted(exceptions.APIException):
    """
    The exception should be thrown when passed locale isn't in model Language
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Locale not found in database (%s)')

    def __init__(self, locale: str = None):
        if locale:
            self.default_detail = {
                'detail': self.default_detail % locale
            }
        super().__init__()


class NotValidUsernameError(exceptions.APIException):
    """
    The exception should be thrown when passed username has @ symbol
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Wrong username')


class NotValidTokenError(exceptions.APIException):
    """
    The exception should be thrown when token in url is not valid
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Not valid token')


class NotValidAccessTokenError(AuthErrorMixin):
    """
    The exception should be thrown when access token in url is not valid
    """
    default_detail = _('Not valid access token')


class NotValidRefreshTokenError(AuthErrorMixin):
    """
    The exception should be thrown when refresh token is not valid
    """
    default_detail = _('Not valid refresh token')


class OAuth2Error(AuthErrorMixin):
    """OAuth2 error"""
    default_detail = _('OAuth2 Error')


class PasswordsAreEqual(exceptions.APIException):
    """
    The exception should be raised when passed password is the same as old ones
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Password is already in use')


class EmailConfirmedError(exceptions.APIException):
    """
    The exception should be raised when user email status is already confirmed
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Email address is already confirmed')


class UserUpdateUploadImageError(exceptions.APIException):
    """
    The exception should be raised when user tries upload an image without crop in request
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Image invalid input.')


class WrongAuthCredentials(AuthErrorMixin):
    """
    The exception should be raised when credentials is not valid for this user
    """
    default_detail = _('Incorrect login or password.')


class FavoritesError(exceptions.APIException):
    """
    The exception should be thrown when item that user
    want to add to favorites is already exists.
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Item is already in favorites.')


class CarouselError(exceptions.APIException):
    """
    The exception should be thrown when the object is already in carousels.
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Item is already in carousels.')


class PasswordResetRequestExistedError(exceptions.APIException):
    """
    The exception should be thrown when password reset request
    already exists and valid.
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Password reset request is already exists and valid.')


class ObjectAlreadyAdded(serializers.ValidationError):
    """
    The exception must be thrown if the object has already been added to the
    list.
    """

    default_detail = _('Object has already been added.')


class BindingObjectNotFound(serializers.ValidationError):
    """The exception must be thrown if the object not found."""

    default_detail = _('Binding object not found.')


class RemovedBindingObjectNotFound(serializers.ValidationError):
    """The exception must be thrown if the object not found."""

    default_detail = _('Removed binding object not found.')


class UnprocessableEntityError(exceptions.APIException):
    """
    The exception should be thrown when executing data on server rise error.
    """
    status_code = status.HTTP_422_UNPROCESSABLE_ENTITY
    default_detail = _('Unprocessable entity valid.')


class GuideElementError(exceptions.APIException):
    """
    The exception should be raised when user tries send guide element that doesn't
    valid for guide.
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Guide element not valid for Guide.')


class AdvertorialError(exceptions.APIException):
    """
    The exception should be raised when user tries create advertorial for guide element
    that already exists.
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Advertorial already exists for this guide element.')
