import logging

from staff.lib import waffle
import yenv
from django.conf import settings

from staff.departments.models import Department, DepartmentRoles, DepartmentStaff
from staff.groups.models import GroupMembership
from staff.lib import requests
from staff.map.models import Table
from staff.person_avatar.handlers import update_gravatar
from staff.person_avatar.models import AvatarMetadata

from staff.person.models import AFFILIATION, PHONE_KIND, PHONE_TYPES, StaffPhone
from staff.person.passport.internal import IntPassport
from staff.person.tasks.ad import actualize_work_phone_task, push_person_data_to_ad_task
from staff.person.tasks.email import SendWelcomeMail
from staff.person.tasks.yandex_disk import ActualizeYandexDisk
from staff.person.models import Contact
from staff.person.notifications import update_survey_mail_date


logger = logging.getLogger('person.effects.base')

ROBOT_SHELL = '/bin/false'


def actualize_is_big_boss(person):
    """Если сотрудник больше никакакой не руководитель, нужно снять галку"""
    person.is_big_boss = (
        person.departmentstaff_set
        .filter(role_id=DepartmentRoles.CHIEF.value)
        .exists()
    )


def delete_roles(person):
    roles_to_delete = [DepartmentRoles.CHIEF.value, DepartmentRoles.DEPUTY.value]
    qs = DepartmentStaff.objects.filter(role__in=roles_to_delete, staff=person.instance)
    for role in qs:
        role.delete()


def actualize_group(person):
    """Актуализируются членства в департаменнтных группах"""
    memberships = (
        GroupMembership.objects
        .exclude(group__department=None)
        .filter(staff=person.instance)
        .iterator()
    )

    try:
        membership = next(memberships)
    except StopIteration:
        GroupMembership.objects.create(
            group=person.department.group,
            staff=person.instance
        )
        return
    membership.group = person.department.group
    membership.save(force_update=True)

    # На всякий случай удалим другие связи, если они есть
    for membership in memberships:
        membership.delete()


def actualize_affiliation(person):
    """Прописывает корректную принадлежность сотрудника"""
    try:
        root_dep_id = (
            person.department
            .get_ancestors(include_self=True)
            .filter(id__in=[
                settings.YANDEX_DEPARTMENT_ID,
                settings.YAMONEY_DEPARTMENT_ID,
            ])
            .values_list('id', flat=True)
        )[0]

        if root_dep_id == settings.YAMONEY_DEPARTMENT_ID:
            person.affiliation = AFFILIATION.YAMONEY
        elif root_dep_id == settings.YANDEX_DEPARTMENT_ID:
            person.affiliation = AFFILIATION.YANDEX

    except IndexError:
        person.affiliation = AFFILIATION.EXTERNAL


def actualize_django_user(person):
    """Прописывает правильные данные в пользователя"""
    user = person.user
    user.username = person.login
    user.first_name = person.first_name
    user.last_name = person.last_name
    user.email = person.email
    user.save()

    person.user = user


def actualize_is_homeworker(person):
    person.is_homeworker = (
        person.office_id == settings.HOMIE_OFFICE_ID
    )
    actualize_yandex_disk(person)


def actualize_is_robot(person):
    person.is_robot = (
        person.office_id == settings.VIRTUAL_OFFICE_ID
    )


def actualize_robot_shell(person):
    if person.is_robot:
        person.shell = ROBOT_SHELL


def actualize_work_email(person):
    person.work_email = '%s@%s' % (
        person.login, person.get_domain_display()
    )


def actualize_table_by_table(person):
    if person.table:
        for reserve in person.table.table_reserve.all():
            reserve.intranet_status = 0
            reserve.save()


def actualize_room_by_table(person):
    if person.table:
        person.room = person.table.room


def actualize_table_by_office(person):
    """Если меняется офис надо поправить стол"""
    if person.table and person.table.floor.office != person.office:
        person.table = None


def actualize_room_by_office(person):
    """Если меняется офис надо поправить комнату"""
    if person.room and person.room.floor.office != person.office:
        person.room = None


def actualize_tz(person):
    """Подтягивает часовой пояс из офиса, если оно надо"""
    if not person.tz and person.office:
        person.tz = person.office.tz


def actualize_work_phone(person):
    task = actualize_work_phone_task
    if yenv.type == 'development':
        # it's not working in testing
        return
    else:
        task.apply_async(kwargs={'person_login': person.login}, countdown=30)


def push_person_data_to_ad(person):
    if not waffle.switch_is_active('enable_ad_person_sync') or person.is_dismissed:
        logger.info('push_person_data_to_ad for %s (%s) skipped', person.login, person.is_dismissed)
        return

    # У AD нет тестинга, поэтому ходим только с прода
    if yenv.type == 'production':
        return push_person_data_to_ad_task.apply_async(kwargs={'person_login': person.login}, countdown=40)
    else:
        logger.info('push_person_data_to_ad for %s skipped', person.login)


