import logging
from datetime import timedelta

from django.utils import timezone


from intranet.femida.src.calendar import exceptions
from intranet.femida.src.calendar.api import get_event, update_event
from intranet.femida.src.calendar.exceptions import EventDoesNotExist, EventIsNotAvailable
from intranet.femida.src.calendar.helpers import CALENDAR_DT_FORMAT
from intranet.femida.src.celery_app import app
from intranet.femida.src.core.controllers import update_instance
from intranet.femida.src.interviews.models import Interview
from intranet.femida.src.utils.lock import locked_task


logger = logging.getLogger(__name__)


def _sync_calendar_events(interviews):
    logger.info('Need to check %d interview events', len(interviews))
    count = 0
    for interview in interviews:
        changed_data = {}
        try:
            event = get_event(interview.event_id, strict=True)
        except exceptions.EventDoesNotExist:
            # Если встреча не существует (например, её удалили)
            # – затираем все сведения о ней
            changed_data['event_id'] = None
            changed_data['event_start_time'] = None
        except exceptions.EventIsNotAvailable:
            # Если встреча скрыта – затираем время встречи,
            # т.к. мы не можем знать его наверняка.
            # Оставляем event_id, надеясь,
            # что встреча может стать снова доступна
            changed_data['event_start_time'] = None
        except exceptions.CalendarError:
            continue
        else:
            if interview.event_start_time != event.start_time:
                changed_data['event_start_time'] = event.start_time

        if changed_data:
            update_instance(interview, changed_data)
            count += 1

    return count


@app.task
@locked_task
def sync_calendar_events():
    interviews = (
        Interview.unsafe
        .filter(
            state__in=['assigned', 'estimated'],
            event_id__isnull=False,
        )
    )
    count = _sync_calendar_events(interviews)
    logger.info('%d events was updated after full sync with Calendar', count)


@app.task
@locked_task
def sync_nearest_calendar_events():
    soon = timezone.now() + timedelta(hours=1, minutes=5)
    interviews = Interview.unsafe.filter(
        state='assigned',
        event_start_time__lt=soon,
    )
    count = _sync_calendar_events(interviews)
    logger.info('%d events was updated after lite sync with Calendar', count)


@app.autoretry_task(max_retries=3)
def update_event_task(event_id: int, data: dict):
    update_event(event_id, data, strict=True)


@app.autoretry_task(max_retries=3)
def replace_event_attendee_task(event_id, old_attendee_email, new_attendee_email):
    try:
        event = get_event(event_id=event_id, strict=True)
    except (EventDoesNotExist, EventIsNotAvailable):
        return

    emails = {attendee['email'] for attendee in event.attendees}
    emails.discard(old_attendee_email)
    emails.add(new_attendee_email)

    # если не передать переговорки в поле attendees, то они удалятся из встречи
    room_emails = {room['email'] for room in event.rooms}
    emails.update(room_emails)

    data = {
        'attendees': list(emails),
        'startTs': event.start_time.strftime(CALENDAR_DT_FORMAT),
        'endTs': event.end_time.strftime(CALENDAR_DT_FORMAT),
    }

    try:
        update_event(event_id=event.id, data=data, strict=True)
    except (EventDoesNotExist, EventIsNotAvailable):
        pass
