from typing import List

from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from lms.courses.models import CourseGroup, CourseStudent
from lms.courses.serializers import (
    CourseModuleCreateBaseSerializer, CourseModuleDetailBaseSerializer, CourseModuleListBaseSerializer,
    CourseModuleUpdateBaseSerializer,
)

from .models import Classroom, StudentSlot, Timeslot, TimeslotExchange
from .validators import validate_course_groups

User = get_user_model()


# API
# ===
class TimeslotListInlineSerializer(serializers.ModelSerializer):
    class Meta:
        model = Timeslot
        fields = (
            'id', 'title', 'summary', 'begin_date', 'end_date',
            'max_participants', 'num_participants',
        )
        read_only_fields = fields


class ClassroomListSerializer(serializers.ModelSerializer):
    timeslots = TimeslotListInlineSerializer(many=True)

    class Meta:
        model = Classroom
        fields = (
            'id', 'name', 'description', 'timeslots', 'estimated_time',
        )
        read_only_fields = fields


class ClassroomDetailSerializer(serializers.ModelSerializer):
    timeslots = TimeslotListInlineSerializer(many=True)

    class Meta:
        model = Classroom
        fields = (
            'id', 'name', 'description', 'estimated_time',
            'timeslots',
        )
        read_only_fields = fields


class TimeslotListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Timeslot
        fields = (
            'id', 'title', 'summary', 'begin_date', 'end_date',
            'num_participants', 'max_participants',
        )
        read_only_fields = fields


class TimeslotDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = Timeslot
        fields = (
            'id', 'title', 'summary', 'classroom_id', 'begin_date', 'end_date',
            'num_participants', 'max_participants',
        )
        read_only_fields = fields


class StudentSlotListSerializer(serializers.ModelSerializer):
    class Meta:
        model = StudentSlot
        fields = (
            'id', 'timeslot_id', 'is_attended',
        )
        read_only_fields = fields


class StudentSlotDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = StudentSlot
        fields = (
            'id', 'student_id', 'timeslot_id', 'is_attended',
        )
        read_only_fields = fields


class StudentSlotInlineSerializer(serializers.ModelSerializer):
    timeslot = TimeslotDetailSerializer()

    class Meta:
        model = StudentSlot
        fields = (
            'id', 'student_id', 'timeslot', 'is_attended',
        )
        read_only_fields = fields


class TimeslotExchangeSerializer(serializers.ModelSerializer):
    class Meta:
        model = TimeslotExchange
        fields = (
            'id', 'student_slot_id', 'target_timeslot_id', 'course_id',
        )
        read_only_fields = fields


class TimeslotExchangeUpdateSerializer(serializers.ModelSerializer):
    target_timeslot_id = serializers.PrimaryKeyRelatedField(
        source="target_timeslot", queryset=Timeslot.objects.only('id'), required=True
    )

    class Meta:
        model = TimeslotExchange
        fields = ('target_timeslot_id',)


class TimeslotExchangeDetailSerializer(serializers.ModelSerializer):
    student_slot = StudentSlotInlineSerializer()
    target_timeslot = TimeslotDetailSerializer()

    class Meta:
        model = TimeslotExchange
        fields = (
            'id', 'student_slot', 'target_timeslot', 'course_id',
        )
        read_only_fields = fields


class TimeslotChangeSerializer(serializers.Serializer):
    target_timeslot_id = serializers.IntegerField(required=True)


# LabAPI
# ======
class TimeslotListInlineLabSerializer(serializers.ModelSerializer):
    """
    Вложенный список слотов для занятия
    """

    class Meta:
        model = Timeslot
        fields = (
            'id', 'title', 'summary', 'begin_date', 'end_date',
            'num_participants', 'max_participants', 'course_groups',
            'created', 'modified',
        )
        read_only_fields = fields


class ClassroomListLabSerializer(CourseModuleListBaseSerializer):
    """
    Список занятий с расписанием
    """
    timeslots = TimeslotListInlineLabSerializer(many=True)

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