def actualize_phones(person):
    """
    Заполняем строковые поля mobile_phone и home_phone в переданном контроллере person
    Не вызываем person.save(), т.к. эта ф-я сама вызывается только из из PersonCtl.save()
    """
    phones = (
        StaffPhone.objects.values('id', 'number', 'type')
        .filter(
            staff_id=person.id,
            intranet_status=1,
            kind__in=[PHONE_KIND.COMMON, PHONE_KIND.MONITORING]
        )
        .order_by('position')
    )

    mobile_numbers = []
    home_numbers = []
    for phone in phones:
        if phone['type'] == PHONE_TYPES.HOME:
            home_numbers.append(phone['number'])
        else:
            mobile_numbers.append(phone['number'])

    join = ', '.join

    _mobile_phone = join(mobile_numbers)
    _home_phone = join(home_numbers)

    if (person.mobile_phone != _mobile_phone or
            person.home_phone != _home_phone):
        person.update(
            {
                'mobile_phone': _mobile_phone,
                'home_phone': _home_phone
            }
        )


def start_emailing(person):
    if person.instance.affiliation == AFFILIATION.YANDEX:
        update_survey_mail_date(person.instance)

    send_welcome = SendWelcomeMail
    if yenv.type != 'development':
        send_welcome = send_welcome.delay

    send_welcome([person.id])


def actualize_gravatar(person):
    update_gravatar(person.instance, person.old_state)


def set_avatar_from_preprofile(person):
    if not person._preprofile_id:
        return

    avatar_meta = AvatarMetadata.objects.filter(
        is_deleted=False,
        preprofile_id=person._preprofile_id,
    ).last()
    if not avatar_meta:
        logger.info('No photo found for preprofile %s', person._preprofile_id)
        return

    AvatarMetadata.objects.filter(person_id=person.id).update(
        is_main=False,
        is_avatar=False,
    )

    avatar_meta.person_id = person.id
    avatar_meta.save()


def activate_user(person):
    person.user.is_active = True
    person.user.save()


def create_personal_email(person):
    personal_email_contact_type_id = 1
    if person.home_email:
        Contact.objects.create(
            contact_type_id=personal_email_contact_type_id,
            person_id=person.id,
            account_id=person.home_email,
        )


def actualize_yandex_disk(person):
    if yenv.type in ['development', 'testing']:
        # its not working in development
        # its not available in testing
        return

    ActualizeYandexDisk.apply_async(kwargs={'person_uid': person.uid, 'person_login': person.login}, countdown=30)


def drop_department_roles(person):
    roles_to_exclude = (DepartmentRoles.CHIEF.value, DepartmentRoles.DEPUTY.value)
    qs = (
        DepartmentStaff.objects
        .filter(staff_id=person.id)
        .exclude(role_id__in=roles_to_exclude)  # chief role dropped through Proposal in preprofile
    )
    for department_staff in qs:
        department_staff.delete()


def unblock_login_in_passport(person):
    logger.info('Unblocking passport for {login}'.format(login=person.login))
    if yenv.type != 'production':
        logger.info('Skiping unblock as we are not in production')
        return
    passport = IntPassport(person.uid)
    passport.unblock()
    logger.info('Passport unblocked for {login}'.format(login=person.login))


def check_department(person_ctl, department: Department):
    if department.intranet_status < 1:
        raise Exception('You should not set deleted department')


def check_office(person_ctl, table: Table):
    if table is not None and person_ctl.office != table.floor.office:
        raise Exception('You should set table in person office')


def create_user_in_testing(person_ctl):
    preffered_departments = list(
        person_ctl.department
        .get_ancestors(include_self=True)
        .values_list('url', flat=True)
    )

    form = {
        'id': person_ctl.id,
        'login': person_ctl.login,
        'uid': person_ctl.uid,
        'guid': person_ctl.guid,
        'first_name': person_ctl.first_name,
        'first_name_en': person_ctl.first_name_en,
        'middle_name': person_ctl.middle_name,
        'hide_middle_name': person_ctl.hide_middle_name,
        'preferred_name': person_ctl.preferred_name,
        'preferred_name_en': person_ctl.preferred_name_en,
        'last_name': person_ctl.last_name,
        'last_name_en': person_ctl.last_name_en,
        'tz': person_ctl.tz,
        'tree_id': person_ctl.department.tree_id,
        'departments': preffered_departments,
        'gender': person_ctl.gender,
        'office': person_ctl.office_id,
        'organization': person_ctl.organization_id,
        'join_at': person_ctl.join_at.isoformat(),
        'position': person_ctl.position,
        'position_en': person_ctl.position_en,
        'work_email': person_ctl.work_email,
    }
    try:
        res = requests.post(
            f'https://{settings.STAFF_TEST_HOST}/api/create_user_in_testing/',
            json=form,
            headers={'Authorization': 'OAuth %s' % settings.ROBOT_STAFF_OAUTH_TOKEN},
            timeout=(3, 6, 12),
        )
    except Exception as ex:
        logger.error('Error while adding %s to testing. %s', person_ctl.login, str(ex))
        return

    if res.status_code == 400:
        logger.error(res.json())

    if res.status_code != 201:
        logger.error('Error while creating user in testing, check testing logs')
