from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema

from django.core.exceptions import ValidationError as DjangoValidationError
from django.db.models import Prefetch
from django.utils.translation import gettext

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.filters import OrderingFilter
from rest_framework.generics import get_object_or_404

from lms.classrooms.models import Classroom
from lms.core.views.pagination import LimitOffsetAllPagination
from lms.core.views.viewsets import LabModelViewSet
from lms.courses.models import Course
from lms.courses.permissions import CourseObjectPermission
from lms.courses.views import GetCourseMixin
from lms.enrollments.models import EnrolledUser, Enrollment
from lms.enrollments.serializers import EnrolledUserDetailLabSerializer, EnrolledUserUpdateLabSerializer
from lms.enrollments.views.labapi import EnrolledUserLabFilterSet, EnrollmentLabViewSetBase

from ..models import (
    ClassroomQueue, ClassroomTracker, ClassroomTrackerQueue, EnrolledUserTrackerIssue, EnrollmentQueue,
    EnrollmentTrackerIssue, EnrollmentTrackerQueue, TrackerQueue,
)
from ..serializers import (
    ClassroomCreateLabSerializer, ClassroomDetailLabSerializer, ClassroomDetailNoTimeslotsLabSerializer,
    ClassroomQueueListLabSerializer, ClassroomTrackerCreateLabSerializer, ClassroomTrackerDetailLabSerializer,
    ClassroomTrackerQueueCreateLabSerializer, ClassroomTrackerQueueLabSerializer, ClassroomTrackerUpdateLabSerializer,
    ClassroomUpdateLabSerializer, EnrolledUserListLabSerializer, EnrollmentQueueListLabSerializer,
    EnrollmentTrackerCreateLabSerializer, EnrollmentTrackerDetailSerializer, EnrollmentTrackerQueueCreateLabSerializer,
    EnrollmentTrackerQueueDetailLabSerializer, EnrollmentTrackerQueueListLabSerializer,
    EnrollmentTrackerQueueUpdateLabSerializer, EnrollmentTrackerUpdateLabSerializer, TrackerQueueLabSerializer,
)


class EnrollmentTrackerQueueLabViewSet(LabModelViewSet):
    permission_classes = LabModelViewSet.permission_classes + [CourseObjectPermission]
    serializer_class = EnrollmentTrackerQueueListLabSerializer
    serializer_classes = {
        'create': EnrollmentTrackerQueueCreateLabSerializer,
        'update': EnrollmentTrackerQueueUpdateLabSerializer,
        'partial_update': EnrollmentTrackerQueueUpdateLabSerializer,
        'retrieve': EnrollmentTrackerQueueDetailLabSerializer,
    }
    queryset = (
        EnrollmentTrackerQueue.objects.active()
        .select_related('enrollment', 'enrollment__course')
        .prefetch_related('enrollment_tracker_issues')
    )
    pagination_class = LimitOffsetAllPagination
    lookup_url_kwarg = 'pk'

    _cached_enrollment = None

    def get_course(self, obj=None):
        if obj is not None:
            return obj.enrollment.course
        if self.action == 'create':
            return get_object_or_404(Course.objects.all(), enrollments__id=self.request.data.get('enrollment_id'))
        if self.action == 'list':
            return get_object_or_404(Course.objects.all(), enrollments__id=self.kwargs.get('pk'))

    def get_queryset(self):
        queryset = super().get_queryset()
        if self.action == 'list':
            enrollment_id = self.kwargs[self.lookup_url_kwarg]
            get_object_or_404(Enrollment, pk=enrollment_id)
            filter_kwargs = {'enrollment_id': enrollment_id}
            queryset = queryset.filter(**filter_kwargs)

        return queryset

    @extend_schema(
        summary=gettext("Список очередей на зачисление")
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Информация по очереди на зачисление")
    )
    def retrieve(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Создание новой очереди на зачисление")
    )
    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Обновление очереди на зачисление")
    )
    def update(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Частичное обновление очереди на зачисление")
    )
    def partial_update(self, request, *args, **kwargs):
        return super().partial_update(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Удаление очереди на зачисление")
    )
    def destroy(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)


