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

from rest_framework import serializers
from rest_framework.validators import UniqueValidator

from lms.tags.services import normalize_tag_name

from ..models import Course, CourseCategory, CourseCity, CourseWorkflow, Provider, StudyMode
from ..services import update_course_tags
from ..validators import validate_course_slug
from .course_workflow import CourseWorkflowSerializer

User = get_user_model()


# API Serializers
# ===============
class CourseAuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            'id', 'username', 'first_name', 'last_name',
        )
        read_only_fields = fields


class CourseListSerializer(serializers.ModelSerializer):
    author = CourseAuthorSerializer(read_only=True)
    categories = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
    status = serializers.ChoiceField(choices=Course.STATUS_KEYS)
    type = serializers.ChoiceField(choices=Course.TypeChoices.choices, source='course_type')
    format = serializers.ChoiceField(choices=Course.FormatChoices.choices, required=False)
    tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name')

    class Meta:
        model = Course
        fields = (
            'id', 'slug', 'name', 'shortname', 'summary', 'available_for_enroll', 'is_full', 'enable_groups',
            'city_id', 'study_mode_id', 'author', 'image_url', 'structure', 'format', 'categories',
            'begin_date', 'end_date', 'enroll_begin', 'enroll_end',
            'price', 'payment_method', 'paid_percent', 'payment_terms',
            'num_hours', 'status', 'type', 'tags',
            'created', 'modified',
        )
        read_only_fields = fields


class CourseDetailSerializer(serializers.ModelSerializer):
    author = CourseAuthorSerializer(read_only=True)
    categories = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
    status = serializers.ChoiceField(choices=Course.STATUS_KEYS)
    type = serializers.ChoiceField(choices=Course.TypeChoices.choices, source='course_type')
    format = serializers.ChoiceField(choices=Course.FormatChoices.choices, required=False)
    tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name')

    class Meta:
        model = Course
        fields = (
            'id', 'slug', 'name', 'shortname', 'summary', 'description',
            'available_for_enroll', 'is_full', 'enable_groups', 'is_enroll_open', 'enroll_will_begin', 'has_open_seats',
            'city_id', 'study_mode_id', 'provider_id', 'author', 'image_url', 'structure', 'format', 'categories',
            'begin_date', 'end_date', 'enroll_begin', 'enroll_end', 'enable_followers',
            'price', 'payment_method', 'paid_percent', 'payment_terms',
            'num_hours', 'status', 'type', 'tags',
            'enrollments_only', 'multi_enrollments', 'retries_allowed',
            'created', 'modified',
        )
        read_only_fields = fields


class CourseIDSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)


# LabAPI Serializers
# ==================
class CourseAuthorLabSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            'id', 'username', 'first_name', 'last_name',
        )
        read_only_fields = fields


class CourseListLabSerializer(serializers.ModelSerializer):
    author = CourseAuthorLabSerializer(read_only=True)
    categories = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
    type = serializers.ChoiceField(choices=Course.TypeChoices.choices, source='course_type')
    tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name')

    class Meta:
        model = Course
        fields = (
            'id', 'slug', 'name', 'shortname', 'summary', 'image_url',
            'available_for_enroll', 'is_full', 'enable_groups',
            'city_id', 'study_mode_id', 'author', 'structure', 'categories',
            'begin_date', 'end_date', 'enroll_begin', 'enroll_end',
            'is_active', 'is_archive', 'payment_method', 'price', 'type',
            'tags', 'created', 'modified',
        )
        read_only_fields = fields


class CourseDetailLabSerializer(serializers.ModelSerializer):
    author = CourseAuthorLabSerializer(read_only=True)
    categories = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
    workflow = CourseWorkflowSerializer(read_only=True)
    type = serializers.ChoiceField(choices=Course.TypeChoices.choices, source='course_type')
    format = serializers.ChoiceField(choices=Course.FormatChoices.choices, required=False)
    tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name')

    class Meta:
        model = Course
        fields = (
            'id', 'slug', 'name', 'shortname', 'summary', 'description', 'image_url',
            'available_for_enroll', 'is_full', 'enable_groups', 'enable_followers',
            'begin_date', 'end_date', 'enroll_begin', 'enroll_end', 'workflow',
            'price', 'payment_method', 'paid_percent', 'payment_terms', 'num_hours',
            'city_id', 'study_mode_id', 'provider_id', 'author', 'structure', 'format', 'categories',
            'enrollments_only', 'multi_enrollments', 'retries_allowed',
            'is_active', 'is_archive', 'show_in_catalog', 'type', 'tags',
            'created', 'modified',
        )


