from datetime import timedelta, date
from typing import List

from django.conf import settings
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.utils.dateparse import parse_date, parse_datetime

from intranet.femida.src.calendar import get_holidays
from intranet.femida.src.utils.gap import get_gaps, GapTypeEnum


User = get_user_model()


def filter_users_by_aa_type(queryset, aa_type):
    group_id = settings.AA_TYPE_TO_GROUP_ID.get(aa_type)
    if not group_id:
        return queryset.none()
    return queryset.filter(groups__id=group_id)


def _get_users_with_permission(permission_code):
    return (
        User.objects
        .filter(
            Q(groups__permissions__codename=permission_code)
            | Q(user_permissions__codename=permission_code)
        )
        .distinct()
    )


def get_recruiters_qs():
    return _get_users_with_permission('recruiter_perm')


def get_recruiter_gaps(usernames, date_from, date_to):
    """
    Генератор отсутствий рекрутеров из gap.
    Актуально только для рекрутеров, потому что у них
    свои правила, что считать отсутствием, а что нет.

    Генерит кортежи (имя пользователя, дата отсутствия)
    между датами `date_from` и `date_to` включительно
    """
    gaps = get_gaps(
        usernames=usernames,
        date_from=date_from,
        date_to=date_to,
        workflow=(
            GapTypeEnum.illness,
            GapTypeEnum.maternity,
            GapTypeEnum.vacation,
        ),
    )

    for gap in gaps:
        # Интересуют только полные дни, когда пользователь
        # отсутствовал и не работал
        if not gap['full_day'] or gap['work_in_absence']:
            continue

        # Gap в качестве gap['date_to'] отдает 00:00:00 следующего дня после
        # последнего дня отсутствия.
        # Например, если человек отсутствует 7 и 8 января,
        # ответ будет выглядеть так:
        # {
        #   "date_from": "2018-01-07T00:00:00",
        #   "date_to": "2018-01-09T00:00:00"
        # }
        # Поэтому нам нужно генерить даты до gap['date_to'] не включительно.
        # По той же причине, выбирая минимальную дату, до которой нам нужно генерить,
        # мы к date_to добавляем 1 день.
        dt_from = max(date_from, parse_datetime(gap['date_from']).date())
        dt_to = min(date_to + timedelta(days=1), parse_datetime(gap['date_to']).date())

        while dt_from < dt_to:
            yield gap['person_login'], dt_from
            dt_from += timedelta(days=1)


def get_user_holidays(uid: str, date_from: date, date_to: date) -> List[date]:
    """
    Возвращает список выходных для пользователя с uid=`uid`
    между датами `date_from` и `date_to` включительно
    """
    holidays = get_holidays(date_from, date_to, uid)
    return [parse_date(i['date']) for i in holidays]
