# coding: utf-8


import logging
import operator
from textwrap import dedent

from django.utils.timezone import localtime
from django.utils.translation import ugettext_lazy as _

from idm.core.constants.approverequest import APPROVEREQUEST_DECISION
from idm.core.constants.action import ACTION
from idm.core.models import Workflow, RoleRequest
from idm.utils import json

log = logging.getLogger(__name__)


class Column(object):
    def __init__(self, label, field_name=None):
        self.label = label
        self.field_name = field_name

    def render_data(self, obj):
        raise NotImplementedError()


class DataGridMeta(type):
    def __new__(cls, class_name, bases, attrs):
        new_class = super(DataGridMeta, cls).__new__(cls, class_name, bases, attrs)
        new_class.columns = {}
        for attr, value in list(attrs.items()):
            if isinstance(value, Column):
                new_class.columns[attr] = value
        return new_class


class DataGrid(object, metaclass=DataGridMeta):
    pass


class SystemColumn(Column):
    def render_data(self, obj):
        return obj.system.get_name()


class ActionSystemColumn(Column):
    def render_data(self, obj):
        """Проверим наличиие ссылки на систему и, если нет - в привязанной роли"""
        system = None
        if obj.system is not None:
            system = obj.system
        elif obj.role is not None:
            system = obj.role.system
        return system.get_name() if system else ''


class ActionCommentColumn(Column):
    def render_data(self, obj):
        return obj.data.get('comment', '') if obj.data else ''


class ActionExtraColumn(Column):
    template = dedent('''
    Изменил workflow: {author[name]} ({author[ident]}).
    Подтвердил workflow: {approver[name]} ({approver[ident]}).
    Изменения пользовательского workflow:
    {diff}
    Изменения группового workflow:
    {group_diff}
    ''')

    def render_data(self, obj):
        if obj.action != 'change_workflow':
            return ''
        if not obj.data:
            return ''
        new_workflow_id = obj.data.get('new_workflow')
        old_workflow_id = obj.data.get('old_workflow')
        if not new_workflow_id:
            return ''
        try:
            new_workflow = Workflow.objects.select_related('user', 'approver').get(pk=new_workflow_id)
            old_workflow = Workflow.objects.get(pk=old_workflow_id) if old_workflow_id else None
        except Workflow.DoesNotExist:
            return ''
        diff, group_diff = Workflow.objects.get_diffs(old_workflow, new_workflow, highlight=False, full=False)
        options = {
            'author': {
                'name': new_workflow.user.get_full_name() if new_workflow.user else _('Неизвестно'),
                'ident': new_workflow.user.username if new_workflow.user else 'None',
            },
            'approver': {
                'name': new_workflow.approver.get_full_name() if new_workflow.approver else _('Неизвестно'),
                'ident': new_workflow.approver.username if new_workflow.approver else 'None',
            },
            'diff': diff,
            'group_diff': group_diff,
        }
        result = self.template.format(**options)
        return result


class ActionRoleColumn(Column):
    def render_data(self, obj):
        try:
            result = obj.role.node.humanize()
        except Exception:
            result = ''
        return result


class ActionSubjectColumn(Column):
    def render_data(self, obj):
        try:
            subject = obj.role.get_subject()
            ident = '%s (%s)' % (subject.get_name(), subject.get_ident())
        except Exception:
            ident = ''
        return ident


class ActionFieldsDataColumn(Column):
    def render_data(self, obj):
        result = ''
        try:
            if obj.role and obj.role.fields_data:
                result = ', '.join('%s: %s' % item for item in obj.role.fields_data.items())
        except Exception:
            pass
        return result


class RoleColumn(Column):
    def render_data(self, obj):
        return obj.humanize()


class RoleCodeColumn(Column):
    """
    Колонка служебной информации о роли
    """
    def render_data(self, obj):
        # TODO: сейчас в отчет уходят строки вида ""level"" - разобраться с представлением
        return json.dumps(obj.node.data)


class OwnerColumn(Column):
    def render_data(self, obj):
        user = obj.user
        if user is not None:
            result = user.get_full_name() or user.username
        elif obj.group is not None:
            result = obj.group.name
        else:
            result = _('Робот')
        return result


class UserColumn(Column):
    def render_data(self, obj):
        user = getattr(obj, self.field_name)
        if user is not None:
            result = user.get_full_name() or user.username
        elif obj.group is not None:
            result = obj.group.get_name()
        else:
            result = _('Робот')
        return result


class UsernameColumn(Column):
    def render_data(self, obj):
        result = ''
        try:
            user = getattr(obj, self.field_name)
            result = getattr(user, 'username')
        except AttributeError:
            if obj.group is None:
                result = 'idm'
        return result


class DateTimeColumn(Column):
    def render_data(self, obj):
        value = getattr(obj, self.field_name)
        return localtime(value).strftime('%Y-%m-%d %H:%M') if value else ''


class ApproversColumn(Column):
    def render_data(self, obj):
        result = ['выдана как связанная'] if obj.parent_id else []

        ancestors_roles = obj.get_ancestors(include_self=True)
        top_role = ancestors_roles[0]

        try:
            lastrequest = top_role.get_last_request()
        except RoleRequest.DoesNotExist:
            lastrequest = None

        if lastrequest is not None:
            try:
                approvers = []
                for approve in lastrequest.approves.all():
                    for request in approve.requests.all():
                        if request.decision == APPROVEREQUEST_DECISION.APPROVE:
                            approvers.append(request.approver)

                result += [
                    '{}: {}'.format(approver.get_full_name() if approver else 'Робот', localtime(request.updated))
                    for approver in approvers
                ]
            except Exception:
                pass

        return ', '.join(result)


class RoleStateColumn(Column):
    def render_data(self, obj):
        return obj.get_state_display()


class MergedDataColumn(Column):
    def render_data(self, obj):
        result = ''
        try:
            data = (obj.fields_data or {}).copy()
            data.update(obj.system_specific or {})
            result = ', '.join('%s: %s' % item for item in sorted(data.items(), key=operator.itemgetter(0)))
        except Exception:
            pass
        return result


class ActionColumn(Column):
    def render_data(self, obj):
        return ACTION.ACTIONS.get(obj.action, ['-'] * 3)[2]


class RolesReportGrid(DataGrid):
    owner = OwnerColumn(_('Сотрудник'))
    owner_username = UsernameColumn(_('Логин'), field_name='user')
    group_type = Column(_('Тип группы'), field_name='group__type')
    position = Column(_('Должность'), field_name='user__position')
    department_group = Column(_('Отдел'), field_name='user__department_group')
    system = SystemColumn(_('Система'))
    role = RoleColumn(_('Роль'))
    role_code = RoleCodeColumn(_('Код роли'))
    merged_data = MergedDataColumn(_('Доп. данные'))
    state = RoleStateColumn(_('Состояние'))
    granted_at = DateTimeColumn(_('Выдана'), field_name='granted_at')
    approvers = ApproversColumn(_('Подтвердили'))


class ActionLogGrid(DataGrid):
    added = DateTimeColumn(_('Когда'), field_name='added')
    requester = UserColumn(_('Сотрудник'), field_name='requester')
    requester_username = UsernameColumn(_('Кто (логин)'), field_name='requester')
    action = ActionColumn(_('Действие'))
    system = ActionSystemColumn(_('Система'))
    role = ActionRoleColumn(_('Роль'))
    subject = ActionSubjectColumn(_('Владелец роли'))
    fields_data = ActionFieldsDataColumn(_('Данные роли'))
    comment = ActionCommentColumn(_('Комментарий'))
    extra_info = ActionExtraColumn(_('Дополнительная информация'))
