from itertools import chain

from django.db.models import Q

from staff.departments.models import DepartmentStaff, DepartmentRoles

from staff.lib.models.mptt import get_heirarchy_filter_query


def get_roles(person_id):
    roles = (
        DepartmentStaff.objects
        .filter(
            staff_id=person_id,
            role_id=DepartmentRoles.CHIEF.value,
        )
        .values(
            'department__tree_id',
            'department__lft',
            'department__rght',
        )
    )
    return roles


def get_roles_q(roles):
    if not roles:
        return Q(id__in=[])

    return get_heirarchy_filter_query(
        roles,
        by_children=True,
        filter_prefix='department__',
        prefix='department__',
        include_self=True,
    )


def get_sub_roles(roles_q, person_id):
    return (
        DepartmentStaff.objects
        .filter(
            role_id=DepartmentRoles.CHIEF.value,
        )
        .filter(roles_q)
        .exclude(staff_id=person_id)
        .values(
            'staff_id',
            'staff__department__tree_id',
            'staff__department__lft',
            'staff__department__rght',
            'department__tree_id',
            'department__lft',
            'department__rght',
            'department__level',
        )
        .order_by(
            'department__tree_id',
            'department__lft',
        )
    )


def get_direct_roles(sub_roles):
    direct_roles = {}

    for role in sub_roles:
        level = role['department__level'] - 1
        parent = None
        while level > 0:
            p_roles = direct_roles.get(
                (role['department__tree_id'], level), []
            )
            level -= 1
            for p_role in p_roles:
                is_parent_role = (
                    p_role['department__lft'] < role['department__lft']
                    and
                    p_role['department__rght'] > role['department__rght']
                )
                if is_parent_role:
                    parent = role
                    break

            if parent is not None:
                break

        if parent is None:
            direct_roles.setdefault(
                (role['department__tree_id'], role['department__level']), []
            ).append(role)

    return list(chain(*direct_roles.values()))


def get_direct_chiefs_q(roles, direct_roles):
    direct_chiefs = []
    for role in direct_roles:
        direct_sub = (
            # не является подчиненным руководителей
            # в структуре нашего руководителя
            not any(
                role['staff_id'] != r['staff_id']
                and
                role['staff__department__tree_id'] == r['department__tree_id']
                and
                role['staff__department__lft'] >= r['department__lft']
                and
                role['staff__department__rght'] <= r['department__rght']
                for r in direct_roles
            )
            # является подчиненым нашего руководителя
            and any(
                role['staff__department__tree_id'] == r['department__tree_id']
                and
                role['staff__department__lft'] >= r['department__lft']
                and
                role['staff__department__rght'] <= r['department__rght']
                for r in roles
            )
        )
        if direct_sub:
            direct_chiefs.append(role['staff_id'])

    if direct_chiefs:
        return Q(id__in=direct_chiefs)
    else:
        return Q(id=-1)


def direct_subordinates_q(person_id):
    roles = get_roles(person_id)
    roles_q = get_roles_q(roles)
    sub_roles = get_sub_roles(roles_q, person_id)
    direct_roles = get_direct_roles(sub_roles)
    direct_chiefs_q = get_direct_chiefs_q(roles, direct_roles)
    direct_roles_q = get_heirarchy_filter_query(
        direct_roles,
        by_children=True,
        filter_prefix='department__',
        prefix='department__',
        include_self=True,
    )
    return (
        ~Q(id=person_id)
        &
        ((roles_q & ~direct_roles_q) | direct_chiefs_q)
    )


def all_subordinates_q(person_id):
    roles = get_roles(person_id)
    return (
        ~Q(id=person_id)
        &
        get_roles_q(roles)
    )