class TrackerQueueLabViewSet(LabModelViewSet):
    serializer_class = TrackerQueueLabSerializer
    serializer_classes = {
        'list': TrackerQueueLabSerializer,
    }
    queryset = TrackerQueue.objects.active()
    pagination_class = LimitOffsetAllPagination

    @extend_schema(
        summary=gettext("Список настроек очередей"),
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class ClassroomWithTrackerQueueLabViewSet(GetCourseMixin, LabModelViewSet):
    """
    Занятия с расписанием (с настройкой очередей)
    """
    serializer_class = ClassroomDetailLabSerializer
    serializer_classes = {
        "create": ClassroomCreateLabSerializer,
        "update": ClassroomUpdateLabSerializer,
        "partial_update": ClassroomUpdateLabSerializer,
    }
    queryset = (
        Classroom.objects
        .select_related('course')
        .prefetch_related('timeslots', 'timeslots__course_groups', 'tracker')
    )
    pagination_class = LimitOffsetAllPagination
    permission_classes = LabModelViewSet.permission_classes + [CourseObjectPermission]

    def get_retrieve_serializer(self, *args, **kwargs):
        kwargs['context'] = self.get_serializer_context()
        return ClassroomDetailNoTimeslotsLabSerializer(*args, **kwargs)

    @extend_schema(
        responses={200: ClassroomDetailLabSerializer},
        summary=gettext("Информация по занятий с расписанием")
    )
    def retrieve(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    @extend_schema(
        request=ClassroomCreateLabSerializer,
        responses={201: ClassroomDetailLabSerializer},
        summary=gettext("Создание занятия с расписанием"),
    )
    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)

    @extend_schema(
        request=ClassroomUpdateLabSerializer,
        responses={200: ClassroomDetailLabSerializer},
        summary=gettext("Обновление занятия с расписанием"),
    )
    def update(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    @extend_schema(
        request=ClassroomUpdateLabSerializer,
        responses={200: ClassroomDetailLabSerializer},
        summary=gettext("Частичное обновление занятия с расписанием"),
    )
    def partial_update(self, request, *args, **kwargs):
        return super().partial_update(request, *args, **kwargs)


class ClassroomQueueLabViewSet(LabModelViewSet):
    permission_classes = LabModelViewSet.permission_classes
    serializer_class = ClassroomQueueListLabSerializer
    queryset = ClassroomQueue.objects.all()

    @extend_schema(
        summary=gettext("Список доступных очередей для занятий")
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


# DEPRECATED
class ClassroomTrackerLabViewSet(LabModelViewSet):
    permission_classes = LabModelViewSet.permission_classes + [CourseObjectPermission]
    serializer_class = ClassroomTrackerDetailLabSerializer
    serializer_classes = {
        'create': ClassroomTrackerCreateLabSerializer,
        'update': ClassroomTrackerUpdateLabSerializer,
        'partial_update': ClassroomTrackerUpdateLabSerializer,
    }
    queryset = ClassroomTracker.objects.all()
    lookup_url_kwarg = 'pk'
    lookup_field = 'classroom_id'
    pagination_class = None

    def get_classroom(self) -> 'Classroom':
        if not hasattr(self, '_classroom'):
            classroom_id = self.kwargs.get('pk')
            self._classroom = get_object_or_404(Classroom.objects.select_related('course'), pk=classroom_id)

        return self._classroom

    def get_course(self, obj=None) -> 'Course':
        if obj is None:
            if self.action in ['create', 'list']:
                return self.get_classroom().course

        return obj.classroom.course

    @extend_schema(
        summary=gettext("Просмотр очереди у занятия по расписанию")
    )
    def retrieve(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Привязать занятие по расписанию к очереди"),
    )
    def create(self, request, *args, **kwargs):
        try:
            return super().create(request, *args, **kwargs)
        except DjangoValidationError as exc:
            raise ValidationError(detail=serializers.as_serializer_error(exc))

    def perform_create(self, serializer):
        classroom = self.get_classroom()
        serializer.save(classroom=classroom)

    @extend_schema(
        summary=gettext("Привязать занятие по расписанию к другой очереди")
    )
    def update(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Привязать занятие по расписанию к другой очереди")
    )
    def partial_update(self, request, *args, **kwargs):
        return super().partial_update(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Отвязать занятие по расписанию от очереди")
    )
    def destroy(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)


# DEPRECATED
class ClassroomTrackerQueueLabViewSet(LabModelViewSet):
    permission_classes = LabModelViewSet.permission_classes + [CourseObjectPermission]
    serializer_class = ClassroomTrackerQueueLabSerializer
    serializer_classes = {
        'list': ClassroomTrackerQueueLabSerializer,
        'retrieve': ClassroomTrackerQueueLabSerializer,
        'create': ClassroomTrackerQueueCreateLabSerializer,
    }
    queryset = ClassroomTrackerQueue.objects.select_related('classroom', 'classroom__course', 'queue')
    pagination_class = None

    def get_classroom(self) -> 'Classroom':
        if not hasattr(self, '_classroom'):
            if self.action == 'create':
                classroom_id = self.request.data.get('classroom_id')
            if self.action == 'list':
                classroom_id = self.kwargs.get('pk')
            self._classroom = get_object_or_404(Classroom.objects.select_related('course'), pk=classroom_id)

        return self._classroom

    def get_course(self, obj=None) -> 'Course':
        if obj is None:
            if self.action in ['create', 'list']:
                return self.get_classroom().course

        return obj.classroom.course

    def get_queryset(self):
        queryset = super().get_queryset()
        if self.action == 'list':
            classroom_id = self.get_classroom().id
            filter_kwargs = {'classroom_id': classroom_id}
            queryset = queryset.filter(**filter_kwargs)

        return queryset

    @extend_schema(
        summary=gettext("Список очередей занятия по расписанию")
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Очередь занятия по расписанию")
    )
    def retrieve(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Привязать очередь к занятию по расписанию"),
    )
    def create(self, request, *args, **kwargs):
        try:
            return super().create(request, *args, **kwargs)
        except DjangoValidationError as exc:
            raise ValidationError(detail=serializers.as_serializer_error(exc))

    @extend_schema(
        summary=gettext("отвязать очередь от занятия по расписанию")
    )
    def destroy(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)


class EnrolledUserLabViewSet(LabModelViewSet):
    permission_classes = LabModelViewSet.permission_classes + [CourseObjectPermission]
    serializer_class = EnrolledUserListLabSerializer
    serializer_classes = {
        'list': EnrolledUserListLabSerializer,
        'retrieve': EnrolledUserDetailLabSerializer,
        'partial_update': EnrolledUserUpdateLabSerializer,
    }

    queryset = (
        EnrolledUser.objects
        .select_related('course', 'group', 'user', 'user__staffprofile')
        .prefetch_related(
            Prefetch(
                lookup='enrollment_tracker_issues',
                queryset=EnrollmentTrackerIssue.objects.filter(queue__is_default=True).select_related('queue'),
            ),
            Prefetch(
                lookup='issues',
                queryset=EnrolledUserTrackerIssue.objects.filter(is_default=True),
            ),
            'user__staffprofile__groups',
        )
    )

    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_class = EnrolledUserLabFilterSet
    ordering_fields = ['user__username', 'enroll_date', 'completion_date', 'created', 'status']

    def get_course(self, obj=None):
        if not hasattr(self, '_course'):
            if obj is not None:
                self._course = obj.course
            if self.action in ['list', 'download']:
                self._course = get_object_or_404(Course.objects.all(), pk=self.kwargs.get('pk'))

        return self._course

    def get_queryset(self):
        queryset = super().get_queryset()
        if self.action == 'list':
            course_id = self.kwargs['pk']
            filter_kwargs = {'course_id': course_id}
            queryset = queryset.filter(**filter_kwargs)

        return queryset

    @extend_schema(
        summary=gettext("Список заявок на зачисление"),
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Изменение заявки на зачисление"),
    )
    def partial_update(self, request, *args, **kwargs):
        return super().partial_update(request, *args, **kwargs)


class EnrollmentTrackerViewSet(EnrollmentLabViewSetBase):
    serializer_class = EnrollmentTrackerDetailSerializer
    serializer_classes = {
        'retrieve': EnrollmentTrackerDetailSerializer,
        'create': EnrollmentTrackerCreateLabSerializer,
        'update': EnrollmentTrackerUpdateLabSerializer,
        'partial_update': EnrollmentTrackerUpdateLabSerializer,
    }

    ENROLL_TYPE = Enrollment.TYPE_TRACKER

    @extend_schema(
        summary=gettext("Информация по методу зачисления через трекер")
    )
    def retrieve(self, request, *args, **kwargs):
        return super().retrieve(request, *args, **kwargs)

    @extend_schema(
        request=EnrollmentTrackerCreateLabSerializer,
        responses={201: EnrollmentTrackerDetailSerializer},
        summary=gettext("Создание метода зачисления через трекер"),
    )
    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)

    @extend_schema(
        request=EnrollmentTrackerUpdateLabSerializer,
        responses={200: EnrollmentTrackerDetailSerializer},
        summary=gettext("Обновление метода зачисления через трекер"),
    )
    def update(self, request, *args, **kwargs):
        return super().update(request, *args, **kwargs)

    @extend_schema(
        request=EnrollmentTrackerUpdateLabSerializer,
        responses={200: EnrollmentTrackerDetailSerializer},
        summary=gettext("Частичное обновление метода зачисления через трекер"),
    )
    def partial_update(self, request, *args, **kwargs):
        return super().partial_update(request, *args, **kwargs)

    @extend_schema(
        summary=gettext("Удаление метода зачисления через трекер"),
        responses={204: None},
    )
    def destroy(self, request, *args, **kwargs):
        return super().destroy(request, *args, **kwargs)


class EnrollmentQueueLabViewSet(LabModelViewSet):
    serializer_class = EnrollmentQueueListLabSerializer
    serializer_classes = {
        'list': EnrollmentQueueListLabSerializer,
    }
    queryset = EnrollmentQueue.objects.active()
    pagination_class = LimitOffsetAllPagination

    @extend_schema(
        summary=gettext("Список очередей для заявок"),
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)
