from copy import deepcopy
from typing import List

from staff.departments.models import Department, DepartmentRoles
from staff.departments.controllers.proposal_action import DepartmentSection

from staff.lib.utils.qs_values import localize, extract_related


class DepartmentDataCtl(object):
    def __init__(self, identifiers: List[int]):
        self._identifiers = identifiers
        self._meta_data = {}

        self.qs = (
            Department.objects
            .filter(id__in=self._identifiers)
            .values(
                'departmentstaff__staff_id', 'departmentstaff__role',
                'departmentstaff__staff__login',
                'departmentstaff__staff__first_name',
                'departmentstaff__staff__first_name_en',
                'departmentstaff__staff__last_name',
                'departmentstaff__staff__last_name_en',
                'id', 'code', 'url',
                'name', 'name_en',
                'parent',
                'clubs', 'wiki_page', 'maillists',
                'description', 'description_en',
                'kind_id', 'category', 'position',
            )
            .order_by('id')
        )
        self._raw_dep_data = list(self.qs)

    @classmethod
    def as_diff(cls, proposal_data):
        """
        Для изменяемых в рамках заявки подразделений
        возвращает словарь вида {
        id: {
          <field>: {
            'old': <value>,
            'new': <value>
          }
          ..
        }
        id - айди подразделения
        field - названия конечных полей из заявки
        value - значения до применения заявки и после. вместо объектов - их id
        """
        diff = {}
        ctl = cls([new['id'] for new in proposal_data if new['id'] is not None and not new['delete']])

        for new in deepcopy(proposal_data):
            if new['id'] is None or new['delete']:
                continue

            old = ctl.as_form_data(new['id'])
            department_diff = {}

            for fieldset_key, fieldset_data in new.items():
                if not DepartmentSection.has_value(fieldset_key):
                    continue

                if not isinstance(fieldset_data, dict):
                    continue

                for key, data in fieldset_data.items():
                    if key in old[fieldset_key] and old[fieldset_key][key] != data:
                        department_diff[key] = {
                            'old': old[fieldset_key][key],
                            'new': data,
                        }
            diff[new['id']] = department_diff
        return diff

    def as_meta(self, identifier=None):
        if not self.qs:
            return {}

        requested_meta = None

        dep_meta_stub = {
            'name': '',
            'chief': {
                'caption': '',
                'login': '',
            },
            'deputies': [],
            'url': '',
        }
        meta = {}
        for record in self._raw_dep_data:
            _id = record['id']
            meta[_id] = meta.get(record['id'], deepcopy(dep_meta_stub))
            meta[_id]['url'] = record['url']
            person_data = localize(
                extract_related(record.copy(), related_name='departmentstaff__staff')
            )
            del person_data['id']
            person_data['caption'] = '{first_name} {last_name}'.format(
                first_name=person_data.pop('first_name'),
                last_name=person_data.pop('last_name'),
            )
            if record['departmentstaff__role'] == DepartmentRoles.CHIEF.value:
                meta[_id]['chief'] = person_data

            if record['departmentstaff__role'] == DepartmentRoles.DEPUTY.value:
                meta[_id]['deputies'].append(person_data)

            if not meta[record['id']].get('name'):
                meta[_id]['name'] = localize(record.copy())['name']

            if identifier and str(identifier) in (str(record['id']), record['url']):
                requested_meta = meta[_id]

        return requested_meta or meta

    def as_actions(self):
        if not self.qs:
            return {}
        return {department_id: self.as_form_data(department_id) for department_id in self._identifiers}

    def as_form_data(self, identifier):
        if not self._raw_dep_data:
            return {}

        def dep_records(identifier):
            for record in self._raw_dep_data:
                if str(identifier) in (str(record['id']), record['url']):
                    yield record

        chief_id = ''
        deputies = []

        for record in dep_records(identifier):
            if record['departmentstaff__role'] == DepartmentRoles.CHIEF.value:
                chief_id = record['departmentstaff__staff_id']
            if record['departmentstaff__role'] == DepartmentRoles.DEPUTY.value:
                deputies.append(record['departmentstaff__staff_id'])

        for target_dep in dep_records(identifier):
            department_data = {
                'id': target_dep['id'],
                'fake_id': '',
                'name': {
                    'name': target_dep['name'],
                    'name_en': target_dep['name_en'],
                },
                'hierarchy': {
                    'parent': target_dep['parent'],
                    'fake_parent': '',
                },
                'administration': {
                    'chief': chief_id,
                    'deputies': deputies,
                },
                'technical': {
                    'kind': target_dep['kind_id'],
                    'code': target_dep['code'],
                    'position': target_dep['position'],
                    'category': target_dep['category']
                },
                'delete': False
            }
            return department_data
