# coding: utf-8


import logging

from idm.core.constants.email_templates import TEMPLATE_TYPES
from idm.core.constants.system import SYSTEM_WORKFLOW_EXECUTION_METHOD
from idm.core.workflow.plain.executors import PlainUserWorkflowExecutor, PlainGroupWorkflowExecutor
from idm.core.workflow.plain.group import GroupWrapper
from idm.core.workflow.plain.user import UserWrapper
from idm.core.workflow.sandbox.manager.executors import SandboxedUserWorkflowExecutor, SandboxedGroupWorkflowExecutor
from idm.users import ranks
from idm.users.constants.user import USER_TYPES
from idm.utils.i18n import get_lang_key

log = logging.getLogger(__name__)


def subjectify(thing):
    """
    Оборачивает объект поддерживаемого типа в Subject
    :param thing: Что обернуть. Поддерживаются пользователи, группы и роли
    :return: обёртку типа Subject
    """
    subject = None
    from idm.users.models import User, Group
    from idm.core.models import Role
    if isinstance(thing, Subject):
        subject = thing
    elif isinstance(thing, (User, UserWrapper)):
        subject = UserSubject(thing)
    elif isinstance(thing, (Group, GroupWrapper)):
        subject = GroupSubject(thing)
    elif isinstance(thing, Role):
        thing.fetch_user()
        thing.fetch_group()
        if thing.user and thing.group:
            raise ValueError('Both user and group provided')
        elif not thing.user and not thing.group:
            raise ValueError('No user nor group provided')
        if thing.user:
            subject = UserSubject(thing.user)
        else:
            subject = GroupSubject(thing.group)
    if subject is None:
        raise ValueError('Cannot subjectify %s' % repr(thing))
    return subject


class Subject(object):
    """Субъект, класс, сводящий пользователя и группу к общему интерфейсу"""
    @staticmethod
    def get_executor_class(system, node):
        raise NotImplementedError()

    def __init__(self, object_):
        self.object = object_
        self.forms = {}

    def __str__(self):
        return self.inflect(get_lang_key())

    def __eq__(self, other):
        return subjectify(other).object == self.object

    def __ne__(self, other):
        return not self == other

    def __hash__(self):
        raise NotImplementedError()

    def get_name(self, lang='en'):
        raise NotImplementedError()

    def get_ident(self):
        raise NotImplementedError()

    def get_mobile_phone(self):
        raise NotImplementedError()

    def get_type(self, case=None):
        return self.forms[case]

    def inflect(self, case='en'):
        lang = 'ru' if case != 'en' else 'ru'
        result = '%s "%s"' % (self.get_type(case),
                              self.get_name(lang))
        return result

    def is_active(self):
        raise NotImplementedError()

    def is_detached(self):
        raise NotImplementedError()

    def is_managed_by(self, user):
        raise NotImplementedError()

    def get_inactivity_reason(self):
        raise NotImplementedError()

    def is_requestable_for(self):
        """Для этого субъекта может быть запрошена роль"""
        if not self.is_active():
            return False
        if self.is_detached():
            return False
        return True

    def get_absolute_url(self):
        raise NotImplementedError()

    def get_responsibles(self):
        raise NotImplementedError()