class ClassroomDetailLabSerializer(CourseModuleDetailBaseSerializer):
    """
    Информация о занятии с расписанием
    """
    timeslots = TimeslotListInlineLabSerializer(many=True)

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


class ClassroomDetailNoTimeslotsLabSerializer(CourseModuleDetailBaseSerializer):
    """
    Информация о занятии без слотов
    """

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


class ClassroomCreateLabSerializer(CourseModuleCreateBaseSerializer):
    """
    Создание занятия с расписанием
    """

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


class ClassroomUpdateLabSerializer(CourseModuleUpdateBaseSerializer):
    """
    Обновление занятия с расписанием
    """

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


class TimeslotDetailLabSerializer(serializers.ModelSerializer):
    """
    Информация о слоте
    """
    course_groups = serializers.SerializerMethodField()

    def get_course_groups(self, instance: Timeslot) -> List[int]:
        groups = instance.course_groups.order_by('id').values_list('id', flat=True)
        return serializers.ListSerializer(groups, child=serializers.IntegerField()).data

    class Meta:
        model = Timeslot
        fields = (
            'id', 'begin_date', 'end_date',
            'title', 'summary', 'num_participants', 'max_participants',
            'course_groups',
            'created', 'modified',
        )
        read_only_fields = fields


class TimeslotUpdateLabSerializer(serializers.ModelSerializer):
    """
    Обновление информации о слоте
    """
    course_groups = serializers.ListSerializer(
        child=serializers.IntegerField(),
        required=False,
        allow_null=True,
    )

    class Meta:
        model = Timeslot
        fields = (
            'begin_date', 'end_date',
            'title', 'summary', 'max_participants',
            'course_groups',
        )

    def validate(self, attrs):
        max_participants = attrs.get('max_participants', None)
        if (
            max_participants and
            max_participants != 0 and
            max_participants < self.instance.num_participants and
            self.instance.has_students
        ):
            raise ValidationError({
                'max_participants':
                    _("На слот уже записано {} студентов").format(self.instance.num_participants),
            })

        course_groups = attrs.get('course_groups')

        if course_groups:
            validate_course_groups(course_groups, self.instance.course)

        return attrs

    def update(self, instance: Timeslot, validated_data):
        course_groups = validated_data.pop('course_groups', None)
        instance = super().update(instance, validated_data)
        if course_groups:
            instance.course_groups.set(course_groups)

        return instance


class TimeslotCreateLabSerializer(serializers.ModelSerializer):
    """
    Создание слота
    """
    classroom_id = serializers.PrimaryKeyRelatedField(
        source='classroom', queryset=Classroom.objects.all(),
    )
    course_groups = serializers.ListSerializer(
        child=serializers.IntegerField(),
        required=False,
        allow_null=True,
    )

    class Meta:
        model = Timeslot
        fields = (
            'classroom_id', 'begin_date', 'end_date',
            'title', 'summary', 'max_participants',
            'course_groups',
        )

    def validate(self, attrs):
        course = self.context.get('course', None)
        course_groups = attrs.get('course_groups')

        if course and course_groups:
            validate_course_groups(course_groups, course)

        return attrs

    def create(self, validated_data):
        course_groups = validated_data.pop('course_groups', None)
        instance = super().create(validated_data)
        if course_groups:
            instance.course_groups.set(course_groups)

        return instance


class UserDetailInlineLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            'id', 'username', 'first_name', 'last_name',
        )
        read_only_fields = fields


class CourseGroupDetailInlineLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = CourseGroup
        fields = (
            'id', 'name',
        )
        read_only_fields = fields


class CourseStudentInlineLabSerializer(serializers.ModelSerializer):
    user = UserDetailInlineLabSerializer()
    group = CourseGroupDetailInlineLabSerializer()

    class Meta:
        model = CourseStudent
        fields = (
            'id', 'user', 'group'
        )
        read_only_fields = fields


class StudentSlotListLabSerializer(serializers.ModelSerializer):
    student = CourseStudentInlineLabSerializer()

    class Meta:
        model = StudentSlot
        fields = (
            'id',
            'student', 'is_attended',
            'created', 'modified',
        )
        read_only_fields = fields
