from itertools import groupby
from typing import Dict, Any, List, Tuple

from staff.departments.models import Department, DepartmentRoles
from staff.person.models import Staff
from staff.proposal.forms.department import DepartmentEditForm

from staff.lib.models.departments_chain import get_departments_tree

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

from staff.proposal.controllers.utils import raw_data


def order_field(tree_id, lft):
    return tree_id * 10000 + lft


def overdraft_decimal_fields(record: Dict[str, Any]) -> Dict[str, str]:
    result = {}

    allowed_overdraft_percents = record['allowedheadcountoverdraft__percents_with_child_departments']
    result['allowed_overdraft_percents'] = (
        str(allowed_overdraft_percents)
        if allowed_overdraft_percents
        else ''
    )

    return result


def reduce_department_data(data_qs) -> Dict[str, Dict]:
    """
    Из плоского списка values с данными о departmentstaff
    собирает словарь {url: {field: value}}, аггрегируя departmentstaff
    """
    dep_data = {}
    keys = {
        DepartmentRoles.CHIEF.value: 'chief',
        DepartmentRoles.DEPUTY.value: 'deputies',
        DepartmentRoles.HR_PARTNER.value: 'hr_partners',
        DepartmentRoles.BUDGET_HOLDER.value: 'budget_holders',
        DepartmentRoles.HR_ANALYST.value: 'hr_analysts',
    }

    for url, records in groupby(data_qs, lambda record: record['url']):
        dep_data[url] = {
            'chief': None,
            'deputies': [],
            'hr_partners': [],
            'hr_analysts': [],
            'budget_holder': None,
        }
        for record in records:
            key = keys.get(record['departmentstaff__role'])
            if key is None:
                continue
            person_data = extract_related(record, related_name='departmentstaff__staff')
            if key in ('chief', 'budget_holder'):
                dep_data[url][key] = person_data
            elif key in ('deputies', 'hr_partners', 'hr_analysts'):
                dep_data[url][key].append(person_data)

        dep_data[url].update(
            {
                'name': record['name'],
                'name_en': record['name_en'],
                'parent': record['parent__url'],
                'description': record['description'],
                'description_en': record['description_en'],
                'department_type': record['kind_id'],
                'code': record['code'],
                'order': record['position'],
                'hr_type': False,  # Пока не начали хранить его в базе
                'type': record['kind__name'],
                'type_en': record['kind__name_en'],
                'id': record['id'],
                'category': record['category'],
                'level': record['level'],
                'order_field': order_field(record['tree_id'], record['lft']),
                **overdraft_decimal_fields(record),
            }
        )
    return dep_data


def subdict(origin_dict: Dict[str, Any], fields: Tuple, default=None) -> Dict[str, Any]:
    result = {}
    for field in fields:
        result[field] = origin_dict.get(field, default)
    return result


class DepartmentDataCtl:
    def __init__(self, urls: List[str], person: Staff):
        self._person = person
        self._meta_data = {}

        dep_data = (
            Department.objects.filter(url__in=urls)
            .values(
                'departmentstaff__role',
                'departmentstaff__staff_id',
                '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__url',
                'clubs', 'wiki_page', 'maillists',
                'description', 'description_en',
                'kind_id', 'position',
                'kind__name', 'kind__name_en',
                'category',
                'level', 'lft', 'tree_id',
                'allowedheadcountoverdraft__percents_with_child_departments',
            )
            .order_by('url')
        )
        self.departments_data = reduce_department_data(dep_data)

        id2url = {d['id']: url for url, d in self.departments_data.items()}

        self.department_chains = {
            id2url[pk]: chain
            for pk, chain in get_departments_tree(id2url.keys()).items()
        }

    def overdraft_fields(self, dep: Dict[str, Any]) -> Dict[str, str]:
        if not self._person.user.has_perm('django_intranet_stuff.can_execute_department_proposals'):
            return {}

        return subdict(dep, ('allowed_overdraft_percents',))

    def as_form_data(self, url):
        """"""
        if not self.departments_data or url not in self.departments_data:
            return {}

        result = raw_data(DepartmentEditForm().data_as_dict())
        dep = self.departments_data[url]
        result['url'] = url

        result['name'].update(subdict(dep, ('name', 'name_en')))
        result['technical'].update(subdict(dep, (
            'code',
            'department_type',
            'hr_type',
            'order',
            'category',
        )))
        result['technical'].update(self.overdraft_fields(dep))

        result['hierarchy'].update(subdict(dep, ('parent', 'fake_parent', 'changing_duties')))

        result['administration']['chief'] = dep['chief'] and dep['chief']['login']
        result['administration']['deputies'] = [p['login'] for p in dep['deputies']]
        result['administration']['hr_partners'] = [p['login'] for p in dep['hr_partners']]
        result['administration']['budget_holder'] = dep['budget_holder'] and dep['budget_holder']['login']

        return result

    def as_meta(self, url=None):
        if not self.departments_data or url not in self.departments_data:
            return {}

        def person_info(person_dict):
            if not person_dict:
                return person_dict

            info = localize(person_dict.copy())
            return info

        info = localize(self.departments_data[url].copy())

        info['hr_type'] = None
        info['url'] = url
        info['chief'] = person_info(info['chief'])
        info['budget_holder'] = person_info(info['budget_holder'])
        info['deputies'] = [
            person_info(p) for p in info['deputies']
        ]
        info['hr_partners'] = [
            person_info(p) for p in info['hr_partners']
        ]
        info['parents'] = [
            localize(p) for p in self.department_chains[url]
        ]
        info['parents'] = [
            p['name'] for p in info['parents']
        ]
        return info