class UserSubject(Subject):
    @staticmethod
    def get_executor_class(system, node, execution_method=None):
        system = system or (node and node.system) or None
        if system is None:
            raise ValueError('Information about system was not provided')
        if execution_method is None:
            execution_method = system.workflow_execution_method
        if execution_method == SYSTEM_WORKFLOW_EXECUTION_METHOD.PLAIN:
            return PlainUserWorkflowExecutor
        elif execution_method == SYSTEM_WORKFLOW_EXECUTION_METHOD.SANDBOXED:
            return SandboxedUserWorkflowExecutor
        else:
            raise ValueError('Invalid workflow execution method ({})'.format(system.workflow_execution_method))

    def __init__(self, object_):
        super(UserSubject, self).__init__(object_)
        self.is_group = False
        self.is_user = True
        self.user = self.object
        self.forms = {
            'ru': 'пользователь',
            'en': 'user',
            'кого-чего': 'пользователя',
            'кого-что': 'пользователя',
            'кому': 'пользователю',
            'кем': 'пользователем'
        }

    def __hash__(self):
        return hash(self.user)

    def is_active(self):
        return self.user.is_active

    def get_name(self, lang=None):
        return self.user.get_full_name(lang=lang)

    def get_ident(self):
        return self.user.username

    def get_readable_ident(self):
        return self.get_ident()

    def get_sms_message(self):
        return self.user.mobile_phone

    def get_role_kwargs(self):
        return {'user': self.user}

    def is_detached(self):
        return self.user.type == USER_TYPES.USER and self.user.department_group_id is None

    def is_managed_by(self, user):
        return (
            user.is_head_for(self.user, ranks=ranks.ALL_HEAD_TYPES)
            or user.is_owner_of(self.user)
        )

    def get_inactivity_reason(self):
        return 'уволен'

    def get_workflow_code(self, system):
        return system.get_user_workflow_code()

    def get_responsibles(self, with_heads=False):
        result = [self.user]
        if self.user.is_robot and (self.user.notify_responsibles or with_heads):
            result += list(self.user.responsibles.all())
            
        if not with_heads:
            return result

        if not self.user.is_robot:
            result += self.user.all_heads

        return result

    def get_absolute_url(self):
        return '/user/%s' % self.get_ident()

    def get_reminder_recipients_and_template_type(self, allow_external_recepients):
        if self.user.is_robot:
            template_type = TEMPLATE_TYPES.ROBOT
            responsibles = self.user.responsibles.all()
        elif self.user.is_external() and not allow_external_recepients:
            template_type = TEMPLATE_TYPES.INTERNAL_HEAD
            responsibles = [self.user.internal_head]
            if not responsibles[0]:
                log.warning('У сотрудника %s нет руководителя в Яндексе', self.get_ident())
                responsibles = []
        else:
            template_type = TEMPLATE_TYPES.SELF_USER
            responsibles = [self.user]
        return responsibles, template_type


class GroupSubject(Subject):
    @staticmethod
    def get_executor_class(system, node, execution_method=None):
        system = system or (node and node.system) or None
        if system is None:
            raise ValueError('Information about system was not provided')
        if execution_method is None:
            execution_method = system.workflow_execution_method
        if execution_method == SYSTEM_WORKFLOW_EXECUTION_METHOD.PLAIN:
            return PlainGroupWorkflowExecutor
        elif execution_method == SYSTEM_WORKFLOW_EXECUTION_METHOD.SANDBOXED:
            return SandboxedGroupWorkflowExecutor
        else:
            raise ValueError('Invalid workflow execution method ({})'.format(system.workflow_execution_method))

    def __init__(self, object_):
        super(GroupSubject, self).__init__(object_)
        self.is_user = False
        self.is_group = True
        self.group = self.object
        self.forms = {
            'ru': 'группа',
            'en': 'group',
            'кого-чего': 'группы',
            'кого-что': 'группу',
            'кому': 'группе',
            'кем': 'группой'
        }

    def __hash__(self):
        return hash(self.group)

    def is_active(self):
        return self.group.is_active()

    def get_name(self, lang=None):
        return self.group.get_name(lang=lang)

    def get_ident(self):
        return self.group.external_id

    def get_readable_ident(self):
        return self.group.slug

    def get_sms_message(self):
        # FIXME: договориться с фронтэндом о результатах этого метода
        return True

    def get_role_kwargs(self):
        return {'group': self.group}

    def is_detached(self):
        return False

    def is_managed_by(self, user):
        return self.group.is_managed_by(user, ranks=ranks.HEAD_OR_DEPUTY)

    def get_inactivity_reason(self):
        return 'удалена со Стаффа'

    def get_workflow_code(self, system):
        return system.get_group_workflow_code()

    def get_responsibles(self, with_heads=False):
        return self.group.get_responsibles()

    def get_absolute_url(self):
        return '/group/%s' % self.get_ident()

    def get_reminder_recipients_and_template_type(self, *args, **kwargs):
        template_type = TEMPLATE_TYPES.GROUP
        responsibles = self.group.get_responsibles()
        return responsibles, template_type
