from rest_framework import serializers
from .models import (
    RegistrationFileGroup,
    RegistrationForm,
    Student,
    Guardian,
    Sibling,
    Language,
    RegistrationSchoolFileMaster,
    RegistrationSchoolFileTemplate,
    RegistrationParentFileMaster,
    RegistrationParentFileTemplate,
    AbsenceManagement,
    BilanCompetence
)
from School.models import SchoolClass, Fee, Discount, FeeType
from Auth.models import ProfileRole, Profile
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
from GestionNotification.models import Notification
from N3wtSchool import settings
from django.utils import timezone
import pytz
import Subscriptions.util as util

class AbsenceManagementSerializer(serializers.ModelSerializer):
    student_name = serializers.SerializerMethodField()

    class Meta:
        model = AbsenceManagement
        fields = '__all__'
        # Ajoute les nouveaux champs au retour JSON
        extra_fields = ['student_name']

    def get_student_name(self, obj):
        if obj.student:
            return f"{obj.student.first_name} {obj.student.last_name}"
        return None

class RegistrationSchoolFileMasterSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    file_url = serializers.SerializerMethodField()

    class Meta:
        model = RegistrationSchoolFileMaster
        fields = '__all__'

    def get_file_url(self, obj):
        return obj.file.url if obj.file else None

class RegistrationParentFileMasterSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    class Meta:
        model = RegistrationParentFileMaster
        fields = '__all__'

class RegistrationSchoolFileTemplateSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    file_url = serializers.SerializerMethodField()
    master_file_url = serializers.SerializerMethodField()
    requires_electronic_signature = serializers.SerializerMethodField()
    is_electronically_signed = serializers.SerializerMethodField()

    class Meta:
        model = RegistrationSchoolFileTemplate
        fields = '__all__'

    def get_file_url(self, obj):
        # Retourne l'URL complète du fichier si disponible
        return obj.file.url if obj.file else None

    def get_master_file_url(self, obj):
        # URL du fichier source du master (pour l'aperçu FileUpload côté parent)
        if obj.master and obj.master.file:
            return obj.master.file.url
        return None

    def get_requires_electronic_signature(self, obj):
        # Retourne si le document nécessite une signature électronique
        if obj.master:
            return obj.master.requires_electronic_signature
        return False

    def get_is_electronically_signed(self, obj):
        # Retourne True si le document a été signé électroniquement
        return bool(obj.electronic_signature)

    def update(self, instance, validated_data):
        # Auto-remplir la date de signature si electronic_signature est fournie
        from django.utils import timezone
        if 'electronic_signature' in validated_data and validated_data['electronic_signature']:
            # Nouvelle signature ou re-signature : enregistrer la date
            validated_data['electronic_signature_date'] = timezone.now()
            # Si le document était refusé, le repasser en attente de validation
            if instance.isValidated == False:
                validated_data['isValidated'] = None
        return super().update(instance, validated_data)

class RegistrationParentFileTemplateSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    file_url = serializers.SerializerMethodField()
    master_name = serializers.CharField(source='master.name', read_only=True)
    master_description = serializers.CharField(source='master.description', read_only=True)
    is_required = serializers.BooleanField(source='master.is_required', read_only=True)

    class Meta:
        model = RegistrationParentFileTemplate
        fields = '__all__'

    def get_file_url(self, obj):
        # Retourne l'URL complète du fichier si disponible
        return obj.file.url if obj.file else None

class GuardianSimpleSerializer(serializers.ModelSerializer):
    associated_profile_email = serializers.SerializerMethodField()

    class Meta:
        model = Guardian
        fields = ['id', 'associated_profile_email']

    def get_associated_profile_email(self, obj):
        if obj.profile_role and obj.profile_role.profile:
            return obj.profile_role.profile.email
        return None

class RegistrationFormSimpleSerializer(serializers.ModelSerializer):
    guardians = GuardianSimpleSerializer(many=True, source='student.guardians')
    last_name = serializers.SerializerMethodField()
    first_name = serializers.SerializerMethodField()

    class Meta:
        model = RegistrationForm
        fields = ['student_id', 'last_name', 'first_name', 'guardians']

    def get_last_name(self, obj):
        return obj.student.last_name

    def get_first_name(self, obj):
        return obj.student.first_name

