from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend


class UsernameOrEmailBackend(ModelBackend):
    """Autentica pelo nome de utilizador ou por um email único.

    Mantém toda a validação de palavra-passe e de ``user_can_authenticate``
    do backend oficial do Django. Caso existam vários utilizadores com o
    mesmo email, o acesso por email é recusado e continua disponível pelo
    nome de utilizador.
    """

    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        identifier = username or kwargs.get(UserModel.USERNAME_FIELD)
        if identifier is None or password is None:
            return None

        user = None
        try:
            user = UserModel._default_manager.get(
                **{f"{UserModel.USERNAME_FIELD}__iexact": identifier}
            )
        except UserModel.DoesNotExist:
            email_field = getattr(UserModel, "EMAIL_FIELD", "email")
            matches = UserModel._default_manager.filter(
                **{f"{email_field}__iexact": identifier}
            )
            if matches.count() == 1:
                user = matches.first()
        except UserModel.MultipleObjectsReturned:
            return None

        if user and user.check_password(password) and self.user_can_authenticate(user):
            return user
        return None
