import logging

import yenv
from ldap import LDAPError

from staff.lib.tasks import LockedTask

from staff.celery_app import app

from staff.person.ad import (
    LDAPException,
    update_ad_user_avatar,
    update_ad_user_data,
    update_person_from_ad,
)
from staff.person.models import Staff


logger = logging.getLogger(__name__)


@app.task(ignore_result=True, name='staff.person.tasks.ad.SyncActiveDirectory')
class SyncActiveDirectory(LockedTask):
    def _get_method(self):
        if yenv.type == 'development':
            return SyncActiveDirectory
        return SyncActiveDirectory.delay

    def locked_run(self, person_ids=None, **kwargs):
        if person_ids is None:
            method = self._get_method()
            person_ids = list(
                Staff.objects
                .filter(is_dismissed=False, is_robot=False)
                .values_list('id', flat=True)
            )
            # в итоге может быть запущено на одну задачу больше, если не делится нацело, не хочу мудрить
            max_packs = 50
            pack_len = len(person_ids) // max_packs if len(person_ids) >= max_packs else 1
            i = 0
            while i < len(person_ids):
                pack = person_ids[i:i + pack_len]
                method(person_ids=pack, nolock=True)
                i += pack_len

        else:
            for person in Staff.objects.filter(id__in=person_ids):
                try:
                    update_person_from_ad(person)
                except Exception:
                    logger.exception('Can\'t update person with login %s.', person.login)
                    if yenv.type == 'development':
                        raise


@app.task(name='staff.person.tasks.ad.actualize_work_phone_task')
def actualize_work_phone_task(person_login: str) -> None:
    """Получаем work_phone из AD"""
    person = Staff.objects.get(login=person_login)
    update_person_from_ad(person)


@app.task(name='staff.person.tasks.ad.push_person_data_to_ad_task')
def push_person_data_to_ad_task(person_login: str) -> None:
    """Отправляем данные сотрудника в AD"""
    person = (
        Staff.objects
        .select_related('department', 'office__city__country')
        .prefetch_related('department_roles')
        .get(login=person_login)
    )

    if not person.affiliation:
        logger.error('Affiliation is not set for person %s', person.login)
        return

    try:
        update_ad_user_data(person)
        update_ad_user_avatar(person)
    except (LDAPException, LDAPError) as exc:
        logger.exception('Error trying to upload person data for login %s to AD: %s', person_login, exc)
    else:
        logger.info('Person data for login %s successfully uploaded to AD', person.login)
