from functools import partial
from typing import List, Optional

from django.conf import settings
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers

from lms.classrooms.models import Classroom
from lms.classrooms.serializers import TimeslotListInlineLabSerializer
from lms.core.validators import validate_unique_elements
from lms.courses.models import Course
from lms.courses.serializers import (
    CourseGroupInlineSerializer, CourseModuleCreateBaseSerializer, CourseModuleDetailBaseSerializer,
    CourseModuleUpdateBaseSerializer,
)
from lms.enrollments.models import EnrolledUser, Enrollment, EnrollSurvey
from lms.staff.serializers import StaffGroupListLabSerializer
from lms.users.serializers import UserDetailLabSerializer

from .models import (
    ClassroomQueue, ClassroomTracker, ClassroomTrackerQueue, EnrolledUserTrackerIssue, EnrollmentQueue,
    EnrollmentTracker, EnrollmentTrackerIssue, EnrollmentTrackerQueue, StudentSlotTrackerIssue, TrackerHook,
    TrackerHookEvent, TrackerQueue,
)


class EnrollmentTrackerQueueListLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = EnrollmentTrackerQueue
        fields = (
            'id', 'name', 'enrollment_id', 'is_default', 'summary', 'description', 'issue_type',
            'accepted_status', 'rejected_status', 'cancelled_status', 'can_delete', 'tracker_pulling_enabled',
        )
        read_only_fields = fields


class EnrollmentTrackerQueueDetailLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = EnrollmentTrackerQueue
        fields = (
            'id', 'name', 'enrollment_id', 'is_default', 'summary', 'description', 'issue_type',
            'accepted_status', 'rejected_status', 'cancelled_status', 'can_delete', 'tracker_pulling_enabled',
        )
        read_only_fields = fields


class EnrollmentTrackerQueueCreateLabSerializer(serializers.ModelSerializer):
    enrollment_id = serializers.PrimaryKeyRelatedField(
        source='enrollment', queryset=Enrollment.objects.active()
    )
    is_default = serializers.BooleanField(required=False, allow_null=False)

    class Meta:
        model = EnrollmentTrackerQueue
        fields = (
            'name', 'enrollment_id', 'is_default', 'summary', 'description', 'issue_type',
            'accepted_status', 'rejected_status', 'cancelled_status', 'tracker_pulling_enabled',
        )

    def validate(self, attrs):
        instance = EnrollmentTrackerQueue(**attrs)
        instance.clean()

        return attrs


class EnrollmentTrackerQueueNestedLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = EnrollmentTrackerQueue
        fields = (
            'name', 'is_default', 'summary', 'description', 'issue_type',
            'accepted_status', 'rejected_status', 'cancelled_status', 'tracker_pulling_enabled',
        )


class EnrollmentTrackerQueueUpdateLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = EnrollmentTrackerQueue
        fields = (
            'name', 'is_default', 'summary', 'description', 'issue_type',
            'accepted_status', 'rejected_status', 'cancelled_status', 'tracker_pulling_enabled',
        )


class EnrollmentTrackerIssueListSerializer(serializers.ModelSerializer):
    class Meta:
        model = EnrollmentTrackerIssue
        fields = (
            'id', 'queue_id', 'issue_number',
            'got_status_from_startrek', 'status', 'status_processed', 'issue',
        )
        read_only_fields = fields


class EnrolledUserTrackerIssueListSerializer(serializers.ModelSerializer):
    class Meta:
        model = EnrolledUserTrackerIssue
        fields = (
            'id', 'queue_id', 'issue_key', 'issue_status', 'status',
        )
        read_only_fields = fields


class TrackerHookEventDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = TrackerHookEvent
        fields = (
            'id', 'issue', 'request_body',
        )


class TrackerHookEventCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = TrackerHookEvent
        fields = (
            'request_body',
        )


class TrackerHookDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = TrackerHook
        fields = (
            "id", "issue", "data", "action", "status", "result",
        )
        read_only_fields = fields


class TrackerHookCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = TrackerHook
        fields = (
            "data",
        )


class TrackerQueueLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = TrackerQueue
        fields = (
            'id', 'name', 'display_name', 'issue_type', 'initial_state', 'is_active',
        )


class ClassroomQueueListLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = ClassroomQueue
        fields = (
            'id',
            'name',
            'display_name',
            'created',
            'modified',
            'issue_type',
            'initial_state',
            'template_id',
            'is_active',
        )
        read_only_fields = fields


class ClassroomQueueInlineLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = ClassroomQueue
        fields = (
            'id',
            'name',
            'display_name',
        )
        read_only_fields = fields


class ClassroomTrackerDetailLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = ClassroomTracker
        fields = (
            'queue_id',
        )
        read_only_fields = fields


