# coding: utf-8


import operator

from django.utils.encoding import force_text

from idm.core.constants.groupmembership import GROUPMEMBERSHIP_STATE
from idm.core.workflow.plain.group import GroupWrapper
from idm.core.workflow.plain.user import UserWrapper
from idm.framework.mixins import Representable
from idm.users.models import User, Group


def systemify(thing):
    from idm.core.models import System
    if isinstance(thing, SystemWrapper):
        return thing
    elif isinstance(thing, str):
        thing = System.objects.get(slug=thing)
    return SystemWrapper(thing)


class SystemWrapper(Representable):
    """
    Обертка объекта системы для использования в Workflow
    """
    user_wrapper_class = UserWrapper
    group_wrapper_class = GroupWrapper
    _node_class = None

    @classmethod
    def get_node_wrapper_class(cls):
        if cls._node_class is None:
            from idm.core.workflow.plain.node import NodeWrapper
            cls._node_class = NodeWrapper
        return cls._node_class

    def __init__(self, system):
        self._system = system

    def __eq__(self, other):
        if isinstance(other, type(self)):
            return self._system == other._system
        else:
            return self._system == other

    def __str__(self):
        return force_text(self._system)

    def __getattr__(self, name):
        return getattr(self._system, name)

    def _get_base_filter(self, role_data, system_specific=False, among_states='almost_active', prefix='roles__'):
        from idm.core.models import RoleNode, Role
        assert among_states in ('almost_active', 'active', 'returnable')
        node = RoleNode.objects.get_node_by_data(self._system, role_data)
        role_filter = {
            'system': self._system,
            'node': node,
        }
        if system_specific is not False:
            role_filter['system_specific'] = system_specific
        role_filter['state_set'] = among_states
        filter_q, _ = Role.objects.get_filtered_query(prefix=prefix, **role_filter)
        return filter_q

    def all_users_with_role(self, role_data, system_specific=False, among_states='almost_active', limit=None):
        """
        Вернуть список пользователей с ролью с данными {role_data}.
        В случае, если не передан параметр {system_specific}, совпадение по полю system_specific не учитывается.
        """
        q_filter = self._get_base_filter(role_data, system_specific, among_states)
        inherited_q = self._get_base_filter(
            role_data, system_specific, among_states,
            prefix='memberships__group__groupclosure_parents__parent__roles__'
        )
        users = User.objects.filter(q_filter)
        if self._system.is_group_aware():
            inherited_users = User.objects.filter(
                inherited_q,
                memberships__state__in=GROUPMEMBERSHIP_STATE.ACTIVE_STATES,
            )
            users = users.union(inherited_users)
        
        if limit:
            users = users.all()[:limit]
        users = sorted(set(users), key=operator.attrgetter('username'))

        return [self.user_wrapper_class(user, {'system': self._system}) for user in users]

    def all_groups_with_role(self, role_data, system_specific=False, among_states='almost_active'):
        """
        Аналогично all_users_with_role, но для групп.
        """

        q_filter = self._get_base_filter(role_data, system_specific, among_states)
        groups = Group.objects.active().prefetch_related('roles').filter(q_filter).distinct()
        return [self.group_wrapper_class(group, {'system': self._system}) for group in groups]

    def get_aliased_nodes(self, name, lang='ru', type=None, distinct=False):
        """
        Находит узлы, которые имеют название name
        """
        nodes = self.nodes.active()
        filter_ = {
            'aliases__is_active': True,
        }
        if lang == 'ru':
            filter_['aliases__name'] = name
        else:
            filter_['aliases__name_en'] = name
        if type is not None:
            filter_['aliases__type'] = type

        nodes = nodes.filter(**filter_)
        if distinct:
            nodes = nodes.distinct()
        wrappers = [self.get_node_wrapper_class()(node) for node in nodes]
        return wrappers

    def get_node_by_data(self, data):
        from idm.core.models import RoleNode
        return self.get_node_wrapper_class()(RoleNode.objects.get_node_by_data(self._system, data))

    @property
    def creator(self):
        creator = self._system.creator
        if creator is not None:
            return self.user_wrapper_class(creator)