class RegistrationFileGroupSerializer(serializers.ModelSerializer):
    registration_forms = serializers.SerializerMethodField()

    class Meta:
        model = RegistrationFileGroup
        fields = '__all__'

    def get_registration_forms(self, obj):
        forms = RegistrationForm.objects.filter(fileGroup=obj)
        return RegistrationFormSimpleSerializer(forms, many=True).data

class LanguageSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    class Meta:
        model = Language
        fields = '__all__'

class SiblingSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    class Meta:
        model = Sibling
        fields = '__all__'

class GuardianSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    profile_role = serializers.PrimaryKeyRelatedField(queryset=ProfileRole.objects.all(), required=False)
    profile_role_data = ProfileRoleSerializer(write_only=True, required=False)
    associated_profile_email = serializers.SerializerMethodField()

    class Meta:
        model = Guardian
        fields = '__all__'

    def get_associated_profile_email(self, obj):
        if obj.profile_role and obj.profile_role.profile:
            return obj.profile_role.profile.email
        return None

class StudentSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    guardians = GuardianSerializer(many=True, required=False)
    siblings = SiblingSerializer(many=True, required=False)
    spoken_languages = LanguageSerializer(many=True, required=False)
    associated_class_id = serializers.PrimaryKeyRelatedField(queryset=SchoolClass.objects.all(), source='associated_class', required=False, write_only=False, read_only=False)
    age = serializers.SerializerMethodField()
    formatted_birth_date = serializers.SerializerMethodField()
    birth_date = serializers.DateField(input_formats=['%d-%m-%Y', '%Y-%m-%d'], required=False, allow_null=True)
    associated_class_name = serializers.SerializerMethodField()

    class Meta:
        model = Student
        fields = '__all__'

    def create_or_update_guardians(self, guardians_data):
        guardians_ids = []
        for guardian_data in guardians_data:
            guardian_id = guardian_data.get('id', None)
            profile_role_data = guardian_data.pop('profile_role_data', None)
            profile_role = guardian_data.pop('profile_role', None)

            if guardian_id:
                # Si un ID est fourni, récupérer ou mettre à jour le Guardian existant
                try:
                    guardian_instance = Guardian.objects.get(id=guardian_id)
                    # Mettre à jour explicitement tous les champs y compris birth_date, profession, address
                    for field, value in guardian_data.items():
                        if field != 'id':  # Ne pas mettre à jour l'ID
                            setattr(guardian_instance, field, value)
                    guardian_instance.save()
                    guardians_ids.append(guardian_instance.id)
                    continue
                except Guardian.DoesNotExist:
                    # Si le guardian n'existe pas, créer un nouveau
                    guardian_instance = Guardian.objects.create(**guardian_data)
                    guardians_ids.append(guardian_instance.id)
                    continue

            if profile_role_data:
                # Vérifiez si 'profile_data' est fourni pour créer un nouveau profil
                profile_data = profile_role_data.pop('profile_data', None)
                if profile_data:
                    # Créer un nouveau profil
                    profile_serializer = ProfileSerializer(data=profile_data)
                    profile_serializer.is_valid(raise_exception=True)
                    profile = profile_serializer.save()
                    profile.set_password(profile_data['password'])
                    profile.save()
                    profile_role_data['profile'] = profile.id  # Associer le profil créé

                # Vérifiez si 'profile' est un objet ou une clé primaire
                if isinstance(profile_role_data.get('profile'), Profile):
                    profile_role_data['profile'] = profile_role_data['profile'].id

                establishment_id = profile_role_data.pop('establishment').id
                profile_role_data['establishment'] = establishment_id

                # Vérifiez si un ProfileRole existe déjà pour ce profile et cet établissement
                existing_profile_role = ProfileRole.objects.filter(
                    profile_id=profile_role_data['profile'],
                    establishment=profile_role_data['establishment'],
                    role_type=profile_role_data['role_type']
                ).first()

                if existing_profile_role:
                    # Mettre à jour le ProfileRole existant
                    profile_role_serializer = ProfileRoleSerializer(existing_profile_role, data=profile_role_data)
                    profile_role_serializer.is_valid(raise_exception=True)
                    profile_role = profile_role_serializer.save()
                else:
                    # Créer un nouveau ProfileRole si aucun n'existe
                    profile_role_serializer = ProfileRoleSerializer(data=profile_role_data)
                    profile_role_serializer.is_valid(raise_exception=True)
                    profile_role = profile_role_serializer.save()
            elif profile_role:
                # Récupérer un ProfileRole existant par son ID
                profile_role = ProfileRole.objects.get(id=profile_role.id)

            if profile_role:
                guardian_data['profile_role'] = profile_role

            # Vérifiez si un Guardian existe déjà pour ce ProfileRole
            existing_guardian = Guardian.objects.filter(profile_role=profile_role).first()
            if existing_guardian:
                # Mettre à jour le Guardian existant
                for key, value in guardian_data.items():
                    setattr(existing_guardian, key, value)
                existing_guardian.save()
                guardians_ids.append(existing_guardian.id)
            else:
                # Créer un nouveau Guardian
                guardian_instance = Guardian.objects.create(**guardian_data)
                guardians_ids.append(guardian_instance.id)

        return guardians_ids

    def create_or_update_siblings(self, siblings_data, student_instance):
        """
        Crée ou met à jour les frères et sœurs associés à un étudiant.
        Supprime les frères et sœurs qui ne sont plus présents dans siblings_data.
        """

        # Si siblings_data est vide, supprimer tous les frères et sœurs associés
        if not siblings_data:
            student_instance.siblings.clear()  # Supprime toutes les relations
            return []

        # Récupérer les IDs des frères et sœurs existants
        existing_sibling_ids = set(student_instance.siblings.values_list('id', flat=True))

        # Créer ou mettre à jour les frères et sœurs
        updated_sibling_ids = []
        for sibling_data in siblings_data:
            sibling_instance, created = Sibling.objects.update_or_create(
                id=sibling_data.get('id'),
                defaults=sibling_data
            )
            updated_sibling_ids.append(sibling_instance.id)

        # Supprimer les frères et sœurs qui ne sont plus dans siblings_data
        siblings_to_delete = existing_sibling_ids - set(updated_sibling_ids)
        Sibling.objects.filter(id__in=siblings_to_delete).delete()
        return updated_sibling_ids

    def create_or_update_languages(self, languages_data):
        languages_ids = []
        for language_data in languages_data:
            language_instance, created = Language.objects.update_or_create(
                id=language_data.get('id'),
                defaults=language_data
            )
            languages_ids.append(language_instance.id)
        return languages_ids

    def create(self, validated_data):
        guardians_data = validated_data.pop('guardians', [])
        siblings_data = validated_data.pop('siblings', [])
        languages_data = validated_data.pop('spoken_languages', [])
        student = Student.objects.create(**validated_data)
        student.guardians.set(self.create_or_update_guardians(guardians_data))
        student.siblings.set(self.create_or_update_siblings(siblings_data, student))
        student.spoken_languages.set(self.create_or_update_languages(languages_data))

        return student

    def update(self, instance, validated_data):
        guardians_data = validated_data.pop('guardians', [])
        siblings_data = validated_data.pop('siblings', [])
        languages_data = validated_data.pop('spoken_languages', [])
        if guardians_data:
            instance.guardians.set(self.create_or_update_guardians(guardians_data))

        sibling_ids = self.create_or_update_siblings(siblings_data, instance)
        instance.siblings.set(sibling_ids)

        if languages_data:
            instance.spoken_languages.set(self.create_or_update_languages(languages_data))

        for field in self.fields:
            try:
                setattr(instance, field, validated_data[field])
            except KeyError:
                pass
        instance.save()

        return instance

    def get_age(self, obj):
        return obj.age

    def get_formatted_birth_date(self, obj):
        return obj.formatted_birth_date

    def get_associated_class_name(self, obj):
        return obj.associated_class.atmosphere_name if obj.associated_class else None