class ClassroomTrackerCreateLabSerializer(serializers.ModelSerializer):
    queue_id = serializers.PrimaryKeyRelatedField(
        source='queue', queryset=ClassroomQueue.objects.active(), required=True,
    )

    class Meta:
        model = ClassroomTracker
        fields = (
            'queue_id',
        )


class ClassroomTrackerUpdateLabSerializer(serializers.ModelSerializer):
    queue_id = serializers.PrimaryKeyRelatedField(
        source='queue', queryset=ClassroomQueue.objects.active(), required=True,
    )

    class Meta:
        model = ClassroomTracker
        fields = (
            'queue_id',
        )


class ClassroomTrackerQueueLabSerializer(serializers.ModelSerializer):
    queue = TrackerQueueLabSerializer()

    class Meta:
        model = ClassroomTrackerQueue
        fields = ('id', 'queue', 'classroom_id')
        read_only_fields = fields


class ClassroomTrackerQueueCreateLabSerializer(serializers.ModelSerializer):
    queue_id = serializers.PrimaryKeyRelatedField(
        source='queue', queryset=TrackerQueue.objects.active(), required=True,
    )
    classroom_id = serializers.PrimaryKeyRelatedField(
        source='classroom', queryset=Classroom.objects.active(), required=True,
    )

    class Meta:
        model = ClassroomTrackerQueue
        fields = (
            'queue_id',
            'classroom_id',
        )


class TrackerQueueInlineSerializer(serializers.ModelSerializer):
    class Meta:
        model = TrackerQueue
        fields = (
            'id',
            'name',
            'display_name',
        )


class StudentSlotTrackerIssueSerializer(serializers.ModelSerializer):
    queue = TrackerQueueInlineSerializer()

    class Meta:
        model = StudentSlotTrackerIssue
        fields = (
            'id',
            'slot_id',
            'queue',
            'issue_number',
            'issue',
        )


class ClassroomTrackerQueueIdMixin:
    def get_tracker_queue_id(self, obj: Classroom) -> Optional[int]:
        tracker = getattr(obj, "tracker", None)
        return tracker and tracker.queue_id


class ClassroomDetailLabSerializer(ClassroomTrackerQueueIdMixin, CourseModuleDetailBaseSerializer):
    timeslots = TimeslotListInlineLabSerializer(many=True)
    tracker_queue_id = serializers.SerializerMethodField()

    class Meta(CourseModuleDetailBaseSerializer.Meta):
        model = Classroom
        fields = CourseModuleDetailBaseSerializer.Meta.fields + (
            'timeslots', 'tracker_queue_id', 'calendar_enabled',
        )
        read_only_fields = fields


class ClassroomDetailNoTimeslotsLabSerializer(ClassroomTrackerQueueIdMixin, CourseModuleDetailBaseSerializer):
    tracker_queue_id = serializers.SerializerMethodField()

    class Meta(CourseModuleDetailBaseSerializer.Meta):
        model = Classroom
        fields = CourseModuleDetailBaseSerializer.Meta.fields + (
            'tracker_queue_id', 'calendar_enabled',
        )


class ClassroomCreateLabSerializer(CourseModuleCreateBaseSerializer):
    """
    Создание занятия с расписанием (с выбором очереди)
    """
    tracker_queue_id = serializers.PrimaryKeyRelatedField(
        queryset=ClassroomQueue.objects.active(),
        required=False,
        allow_null=True,
    )

    class Meta(CourseModuleCreateBaseSerializer.Meta):
        model = Classroom
        fields = CourseModuleCreateBaseSerializer.Meta.fields + (
            'tracker_queue_id', 'estimated_time', 'calendar_enabled',
        )

    def create(self, validated_data):
        tracker_queue_id = validated_data.pop('tracker_queue_id', None)
        instance: Classroom = super().create(validated_data)
        if tracker_queue_id:
            ClassroomTracker.objects.get_or_create(
                classroom=instance,
                queue=tracker_queue_id,
            )

        return instance


class ClassroomUpdateLabSerializer(CourseModuleUpdateBaseSerializer):
    tracker_queue_id = serializers.PrimaryKeyRelatedField(
        queryset=ClassroomQueue.objects.active(),
        required=False,
        allow_null=True,
    )

    class Meta(CourseModuleUpdateBaseSerializer.Meta):
        model = Classroom
        fields = CourseModuleUpdateBaseSerializer.Meta.fields + (
            'tracker_queue_id', 'estimated_time', 'calendar_enabled'
        )

    def update(self, instance, validated_data):
        tracker_queue = validated_data.pop('tracker_queue_id', None)
        instance: Classroom = super().update(instance, validated_data)

        tracker: ClassroomTracker = ClassroomTracker.objects.filter(classroom=instance).first()
        if tracker_queue:
            if not tracker:
                tracker = ClassroomTracker.objects.create(
                    classroom=instance,
                    queue=tracker_queue,
                )
            elif tracker.queue_id != tracker_queue.id:
                tracker.queue = tracker_queue
                tracker.save()

        else:
            if tracker:
                tracker.delete()
                tracker = None

        # обновляем внутренний кэш модели
        instance.tracker = tracker

        return instance


