# coding: utf-8


from django.utils.translation import ugettext_lazy as _

from idm.core.workflow.exceptions import WorkflowError


class TreeMixin(object):
    @classmethod
    def as_wrapper(cls, obj, context=None):
        kwargs = {}
        if context is not None:
            kwargs['context'] = context
        return cls(obj, **kwargs)

    @property
    def object(self):
        return getattr(self, self.object_attribute_name)

    def get_context(self):
        return getattr(self, 'context', None)

    def get_parent(self):
        parent = self.object.parent
        # совсем корневые объекты - виртуальные, поэтому нужно падать чуть раньше
        if parent is None or parent.is_root_node():
            raise self.no_parent_exception()
        return self.as_wrapper(parent, self.get_context())

    @property
    def parent(self):
        return self.get_parent()

    def is_root(self):
        return self.object.is_root_node() or self.object.parent.is_root_node()

    def get_root(self):
        """Это ненастоящий root, настоящий виртуальный, а это первый нормальный узел"""
        return self.get_ancestor(1)

    def get_ancestors(self):
        context = self.get_context()
        return [self.as_wrapper(node, context)
                for node in self.object.get_ancestors(include_self=True) if node.level >= 1]

    def get_ancestor(self, level):
        if level < 0:
            level = self.object.level + level
        if level == 0 or level > self.object.level:
            raise ValueError('Cannot get ancestor of level=%d' % level)
        ancestor = self.object.get_ancestors(include_self=True).get(level=level)
        return self.as_wrapper(ancestor, self.get_context())

    def is_descendant_of(self, other):
        return other in self.get_ancestors()

    def get_children(self):
        return [self.as_wrapper(child) for child in self.object.get_child_nodes()]


class SubjectWrapperMixin(object):
    def has_role(self, role_data=None, system_specific=False, node_id=None, system=None):
        """
        Вернуть bool, есть ли у субъекта роль с данными {role_data}.
        В случае, если не передан параметр {system_specific}, совпадение по полю system_specific не учитывается.

        :type role_data: dict
        :type system_specific: NoneType | dict
        :rtype: bool
        """
        from idm.core.models import RoleNode
        from idm.core.workflow.plain.system import systemify
        from idm.core.workflow.exceptions import RoleNodeDoesNotExist

        if system is not None:
            system_wrapper = systemify(system)
        else:
            system_wrapper = systemify(self.context['system'])

        system = system_wrapper._system
        try:
            if node_id is not None:
                node = RoleNode.objects.get(pk=node_id)
            elif role_data is not None:
                node = RoleNode.objects.get_node_by_data(system, role_data)
            else:
                raise WorkflowError(_('Необходимо передать один из параметров `role_data` или `node_id`'))
        except RoleNodeDoesNotExist:
            raise WorkflowError(_('В дереве ролей системы %(system)s отсутствует узел %(data)s') % {
                'system': system.get_name(),
                'data': role_data
            })

        node.fetch_system()
        return getattr(self, self.object_attribute_name).has_role(node, system_specific=system_specific)
