from importlib import import_module

from django.conf import settings
from django.contrib.sessions.base_session import AbstractBaseSession, BaseSessionManager
from django.core.cache import caches
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

import console_sessions.geoip as geoip


class SessionQuerySet(models.QuerySet):
    def tenant(self, user=None):
        """
        Return only records that the user is qualified to view.

        :param user: User object
        """
        if not user or not user.is_authenticated:
            return self.none()

        try:
            if user.is_system_owner:
                return self.all()
        except AttributeError:
            pass

        try:
            return self.filter(user__in=user.user_relations_cache)
        except AttributeError:
            pass

        return self.filter(user=user)

    def delete(self):
        """
        Delete sessions from both DB and cache (first cache, then DB)
        """
        # noinspection PyPep8Naming
        SessionStore = Session.get_session_store_class()
        prefix = getattr(SessionStore, "cache_key_prefix", None)
        if prefix is not None:
            caches[settings.SESSION_CACHE_ALIAS].delete_many(prefix + s.session_key for s in self)
        return super().delete()


class SessionManager(BaseSessionManager.from_queryset(SessionQuerySet)):
    use_in_migrations = True


class Session(AbstractBaseSession):
    """
    Session objects containing user session information.
    """

    css_icon = 'fas fa-sign-in-alt'

    user = models.ForeignKey(
        getattr(settings, "AUTH_USER_MODEL", "auth.User"),
        null=True,
        on_delete=models.CASCADE,
    )
    user_agent = models.TextField(null=True, blank=True)
    created = models.DateTimeField(default=timezone.now)
    modified = models.DateTimeField(default=timezone.now)
    ip = models.GenericIPAddressField(null=True, blank=True, verbose_name=_("IP"))
    credential_id = models.TextField(null=True, blank=True, default=None)
    last_mfa = models.DateTimeField(null=True)

    objects = SessionManager()

    def __str__(self):
        return f'{self.session_key} for User: {self.user} from IP: {self.ip}'

    @property
    def row_pk(self) -> str:
        """DataTables helper attribute"""
        return f'row_{self.pk}'

    @classmethod
    def get_session_store_class(cls):
        return import_module(settings.SESSION_ENGINE).SessionStore

    def delete(self, *args, **kwargs):
        """
        Delete session from both DB and cache (first cache, then DB)
        """
        # noinspection PyPep8Naming
        SessionStore = Session.get_session_store_class()
        prefix = getattr(SessionStore, "cache_key_prefix", None)
        if prefix is not None:
            caches[settings.SESSION_CACHE_ALIAS].delete(prefix + self.session_key)
        return super(Session, self).delete(*args, **kwargs)

    def location(self) -> str:
        return geoip.ip_to_location(self.ip)

    def location_info(self) -> dict:
        return geoip.ip_to_location_info(self.ip)

    def location_name(self) -> str:
        location = self.location()
        if location:
            return f'{self.ip} in {location}'
        return str(self.ip) if self.ip else ''

    @cached_property
    def device(self) -> dict:
        """
        Describe the user agent of this session, if any
        """
        if self.user_agent:
            from device_detector import SoftwareDetector  # late import to avoid import cost

            return SoftwareDetector(self.user_agent).parse().all_details
        return {}

    def client_name(self) -> str:
        client = self.device.get('client', {})
        name = client.get('name', 'UNK')
        version = client.get('version')
        if version:
            name = f'{name} - {version}'
        return name

    def client_platform(self) -> str:
        os = self.device.get('os', {})
        return os.get('name', 'UNK')
