from django_filters import rest_framework as filters
from drf_spectacular.utils import OpenApiParameter, extend_schema

from django.db.models import Case, Prefetch, Q, When
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _

from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticated
from rest_framework.serializers import ModelSerializer

from lms.core.views.mixins import IsPreviewMixin
from lms.core.views.pagination import LimitOffsetAllPagination
from lms.core.views.viewsets import APIModelViewSet
from lms.courses.models import Course, CourseStudent
from lms.tracker.models import EnrolledUserTrackerIssue

from ..models import EnrolledUser, Enrollment, EnrollSurvey
from ..serializers import (
    DeprecatedUserEnrollDetailSerializer, EnrolledUserCreateSerializer, EnrollmentDetailSerializer,
    EnrollmentListSerializer, EnrollSurveyDetailSerializer, UserEnrollDetailSerializer, UserEnrollListSerializer,
)


class CourseEnrollmentViewSet(IsPreviewMixin, APIModelViewSet):
    serializer_class = EnrollmentListSerializer
    queryset = Enrollment.objects.active()
    permission_classes = [
        IsAuthenticated,
    ]
    pagination_class = None
    lookup_url_kwarg = 'pk'
    lookup_field = 'course_id'
    filter_backends = [filters.DjangoFilterBackend]
    filterset_fields = ['is_active']

    def get_queryset(self):
        queryset = super().get_queryset()

        if self.action == 'list':
            course_id = self.kwargs['pk']
            user = self.request.user
            qs = Course.objects.active(user=user, preview=self.is_preview)
            get_object_or_404(qs, pk=course_id)
            filter_kwargs = {'course_id': self.kwargs['pk']}
            queryset = queryset.filter(**filter_kwargs)
        return queryset

    @extend_schema(
        parameters=[
            OpenApiParameter(name='is_active', required=False, type=bool),
            OpenApiParameter(name='preview', required=False, type=bool),
        ],
        summary=gettext("Список методов зачисления на курс")
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class EnrollmentViewSet(APIModelViewSet):
    serializer_class = EnrollmentDetailSerializer
    queryset = Enrollment.objects.active()
    permission_classes = [
        IsAuthenticated,
    ]
    pagination_class = None

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


class EnrollSurveyViewSet(APIModelViewSet):
    serializer_class = EnrollSurveyDetailSerializer
    queryset = EnrollSurvey.objects.active()
    permission_classes = [
        IsAuthenticated,
    ]
    pagination_class = None

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


class EnrolledUserViewSet(APIModelViewSet):
    serializer_class = EnrolledUserCreateSerializer
    serializer_classes = {
        'retrieve': DeprecatedUserEnrollDetailSerializer,
    }
    queryset = EnrolledUser.objects.all()
    permission_classes = [
        IsAuthenticated,
    ]
    lookup_url_kwarg = 'pk'
    lookup_field = 'course_id'
    pagination_class = None

    _cached_course = None

    def get_queryset(self):
        queryset = super().get_queryset()

        user = getattr(self.request, 'user', None)
        if user:
            queryset = queryset.filter(user=user)

        return queryset

    def get_course(self):
        if not self._cached_course:
            course_id = self.kwargs[self.lookup_url_kwarg]
            course = get_object_or_404(Course.objects.active().select_related('visibility'), pk=course_id)
            if not course.available_for_enroll:
                raise ValidationError(_("курс недоступен для записи"))
            if hasattr(course, 'visibility') and not course.visibility.available_for(user=self.request.user):
                raise ValidationError(_("курс недоступен для пользователя"))

            self._cached_course = course

        return self._cached_course

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['course'] = self.get_course()
        return context

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

    def perform_create(self, serializer: ModelSerializer):
        course = self.get_course()
        user = self.request.user
        serializer.save(course=course, user=user)


class UserEnrollFilter(filters.FilterSet):
    status = filters.ChoiceFilter(
        choices=list(dict(EnrolledUser.StatusChoices.choices + CourseStudent.StatusChoices.choices).items()),
        method='filter_status'
    )
    statuses = filters.MultipleChoiceFilter(
        choices=list(dict(EnrolledUser.StatusChoices.choices + CourseStudent.StatusChoices.choices).items()),
        method='filter_statuses'
    )
    type = filters.ChoiceFilter(field_name='course__course_type', choices=Course.TypeChoices.choices)

    class Meta:
        model = EnrolledUser
        fields = [
            'course_id', 'group_id', 'status',
        ]

    def filter_status(self, qs, name, value):
        return self.filter_statuses(qs=qs, name=name, value=[value])

    def filter_statuses(self, qs, name, value):
        specific_filters = {
            # Заявку отклонили не приняв. Т.е. из статуса PENDING она сразу
            # перешла в REJECTED. Если убрать условие course_student__isnull=True,
            # то в выдачу попадут отчисленные студенты
            EnrolledUser.StatusChoices.REJECTED: Q(
                status=EnrolledUser.StatusChoices.REJECTED, course_student__isnull=True
            ),
            CourseStudent.StatusChoices.ACTIVE: Q(
                course_student__status=CourseStudent.StatusChoices.ACTIVE, course_student__is_passed=False
            ),
            CourseStudent.StatusChoices.COMPLETED: Q(
                course_student__status=CourseStudent.StatusChoices.ACTIVE, course_student__is_passed=True
            ) | Q(
                course_student__status=CourseStudent.StatusChoices.COMPLETED
            ),
        }
        specific_statuses = set()
        course_student_statuses = set()
        enrolled_user_statuses = set()
        for item in value:
            if item in specific_filters:
                specific_statuses.add(item)
                continue
            if item in CourseStudent.StatusChoices:
                course_student_statuses.add(item)
            if item != EnrolledUser.StatusChoices.COMPLETED and item in EnrolledUser.StatusChoices:
                enrolled_user_statuses.add(item)

        qs_filter = Q(status__in=enrolled_user_statuses) | Q(course_student__status__in=course_student_statuses)
        for specific_status in specific_statuses:
            qs_filter |= specific_filters[specific_status]
        return qs.filter(qs_filter)


class UserEnrollViewSet(APIModelViewSet):
    serializer_class = UserEnrollListSerializer
    serializer_classes = {
        "retrieve": UserEnrollDetailSerializer,
    }
    queryset = EnrolledUser.objects.select_related(
        'course', 'group', 'course_student'
    ).prefetch_related(
        Prefetch(
            lookup='issues',
            queryset=EnrolledUserTrackerIssue.objects.filter(is_default=True),
        ),
        'course__categories',
        'course_student__course_progresses',
    ).annotate(
        date_modified=Case(
            When(
                course_student__modified__isnull=False,
                then='course_student__modified'
            ),
            default='modified')
    ).order_by('-date_modified')

    permission_classes = [
        IsAuthenticated,
    ]
    pagination_class = LimitOffsetAllPagination
    filter_backends = [filters.DjangoFilterBackend]
    filterset_class = UserEnrollFilter

    def get_queryset(self):
        queryset = super().get_queryset()
        user = getattr(self.request, 'user')

        return queryset.filter(user=user)

    @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)
