from typing import Tuple

from django.contrib.auth import get_user_model
from django.db.models import F
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from rest_framework.exceptions import ValidationError

from lms.calendars.tasks import update_student_event_attendance_task

from .models import StudentSlot, Timeslot, TimeslotExchange

User = get_user_model()


def refresh_timeslot_participants(timeslot: Timeslot, commit: bool = False) -> None:
    statuses = StudentSlot.StatusChoices
    cnt_students = timeslot.students.filter(status=statuses.ACCEPTED).count()

    if cnt_students != timeslot.num_participants:
        timeslot.num_participants = cnt_students

    if commit:
        timeslot.save()


def update_timeslot_participants(timeslot: Timeslot, value: int = 1) -> None:
    if isinstance(value, int) and value:
        Timeslot.objects.filter(id=timeslot.pk).update(num_participants=F("num_participants") + value)


def delete_timeslot_exchanges(student_slot: StudentSlot) -> None:
    TimeslotExchange.objects.filter(student_slot=student_slot).delete()


def reject_from_slot_on_expell_student(student_id: int):
    """
    При отчислении студента отчисляем его со всех будущих слотов
    """
    studentslots_to_reject = (
        StudentSlot.objects
        .filter(
            student_id=student_id, timeslot__begin_date__gt=timezone.now(), status=StudentSlot.StatusChoices.ACCEPTED,
        )
        .select_related('timeslot')
    )
    timeslots_to_decrement = set(
        studentslots_to_reject.filter(timeslot__num_participants__gt=0).values_list('timeslot_id', flat=True)
    )
    studentslots_to_reject.update(status=StudentSlot.StatusChoices.REJECTED)
    Timeslot.objects.filter(id__in=timeslots_to_decrement).update(num_participants=F("num_participants") - 1)

    for timeslot_id in timeslots_to_decrement:
        update_student_event_attendance_task.delay(timeslot_id=timeslot_id, student_id=student_id)


def change_timeslot(student_slot: StudentSlot, target_timeslot_id: int) -> 'StudentSlot':
    """
    Перезапись на другой таймслот
    """
    if student_slot.timeslot_id == target_timeslot_id:
        raise ValidationError(_("Нельзя обменяться на тот же слот"), code="same_timeslot")

    try:
        target_timeslot = Timeslot.objects.get(classroom_id=student_slot.timeslot.classroom_id, pk=target_timeslot_id)
    except Timeslot.DoesNotExist:
        raise ValidationError(_("Такого слота нет в занятии"), code="not_existing_timeslot")

    student_slot.cancel(_("Произошла перезапись на слот {}").format(target_timeslot))

    return StudentSlot.objects.create(
        timeslot=target_timeslot,
        student=student_slot.student,
        status=StudentSlot.StatusChoices.ACCEPTED,
    )


def swap_student_slots(slot: StudentSlot, target_slot: StudentSlot) -> Tuple['StudentSlot', 'StudentSlot']:
    exchange_reason = _('Произошел обмен на слот {}').format(target_slot.timeslot)
    slot.exchange(exchange_reason)

    exchange_reason = _('Произошел обмен на слот {}').format(slot.timeslot)
    target_slot.exchange(exchange_reason)

    exchanged_slot = StudentSlot.objects.create(
        timeslot_id=target_slot.timeslot_id,
        student_id=slot.student_id,
        status=StudentSlot.StatusChoices.ACCEPTED,
    )

    exchanged_target_slot = StudentSlot.objects.create(
        timeslot_id=slot.timeslot_id,
        student_id=target_slot.student_id,
        status=StudentSlot.StatusChoices.ACCEPTED,
    )

    return exchanged_slot, exchanged_target_slot


def accept_exchange(exchange: TimeslotExchange, target_student_slot: StudentSlot) -> TimeslotExchange:
    exchanged_student_slot, exchanged_target_target_slot = swap_student_slots(
        exchange.student_slot, target_student_slot
    )

    exchange.is_active = False
    exchange.target_student_slot = target_student_slot
    exchange.exchanged_student_slot = exchanged_student_slot
    exchange.exchanged_target_student_slot = exchanged_target_target_slot

    exchange.save()

    return exchange
