from importlib import import_module
from django.conf import settings
from django.contrib.auth import SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY, load_backend
from django.utils.translation import LANGUAGE_SESSION_KEY
from django.utils.crypto import constant_time_compare
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth.signals import user_logged_in, user_logged_out, user_login_failed


# copy from django.contrib.sessions.middleware.SessionMiddleware
_engine = import_module(settings.SESSION_ENGINE)
_SessionStore = _engine.SessionStore


# copy and modified from django.contrib.sessions.middleware.SessionMiddleware
def get_django_session(session_key):
    django_session = _SessionStore(session_key)
    return django_session


# copy and modified from django.contrib.auth.get_user
def get_user_from_django_session(django_session):
    user = None
    try:
        user_id = django_session[SESSION_KEY]
        backend_path = django_session[BACKEND_SESSION_KEY]
    except KeyError:
        pass
    else:
        if backend_path in settings.AUTHENTICATION_BACKENDS:
            backend = load_backend(backend_path)
            user = backend.get_user(user_id)
            # Verify the session
            if ('django.contrib.auth.middleware.SessionAuthenticationMiddleware'
                    in settings.MIDDLEWARE_CLASSES and hasattr(user, 'get_session_auth_hash')):
                session_hash = django_session.get(HASH_SESSION_KEY)
                session_hash_verified = session_hash and constant_time_compare(
                    session_hash,
                    user.get_session_auth_hash()
                )
                if not session_hash_verified:
                    django_session.flush()
                    user = None

    return user or AnonymousUser()


# copy and modified from django.contrib.auth.login
def login(django_session, user):
    session_auth_hash = ''
    assert user is not None
    if hasattr(user, 'get_session_auth_hash'):
        session_auth_hash = user.get_session_auth_hash()

    if SESSION_KEY in django_session:
        if django_session[SESSION_KEY] != user.pk or (
                session_auth_hash and
                django_session.get(HASH_SESSION_KEY) != session_auth_hash):
            # To avoid reusing another user's session, create a new, empty
            # session if the existing session corresponds to a different
            # authenticated user.
            django_session.flush()
    else:
        django_session.cycle_key()
    django_session[SESSION_KEY] = user.pk
    django_session[BACKEND_SESSION_KEY] = user.backend
    django_session[HASH_SESSION_KEY] = session_auth_hash
    django_session.save()
    user_logged_in.send(sender=user.__class__, request=None, user=user)


# copy and modified from django.contrib.auth.logout
def logout(django_session, user):
    if hasattr(user, 'is_authenticated') and not user.is_authenticated():
        user = None
    user_logged_out.send(sender=user.__class__, request=None, user=user)

    # remember language choice saved to session
    # for backwards compatibility django_language is also checked (remove in 1.8)
    language = django_session.get(LANGUAGE_SESSION_KEY, django_session.get('django_language'))

    django_session.flush()

    if language is not None:
        django_session[LANGUAGE_SESSION_KEY] = language

    user_logged_out.send(sender=user.__class__, request=None, user=user)
