from django.conf import settings

from staff.departments.models import Department, DepartmentKind, DepartmentStaff, DepartmentRoles
from staff.lib.models.mptt import is_ancestor


class UnknownApproverClass(object):
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(UnknownApproverClass, cls).__new__(cls)
        return cls._instance

    def __bool__(self):
        return True


UnknownApprover = UnknownApproverClass()


class Approvers(object):
    UnknownApprover = UnknownApprover

    def __init__(self, author, staff_list, foreign, chiefs_role):
        self.author = author
        self.staff_list = staff_list
        self.foreign = foreign
        self.chiefs_role = chiefs_role

    @staticmethod
    def _get_roles_qs():
        yandex_tree_id = Department.objects.get(id=settings.YANDEX_DEPARTMENT_ID).tree_id

        roles_qs = (
            DepartmentStaff.objects
            .select_related('department', 'staff')
            .filter(department__tree_id=yandex_tree_id)
            .order_by('department__level')
        )
        return roles_qs


class TripApprovers(Approvers):

    def __init__(self, **kwargs):
        super(TripApprovers, self).__init__(**kwargs)

        self._chiefs_role = None
        self._top_department_types = None

    @property
    def top_department_types(self):
        if self._top_department_types is None:
            self._top_department_types = (
                DepartmentKind.objects
                .filter(slug__in=settings.TOP_DEPARTMENT_TYPES)
                .values_list('id', flat=True)
            )
        return self._top_department_types

    @property
    def author_roles(self):
        author_roles_qs = self._get_roles_qs().filter(staff=self.author, role_id=DepartmentRoles.CHIEF.value)
        if self.foreign:
            # Если поездка заграничная, руководство не топ подразделением ничего не даст.
            # поэтому отфильтруем ненужные роли.
            author_roles_qs = author_roles_qs.filter(department__kind__in=self.top_department_types)
        return author_roles_qs

    @property
    def filtered_chiefs_role(self):
        if self._chiefs_role is None:
            if self.foreign:
                # Если поездка заграничная руководители не топ подразделений ничего не решают
                self._chiefs_role = {
                    ch: [r for r in roles if r.department.kind_id in self.top_department_types]
                    for ch, roles in self.chiefs_role.items()
                }
            else:
                self._chiefs_role = self.chiefs_role
        return self._chiefs_role

    def get_team_top_roles(self, team):
        return (
            self._get_roles_qs()
            .filter(
                staff__in=team,
                role_id=DepartmentRoles.CHIEF.value,
                department__kind__in=self.top_department_types,
            )
        )

    def check_author_roles(self, team):
        for person in team:
            for role in self.author_roles:
                # Если автор руководитель сотрудника, тот едет без согласования
                if is_ancestor(role.department, person.department) and self.author != person:
                    yield person, None

    def check_team_roles(self, team):
        for person in {r.staff for r in self.get_team_top_roles(team)}:
            # Если кто-то из участников топ, он едет без согласования
            yield person, None

    def check_chiefs(self, team):
        for person in team:
            roles = self.filtered_chiefs_role.get(person, [])
            if roles:
                # Если нашелся руководитель, подтверждает он
                yield person, roles[0].staff.login

    def get(self):
        result = {}
        team = set(self.staff_list)

        for check in (self.check_author_roles, self.check_team_roles, self.check_chiefs):
            for person, approver_login in check(team.copy()):
                result[person.id] = approver_login
                team.discard(person)
                if not team:
                    return result

        for person in team:
            result[person.id] = UnknownApprover

        return result


class ConfApprovers(Approvers):

    def __init__(self, hr_partners, **kwargs):
        super(ConfApprovers, self).__init__(**kwargs)
        self.hr_partners = hr_partners

    def get(self):

        hrbp_ids = self.get_hrbp_ids()

        result = {}

        for person in self.staff_list:
            # Партнеры текущего сотрудника
            person_hrbp = self.hr_partners.get(person.id, [])
            person_hrbp_ids = {hr['id'] for hr in person_hrbp}
            # Непосредственный руководитель
            direct_chief = self.chiefs_role.get(person)
            direct_chief = direct_chief[0].staff if direct_chief else None

            # Сотрудник не HRBP и автор не HRBP утверждает HRBP
            if not (person.id in hrbp_ids or self.author.id in person_hrbp_ids):
                result[person.id] = person_hrbp[0]['login'] if person_hrbp else UnknownApprover
            # Иначе, если автор HRBP и не зарубеж, никто не утверждает
            elif self.author.id in person_hrbp_ids and not self.foreign:
                result[person.id] = None
            # Иначе, если автор непосредственный руководитель, без согласования
            elif self.author == direct_chief:
                result[person.id] = None
            # Иначе согласует непосредственный
            else:
                result[person.id] = direct_chief.login if direct_chief else UnknownApprover

        return result

    def get_hrbp_ids(self):
        hrbp_ids = (
            self._get_roles_qs()
            .filter(staff__in=self.staff_list, role_id=DepartmentRoles.HR_PARTNER.value)
            .values_list('staff', flat=True)
        )
        return hrbp_ids
