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

from .utils import get_cas_client

UserModel = get_user_model()


class ENSCASBackend:
    """ENS CAS authentication backend.

    Implement standard CAS v3 authentication
    """

    def authenticate(self, request, ticket=None):
        cas_client = get_cas_client(request)
        cas_login, attributes, _ = cas_client.verify_ticket(ticket)

        if cas_login is None:
            # Authentication failed
            return None
        cas_login = self.clean_cas_login(cas_login)

        if request:
            request.session["CASCONNECTED"] = True

        return self._get_or_create(cas_login, attributes)

    def clean_cas_login(self, cas_login):
        return cas_login.strip().lower()

    def _get_or_create(self, cas_login, attributes):
        """Handles account retrieval and creation for CAS authentication.

        - If no CAS account exists, create one;
        - If a matching CAS account exists, retrieve it.
        """

        email = attributes.get("email")

        try:
            user = UserModel.objects.get(username=cas_login)
        except UserModel.DoesNotExist:
            user = None

        if user is None:
            user = UserModel.objects.create_user(username=cas_login, email=email)
        return user

    # Django boilerplate.
    def get_user(self, user_id):
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None


class ElectionBackend(ModelBackend):
    """Authentication for a specific election.

    Given a login and an election, we check if the user `{election.id}__{login}`
    exists, and then if the password matches.
    """

    def authenticate(self, request, login=None, password=None, election_id=None):
        if login is None or password is None or election_id is None:
            return None

        try:
            user = UserModel.objects.get(username=f"{election_id}__{login}")
        except UserModel.DoesNotExist:
            return None

        if user.check_password(password):
            return user
        return None

    # Django boilerplate.
    def get_user(self, user_id):
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None
