import logging

from django.contrib.auth import get_user_model
from django.utils import timezone
from django.utils.dateparse import parse_date

from intranet.femida.src.celery_app import app, get_retry_countdown
from intranet.femida.src.users.models import UserAbsence
from intranet.femida.src.users.utils import get_recruiter_gaps, get_user_holidays, get_recruiters_qs
from intranet.femida.src.utils.itertools import get_chunks
from intranet.femida.src.utils.lock import locked_task


User = get_user_model()


logger = logging.getLogger(__name__)


@app.task(max_retries=4)
def sync_recruiter_gaps(user_ids, date_from_str, date_to_str, chunk_size=50):
    date_from = parse_date(date_from_str)
    date_to = parse_date(date_to_str)

    username_to_id_map = dict(
        User.objects
        .filter(id__in=user_ids)
        .values_list('username', 'id')
    )
    usernames = list(username_to_id_map.keys())

    absences = []
    failed_usernames = []

    logger.info('Syncing gaps for %d recruiters: %s', len(usernames), ', '.join(usernames))

    for chunk in get_chunks(usernames, chunk_size):
        try:
            gaps = get_recruiter_gaps(chunk, date_from, date_to)
        except Exception:
            logger.exception(
                'Failed syncing gaps for %d recruiters: %s',
                len(chunk),
                ', '.join(chunk),
            )
            failed_usernames.extend(chunk)
            continue

        for username, dt in gaps:
            absences.append(
                UserAbsence(
                    user_id=username_to_id_map[username],
                    date=dt,
                )
            )

    UserAbsence.objects.bulk_create(
        objs=absences,
        batch_size=1000,
        ignore_conflicts=True,
    )

    # Ретраим для логинов, с которыми зафейлились
    if failed_usernames:
        sync_recruiter_gaps.retry(
            countdown=get_retry_countdown(sync_recruiter_gaps.request.retries),
            kwargs={
                'user_ids': [username_to_id_map[k] for k in failed_usernames],
                'date_from_str': date_from_str,
                'date_to_str': date_to_str,
                'chunk_size': chunk_size,
            },
        )


@app.task(max_retries=4)
def sync_recruiter_holidays(user_ids, date_from_str, date_to_str):
    """
    FEMIDA-5554: Таска падает при походе в календарь из-за user-тикета,
    не соответстующего запрашиваемому uid
    """
    date_from = parse_date(date_from_str)
    date_to = parse_date(date_to_str)

    users = list(
        User.objects
        .filter(id__in=user_ids)
        .values('id', 'uid', 'username')
    )

    absences = []
    failed_user_ids = []

    logger.info(
        'Syncing holidays for %d recruiters: %s',
        len(users),
        ', '.join(u['username'] for u in users),
    )

    for user in users:
        try:
            holidays = get_user_holidays(user['uid'], date_from, date_to)
        except Exception:
            logger.exception('Failed syncing holidays for %s', user['username'])
            failed_user_ids.append(user['id'])
            continue

        for dt in holidays:
            absences.append(
                UserAbsence(
                    user_id=user['id'],
                    date=dt,
                )
            )

    UserAbsence.objects.bulk_create(
        objs=absences,
        batch_size=1000,
        ignore_conflicts=True,
    )

    # Ретраим для id, с которыми зафейлились
    if failed_user_ids:
        sync_recruiter_holidays.retry(
            countdown=get_retry_countdown(sync_recruiter_holidays.request.retries),
            args=[],
            kwargs={
                'user_ids': failed_user_ids,
                'date_from_str': date_from_str,
                'date_to_str': date_to_str,
            },
        )


@app.task
@locked_task
def sync_recruiter_absences(date_from_str=None, date_to_str=None):
    """
    Синкает отсутствия рекрутеров
    :param date_from_str: дата начала (по умолчанию сегодня, синк делатся в 23:00 по МСК)
    :param date_to_str: дата окончания (по умолчанию равен `date_from_str`)

    FEMIDA-5554: Таска снята с регулярного запуска
    """
    if date_from_str is None:
        dt = timezone.now().date()
        date_from_str = dt.strftime('%Y-%m-%d')

    date_to_str = date_to_str or date_from_str
    user_ids = list(get_recruiters_qs().values_list('id', flat=True))
    sync_recruiter_gaps.delay(user_ids, date_from_str, date_to_str)
    sync_recruiter_holidays.delay(user_ids, date_from_str, date_to_str)