class EnrollmentTrackerDetailSerializer(serializers.ModelSerializer):
    queues = serializers.SerializerMethodField()

    def get_queues(self, instance: Enrollment) -> List[int]:
        queues = instance.tracker.values_list('queue_id', flat=True)
        return serializers.ListSerializer(queues, child=serializers.IntegerField()).data

    class Meta:
        model = Enrollment
        fields = (
            'id', 'course_id', 'survey_id', 'enroll_type', 'name', 'summary', 'options',
            'is_active', 'is_default', 'queues',
            'created', 'modified',
        )
        read_only_fields = fields


class EnrollmentTrackerCreateLabSerializer(serializers.ModelSerializer):
    course_id = serializers.PrimaryKeyRelatedField(
        source='course', queryset=Course.objects.all(),
    )

    survey_id = serializers.PrimaryKeyRelatedField(
        source='survey', queryset=EnrollSurvey.objects.all(),
        required=False, allow_null=True,
    )

    queues = serializers.ListSerializer(
        child=serializers.PrimaryKeyRelatedField(
            queryset=EnrollmentQueue.objects.all(),
            required=True,
        ),
        validators=[partial(validate_unique_elements, error_message=_("очереди не должны повторяться"))],
        required=False,
        allow_null=True,
    )

    class Meta:
        model = Enrollment
        fields = (
            'id', 'course_id', 'survey_id', 'name', 'summary', 'options',
            'is_default', 'queues',
        )

    def create(self, validated_data):
        queues = validated_data.pop('queues', [])
        instance = super().create(validated_data)

        if queues:
            EnrollmentTracker.objects.bulk_create(
                objs=(
                    EnrollmentTracker(
                        enrollment=instance,
                        queue=queue,
                        is_default=i == 0,
                    )
                    for i, queue in enumerate(queues)
                ),
                batch_size=settings.BULK_BATCH_SIZE_DEFAULT,
            )

        return instance


class EnrollmentTrackerUpdateLabSerializer(serializers.ModelSerializer):
    survey_id = serializers.PrimaryKeyRelatedField(
        source='survey', queryset=EnrollSurvey.objects.all(),
        required=False, allow_null=True,
    )

    queues = serializers.ListSerializer(
        child=serializers.PrimaryKeyRelatedField(
            queryset=EnrollmentQueue.objects.all(),
            required=True,
        ),
        validators=[partial(validate_unique_elements, error_message=_("очереди не должны повторяться"))],
        required=False, allow_null=True,
    )

    class Meta:
        model = Enrollment
        fields = (
            'survey_id', 'name', 'summary', 'is_active', 'is_default', 'queues',
        )

    def update(self, instance, validated_data):
        queues = validated_data.pop('queues', [])
        instance = super().update(instance, validated_data)

        # удаление старых очередей
        EnrollmentTracker.objects.filter(enrollment_id=instance.id).delete()

        if queues:
            # создание новых очередей
            EnrollmentTracker.objects.bulk_create(
                objs=(
                    EnrollmentTracker(
                        enrollment=instance,
                        queue=queue,
                        is_default=i == 0,
                    )
                    for i, queue in enumerate(queues)
                ),
                batch_size=settings.BULK_BATCH_SIZE_DEFAULT,
            )

        return instance


class EnrolledUserListLabSerializer(serializers.ModelSerializer):
    user = UserDetailLabSerializer()
    group = CourseGroupInlineSerializer()
    departments = StaffGroupListLabSerializer(many=True, source='user.staffprofile.groups')
    issues = EnrollmentTrackerIssueListSerializer(many=True, source='enrollment_tracker_issues')
    tracker_issues = EnrolledUserTrackerIssueListSerializer(many=True, source='issues')

    class Meta:
        model = EnrolledUser
        fields = (
            'id', 'course_id', 'enrollment_id', 'user', 'group',
            'departments', 'issues', 'tracker_issues', 'enroll_date', 'completion_date',
            'status', 'created', 'modified',
        )
        read_only_fields = fields


class EnrollmentQueueListLabSerializer(serializers.ModelSerializer):
    name = serializers.CharField(source="__str__")

    class Meta:
        model = EnrollmentQueue
        fields = (
            'id',
            'name',
            'display_name',
            'issue_type',
            'initial_state',
            'is_active',
        )
        read_only_fields = fields
