#-*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function, unicode_literals

import logging
from collections import namedtuple

from django.conf import settings
from django.contrib.auth import SESSION_KEY
from django.utils import timezone
from django.utils.importlib import import_module

from .models import SessionActivity, create_session_activity
from .utils import deserialize_date, serialize_date
from .conf import SESSION_IP_KEY, SESSION_LAST_USED_KEY, SESSION_USER_AGENT_KEY

logger = logging.getLogger("app.session_activity")

SessionInfo = namedtuple("SessionInfo", [
    "session", "session_key", "is_current",
    "ip", "last_used", "user_agent",
    "session_activity"
])


def get_session_store():
    """
    Loads session store object based on the `SESSION_ENGINE` setting.

    :rtype: :class:`django.contrib.sessions.backends.base.SessionBase`
    """
    return import_module(settings.SESSION_ENGINE).SessionStore


def is_current_session(session, request):
    """
    Checks if a given session object is the same as the on used by the request.

    :type session: :class:`django.contrib.sessions.backends.base.SessionBase`
    :type request: :class:`django.http.HttpRequest` or None
    """
    if request is not None:
        # TODO: Match by current session key or (ip, user agent) pair
        return session.session_key == request.session.session_key
    return False


def get_active_sessions(user, request=None):
    """
    Returns list of all active sessions for given user.

    :type user: django.contrib.auth.models.AbstractBaseUser
    :type request: django.http.HttpRequest
    :return: list of active session info
    :rtype: list of SessionInfo
    """
    SessionStore = get_session_store()
    active_sessions = []

    for obj in SessionActivity.objects.filter(user=user):
        # noinspection PyCallingNonCallable
        session = SessionStore(session_key=obj.session_key)

        if SESSION_KEY in session and session.get_expiry_age():
            session_info = SessionInfo(
                session=session,
                session_key=session.session_key,
                is_current=is_current_session(session, request),
                ip=session.get(SESSION_IP_KEY, None),
                last_used=deserialize_date(session.get(SESSION_LAST_USED_KEY, None)),
                user_agent=session.get(SESSION_USER_AGENT_KEY, ""),
                session_activity=obj
            )
            active_sessions.append(session_info)

    return active_sessions


def update_current_session_info(request):
    """
    Maintains and updates last used, ip and user agent fields for
    current session.

    :type request: django.http.HttpRequest
    """

    # Don't create session if it does not exist already
    # Otherwise we could violate EU regulations
    session_exists = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None) is not None
    if not session_exists:
        logger.debug("Session does not exist for current request")
        return

    session = request.session
    now = timezone.now()
    last_used = deserialize_date(session.get(SESSION_LAST_USED_KEY, None))

    # TODO: limit to authenticated users only?
    if not last_used or (now - last_used) > settings.SESSION_ACTIVITY_UPDATE_THROTTLE:
        logger.debug("Refreshing session activity")

        session[SESSION_LAST_USED_KEY] = serialize_date(now)
        session[SESSION_IP_KEY] = request.META["REMOTE_ADDR"]
        session[SESSION_USER_AGENT_KEY] = request.META.get("HTTP_USER_AGENT", "")

        user = request.user
        if user.is_authenticated():
            if not SessionActivity.objects.filter(
                    user=user, session_key=session.session_key).exists():
                # Create activity record in case it was not added on
                # user log-in (i.e. there are some old sessions in the system)
                create_session_activity(request=request, user=user)


def sign_out_other_sessions(user, request):
    """
    Deactivate all user's sessions that do not belong to
    current request

    :type user: django.contrib.auth.models.AbstractBaseUser
    :type request: django.http.HttpRequest
    """
    count = 0
    active_sessions = get_active_sessions(user=user, request=request)
    for session_info in active_sessions:
        if not session_info.is_current:
            session_info.session.flush()
            count += 1
    return count