class CourseCreateLabSerializer(serializers.ModelSerializer):
    categories = serializers.PrimaryKeyRelatedField(
        queryset=CourseCategory.objects.active().only('id'), many=True, required=False, allow_null=True,
    )
    study_mode_id = serializers.PrimaryKeyRelatedField(
        source='study_mode', queryset=StudyMode.objects.active().only('id'), required=False,
    )
    provider_id = serializers.PrimaryKeyRelatedField(
        source='provider', queryset=Provider.objects.active().only('id'), required=False, allow_null=True,
    )
    city_id = serializers.PrimaryKeyRelatedField(
        source='city', queryset=CourseCity.objects.active().only('id'), required=False, allow_null=True,
    )
    workflow_id = serializers.PrimaryKeyRelatedField(
        source='workflow', queryset=CourseWorkflow.objects.all().only('id'), required=False, allow_null=True,
    )
    type = serializers.ChoiceField(choices=Course.TypeChoices.choices, source='course_type', required=False)
    format = serializers.ChoiceField(choices=Course.FormatChoices.choices, required=False)
    tags = serializers.ListSerializer(child=serializers.CharField(), required=False)

    class Meta:
        model = Course
        fields = (
            'slug', 'name', 'shortname', 'summary', 'description', 'image_url',
            'begin_date', 'end_date', 'enroll_begin', 'enroll_end', 'workflow_id', 'enable_followers',
            'price', 'payment_method', 'paid_percent', 'payment_terms', 'num_hours',
            'city_id', 'study_mode_id', 'provider_id', 'structure', 'format', 'categories', 'show_in_catalog',
            'enrollments_only', 'multi_enrollments', 'retries_allowed', 'type', 'tags',
        )

    def validate_tags(self, value):
        if value and len(value) != len({normalize_tag_name(tag) for tag in value}):
            raise serializers.ValidationError(_("Присутствуют дублирующиеся теги"))

        return value

    @transaction.atomic
    def create(self, validated_data):
        tags = validated_data.pop('tags', None)
        course = super().create(validated_data)

        if tags:
            update_course_tags(course, tags)

        return course


class CourseUpdateLabSerializer(serializers.ModelSerializer):
    categories = serializers.PrimaryKeyRelatedField(
        queryset=CourseCategory.objects.active(), many=True, required=False, allow_null=True,
    )
    study_mode_id = serializers.PrimaryKeyRelatedField(
        source='study_mode', queryset=StudyMode.objects.active(), required=False,
    )
    provider_id = serializers.PrimaryKeyRelatedField(
        source='provider', queryset=Provider.objects.active(), required=False, allow_null=True,
    )
    city_id = serializers.PrimaryKeyRelatedField(
        source='city', queryset=CourseCity.objects.active(), required=False, allow_null=True,
    )
    workflow_id = serializers.PrimaryKeyRelatedField(
        source='workflow', queryset=CourseWorkflow.objects.all().only('id'), required=False, allow_null=True,
    )
    format = serializers.ChoiceField(choices=Course.FormatChoices.choices, required=False)
    tags = serializers.ListSerializer(child=serializers.CharField(), required=False)

    class Meta:
        model = Course
        fields = (
            'slug', 'name', 'shortname', 'summary', 'description', 'image_url',
            'begin_date', 'end_date', 'enroll_begin', 'enroll_end', 'workflow_id', 'enable_followers',
            'price', 'payment_method', 'paid_percent', 'payment_terms', 'num_hours',
            'city_id', 'study_mode_id', 'provider_id', 'structure', 'format', 'categories',
            'enrollments_only', 'multi_enrollments', 'retries_allowed',
            'is_active', 'is_archive', 'show_in_catalog', 'tags',
        )

    def validate_tags(self, value):
        if value and len(value) != len({normalize_tag_name(tag) for tag in value}):
            raise serializers.ValidationError(_("Присутствуют дублирующиеся теги"))

        return value

    @transaction.atomic
    def update(self, instance, validated_data):
        tags = validated_data.pop('tags', None)
        course = super().update(instance, validated_data)

        if tags is not None:
            update_course_tags(course, tags)

        return course


class CourseCheckSlugSerializer(serializers.Serializer):
    slug = serializers.SlugField(
        max_length=255,
        validators=[
            UniqueValidator(
                queryset=Course.objects.all(),
                message=_("Этот код уже используется в другом курсе"),
            ),
            validate_course_slug,
        ],
        required=True,
    )


# ExternalAPI Serializers
# ==================
# TODO: Обновиться на drf-spectacular==0.22.1 и использовать
#  @extend_serializer_schema(component_name='CourseAuthorSerializer')
#  Либо переименовать после рефакторинга (раскидать сериализоваторы по модулям)
class CourseAuthorExternalSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            'id', 'username', 'first_name', 'last_name',
        )
        read_only_fields = fields


class CourseListExternalSerializer(serializers.ModelSerializer):
    author = CourseAuthorExternalSerializer(read_only=True)
    categories = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
    status = serializers.ChoiceField(choices=Course.STATUS_KEYS)
    type = serializers.ChoiceField(choices=Course.TypeChoices.choices, source='course_type')
    format = serializers.ChoiceField(choices=Course.FormatChoices.choices, required=False)
    tags = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name')

    class Meta:
        model = Course
        fields = (
            'id', 'created', 'modified', 'slug', 'name', 'shortname',
            'summary', 'image_url', 'status', 'type', 'tags',
            'available_for_enroll', 'is_active', 'is_full', 'enable_groups',
            'city_id', 'study_mode_id', 'author', 'structure', 'format', 'categories',
            'begin_date', 'end_date', 'enroll_begin', 'enroll_end',
            'price', 'payment_method', 'paid_percent', 'payment_terms', 'num_hours',
        )
        read_only_fields = fields