class RegistrationFormSerializer(serializers.ModelSerializer):
    student = StudentSerializer(many=False, required=False)
    registration_file = serializers.FileField(required=False)
    sepa_file = serializers.FileField(required=False)
    status_label = serializers.SerializerMethodField()
    formatted_last_update = serializers.SerializerMethodField()
    registration_files = RegistrationSchoolFileTemplateSerializer(many=True, required=False)
    fees = serializers.PrimaryKeyRelatedField(queryset=Fee.objects.all(), many=True, required=False)
    discounts = serializers.PrimaryKeyRelatedField(queryset=Discount.objects.all(), many=True, required=False)
    totalRegistrationFees = serializers.SerializerMethodField()
    totalTuitionFees = serializers.SerializerMethodField()

    class Meta:
        model = RegistrationForm
        fields = '__all__'

    def create(self, validated_data):
        student_data = validated_data.pop('student')
        student = StudentSerializer.create(StudentSerializer(), student_data)
        fees_data = validated_data.pop('fees', [])
        discounts_data = validated_data.pop('discounts', [])
        registrationForm = RegistrationForm.objects.create(student=student, **validated_data)

        # Associer les IDs des objets Fee et Discount au RegistrationForm
        registrationForm.fees.set([fee.id for fee in fees_data])
        registrationForm.discounts.set([discount.id for discount in discounts_data])
        return registrationForm

    def update(self, instance, validated_data):
        student_data = validated_data.pop('student', None)
        fees_data = validated_data.pop('fees', None)
        discounts_data = validated_data.pop('discounts', None)
        if student_data:
            student = instance.student
            StudentSerializer.update(StudentSerializer(), student, student_data)

        for field in self.fields:
            try:
                setattr(instance, field, validated_data[field])
            except KeyError:
                pass

        instance.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
        instance.save()

        # Associer les IDs des objets Fee et Discount au RegistrationForm
        if fees_data is not None:
            instance.fees.set([fee.id for fee in fees_data])

        if discounts_data is not None:
            instance.discounts.set([discount.id for discount in discounts_data])

        return instance

    def get_status_label(self, obj):
        return obj.get_status_display()

    def get_formatted_last_update(self, obj):
        utc_time = timezone.localtime(obj.last_update) # Convert to local time
        local_tz = pytz.timezone(settings.TZ_APPLI)
        local_time = utc_time.astimezone(local_tz)

        return local_time.strftime("%d-%m-%Y %H:%M")

    def get_totalRegistrationFees(self, obj):
        return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.REGISTRATION_FEE))

    def get_totalTuitionFees(self, obj):
        return sum(fee.base_amount for fee in obj.fees.filter(type=FeeType.TUITION_FEE))

class StudentByParentSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    associated_class_name = serializers.SerializerMethodField()
    associated_class_id = serializers.SerializerMethodField()

    class Meta:
        model = Student
        fields = ['id', 'last_name', 'first_name', 'level', 'photo', 'associated_class_name', 'associated_class_id']

    def __init__(self, *args, **kwargs):
        super(StudentByParentSerializer, self).__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].required = False

    def get_associated_class_name(self, obj):
        return obj.associated_class.atmosphere_name if obj.associated_class else None

    def get_associated_class_id(self, obj):
        return obj.associated_class.id if obj.associated_class else None

class RegistrationFormByParentSerializer(serializers.ModelSerializer):
    student = StudentByParentSerializer(many=False, required=True)

    class Meta:
        model = RegistrationForm
        fields = ['student', 'status', 'sepa_file']

    def __init__(self, *args, **kwargs):
        super(RegistrationFormByParentSerializer, self).__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].required = False

class GuardianByDICreationSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    associated_profile_email = serializers.SerializerMethodField()
    profile = serializers.SerializerMethodField()

    class Meta:
        model = Guardian
        fields = ['id', 'last_name', 'first_name', 'associated_profile_email', 'phone', 'profile_role', 'profile']

    def get_associated_profile_email(self, obj):
        if obj.profile_role and obj.profile_role.profile:
            return obj.profile_role.profile.email
        return None

    def get_profile(self, obj):
        if obj.profile_role and obj.profile_role.profile:
            return obj.profile_role.profile.id  # Retourne l'ID du profil associé
        return None

class BilanCompetenceSerializer(serializers.ModelSerializer):
    class Meta:
        model = BilanCompetence
        fields = ['id', 'file', 'period', 'created_at']

class StudentByRFCreationSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    guardians = GuardianByDICreationSerializer(many=True, required=False)
    associated_class_name = serializers.SerializerMethodField()
    associated_class_id = serializers.SerializerMethodField()
    bilans = BilanCompetenceSerializer(many=True, read_only=True)

    class Meta:
        model = Student
        fields = ['id', 'last_name', 'first_name', 'guardians', 'level', 'associated_class_name', 'associated_class_id', 'photo', 'bilans']

    def __init__(self, *args, **kwargs):
        super(StudentByRFCreationSerializer, self).__init__(*args, **kwargs)
        for field in self.fields:
            self.fields[field].required = False

    def get_associated_class_name(self, obj):
        return obj.associated_class.atmosphere_name if obj.associated_class else None

    def get_associated_class_id(self, obj):
        return obj.associated_class.id if obj.associated_class else None

class NotificationSerializer(serializers.ModelSerializer):
    notification_type_label = serializers.ReadOnlyField()

    class Meta:
        model = Notification
        fields = '__all__'

