from django.http.response import JsonResponse
from django.http import HttpResponse
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
from django.utils.decorators import method_decorator
from rest_framework.views import APIView
from rest_framework.decorators import action, api_view
from rest_framework import status
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi

import json
import os
import threading
from django.core.files import File

import N3wtSchool.mailManager as mailer
import Subscriptions.util as util

from Subscriptions.serializers import RegistrationFormSerializer, RegistrationSchoolFileTemplateSerializer, RegistrationParentFileTemplateSerializer
from Subscriptions.pagination import CustomSubscriptionPagination
from Subscriptions.models import (
    Guardian,
    RegistrationForm,
    RegistrationSchoolFileTemplate,
    RegistrationFileGroup,
    RegistrationParentFileTemplate,
    StudentCompetency
)
from Subscriptions.automate import updateStateMachine
from School.models import EstablishmentCompetency
from Establishment.models import Establishment

from N3wtSchool import settings, bdd
from django.db.models import Q



import logging
logger = logging.getLogger(__name__)

# /Subscriptions/registerForms
class RegisterFormView(APIView):
    """
    Gère la liste des dossiers d’inscription, lecture et création.
    """
    pagination_class = CustomSubscriptionPagination

    @swagger_auto_schema(
        manual_parameters=[
            openapi.Parameter('filter', openapi.IN_QUERY, description="filtre", type=openapi.TYPE_STRING, enum=['current_year', 'next_year', 'historical'], required=True),
            openapi.Parameter('search', openapi.IN_QUERY, description="search", type=openapi.TYPE_STRING, required=False),
            openapi.Parameter('page_size', openapi.IN_QUERY, description="limite de page lors de la pagination", type=openapi.TYPE_INTEGER, required=False),
            openapi.Parameter('establishment_id', openapi.IN_QUERY, description="ID de l'établissement", type=openapi.TYPE_INTEGER, required=True),
        ],
        responses={200: RegistrationFormSerializer(many=True)},
        operation_description="Récupère les dossier d'inscriptions en fonction du filtre passé.",
        operation_summary="Récupérer les dossier d'inscriptions",
        examples={
            "application/json": [
                {
                    "id": 1,
                    "student": {
                        "id": 1,
                        "first_name": "John",
                        "last_name": "Doe",
                        "date_of_birth": "2010-01-01"
                    },
                    "status": "current_year",
                    "last_update": "10-02-2025 10:00"
                },
                {
                    "id": 2,
                    "student": {
                        "id": 2,
                        "first_name": "Jane",
                        "last_name": "Doe",
                        "date_of_birth": "2011-02-02"
                    },
                    "status": "historical",
                    "last_update": "09-02-2025 09:00"
                }
            ]
        }
    )
    def get(self, request):
        """
        Récupère les fiches d'inscriptions en fonction du filtre passé.
        """
        # Récupération des paramètres
        filter = request.GET.get('filter', '').strip()
        page_size = request.GET.get('page_size', None)
        establishment_id = request.GET.get('establishment_id', None)
        search = request.GET.get('search', '').strip()  # <-- Ajout du paramètre search

        # Gestion du page_size
        if page_size is not None:
            try:
                page_size = int(page_size)
            except ValueError:
                page_size = settings.NB_RESULT_SUBSCRIPTIONS_PER_PAGE

        # Récupérer les années scolaires
        current_year = util.getCurrentSchoolYear()
        next_year = util.getNextSchoolYear()
        historical_years = util.getHistoricalYears()

        # Récupérer les dossiers d'inscriptions en fonction du filtre
        registerForms_List = None
        if filter == 'current_year':
            registerForms_List = RegistrationForm.objects.filter(school_year=current_year)
        elif filter == 'next_year':
            registerForms_List = RegistrationForm.objects.filter(school_year=next_year)
        elif filter == 'historical':
            registerForms_List = RegistrationForm.objects.filter(school_year__in=historical_years)
        else:
            registerForms_List = None

        if registerForms_List:
            registerForms_List = registerForms_List.filter(establishment=establishment_id)
            # Ajout du filtre search sur le nom et prénom de l'élève
            if search:
                registerForms_List = registerForms_List.filter(
                    Q(student__first_name__icontains=search) |
                    Q(student__last_name__icontains=search)
                )
            registerForms_List = registerForms_List.order_by('-last_update')

        if not registerForms_List:
            return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False)

        # Pagination
        paginator = self.pagination_class()
        page = paginator.paginate_queryset(registerForms_List, request)
        if page is not None:
            registerForms_serializer = RegistrationFormSerializer(page, many=True)
            response_data = paginator.get_paginated_response(registerForms_serializer.data)
            return JsonResponse(response_data, safe=False)

        return JsonResponse({'error': 'aucune donnée trouvée', 'count': 0}, safe=False)

    @swagger_auto_schema(
        request_body=RegistrationFormSerializer,
        responses={200: RegistrationFormSerializer()},
        operation_description="Crée un dossier d'inscription.",
        operation_summary="Créer un dossier d'inscription",
        examples={
            "application/json": {
                "student": {
                    "id": 1,
                    "first_name": "John",
                    "last_name": "Doe",
                    "date_of_birth": "2010-01-01"
                },
                "status": "current_year",
                "last_update": "10-02-2025 10:00",
                "codeLienInscription": "ABC123XYZ456"
            }
        }
    )
    @method_decorator(csrf_protect, name='dispatch')
    @method_decorator(ensure_csrf_cookie, name='dispatch')
    def post(self, request):
        """
        Crée un dossier d'inscription.
        """
        regiterFormData = request.data.copy()
        logger.info(f"Création d'un dossier d'inscription {request}")
        # Ajout de la date de mise à jour
        regiterFormData["last_update"] = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
        # Ajout du code d'inscription
        code = util.genereRandomCode(12)
        regiterFormData["codeLienInscription"] = code

        guardiansId = regiterFormData.pop('idGuardians', [])
        registerForm_serializer = RegistrationFormSerializer(data=regiterFormData)
        fileGroupId =  regiterFormData.pop('fileGroup', None)

        if registerForm_serializer.is_valid():
            di = registerForm_serializer.save()

            # Mise à jour de l'automate
            updateStateMachine(di, 'EVENT_INIT')

            # Récupération du reponsable associé
            for guardianId in guardiansId:
                guardian = Guardian.objects.get(id=guardianId)
                di.student.guardians.add(guardian)
                di.save()
            if fileGroupId:
                di.fileGroup = RegistrationFileGroup.objects.get(id=fileGroupId)
                di.save()

            return JsonResponse(registerForm_serializer.data, safe=False)
        else:
            logger.error(f"Erreur lors de la validation des données {regiterFormData}")

        return JsonResponse(registerForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)

# /Subscriptions/registerForms/{id}
class RegisterFormWithIdView(APIView):
    """
    Gère la lecture, création, modification et suppression d’un dossier d’inscription.
    """
    pagination_class = CustomSubscriptionPagination

    @swagger_auto_schema(
        responses={200: RegistrationFormSerializer()},
        operation_description="Récupère un dossier d'inscription donné.",
        operation_summary="Récupérer un dossier d'inscription",
        examples={
            "application/json": {
                "id": 1,
                "student": {
                    "id": 1,
                    "first_name": "John",
                    "last_name": "Doe",
                    "date_of_birth": "2010-01-01"
                },
            }
        }
    )
    def get(self, request, id):
        """
        Récupère un dossier d'inscription donné.
        """
        registerForm = bdd.getObject(RegistrationForm, "student__id", id)
        if registerForm is None:
            return JsonResponse({"errorMessage":'Le dossier d\'inscription n\'a pas été trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)
        registerForm_serializer = RegistrationFormSerializer(registerForm)
        return JsonResponse(registerForm_serializer.data, safe=False)

    @swagger_auto_schema(
        request_body=RegistrationFormSerializer,
        responses={200: RegistrationFormSerializer()},
        operation_description="Modifie un dossier d'inscription donné.",
        operation_summary="Modifier un dossier d'inscription",
        examples={
            "application/json": {
                "id": 1,
                "student": {
                    "id": 1,
                    "first_name": "John",
                    "last_name": "Doe",
                    "date_of_birth": "2010-01-01"
                },
                "status": "under_review",
                "last_update": "10-02-2025 10:00"
            }
        }
    )
    @method_decorator(csrf_protect, name='dispatch')
    @method_decorator(ensure_csrf_cookie, name='dispatch')
    def put(self, request, id):
        """
        Modifie un dossier d'inscription donné.
        """

        studentForm_data = request.data.get('data', '{}')

        try:
            data = json.loads(studentForm_data)
        except json.JSONDecodeError:
            return JsonResponse({"error": "Invalid JSON format in 'data'"}, status=status.HTTP_400_BAD_REQUEST)

        # Extraire le fichier photo
        photo_file = request.FILES.get('photo')

        # Extraire le fichier photo
        sepa_file = request.FILES.get('sepa_file')

        # Ajouter la photo aux données de l'étudiant
        if photo_file:
            data['student']['photo'] = photo_file

        if sepa_file:
            data['sepa_file'] = sepa_file

        # Gérer le champ `_status`
        _status = data.pop('status', 0)
        _status = int(_status)

        # Récupérer le dossier d'inscription
        registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
        if not registerForm:
            return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)

        studentForm_serializer = RegistrationFormSerializer(registerForm, data=data, partial=True)
        if studentForm_serializer.is_valid():
            studentForm_serializer.save()

            # Sauvegarder la photo si elle est présente dans la requête
            if photo_file:
                student = registerForm.student

                # Vérifier si une photo existante est déjà associée à l'étudiant
                if student.photo and student.photo.name:
                    # Construire le chemin complet du fichier existant
                    if os.path.isabs(student.photo.name):
                        existing_file_path = student.photo.name
                    else:
                        existing_file_path = os.path.join(settings.MEDIA_ROOT, student.photo.name.lstrip('/'))

                    # Vérifier si le fichier existe et le supprimer
                    if os.path.exists(existing_file_path):
                        os.remove(existing_file_path)
                        student.photo.delete(save=False)
                    else:
                        print(f'File does not exist: {existing_file_path}')

                # Sauvegarder la nouvelle photo
                student.photo.save(photo_file.name, photo_file, save=True)
        else:
            return JsonResponse(studentForm_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)

        if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
            # Le parent a rempli le dossier d'inscription sans sélectionner "Prélèvement par Mandat SEPA"
            # L'école doit désormais valider le dossier d'inscription
            try:
                # Génération de la fiche d'inscription au format PDF
                base_dir = os.path.join(settings.MEDIA_ROOT, f"registration_files/dossier_rf_{registerForm.pk}")
                os.makedirs(base_dir, exist_ok=True)

                # Fichier PDF initial
                initial_pdf = f"{base_dir}/Inscription_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf"
                registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf)
                registerForm.save()

                # Envoi du mail d'inscription au second guardian si besoin
                guardians = registerForm.student.guardians.all()
                from Auth.models import Profile
                from N3wtSchool.mailManager import sendRegisterForm

                for guardian in guardians:
                    # Recherche de l'email dans le profil lié au guardian (si existant)
                    email = None
                    if hasattr(guardian, "profile_role") and guardian.profile_role and hasattr(guardian.profile_role, "profile") and guardian.profile_role.profile:
                        email = guardian.profile_role.profile.email
                    # Fallback sur le champ email direct (si jamais il existe)
                    if not email:
                        email = getattr(guardian, "email", None)
                    logger.debug(f"[RF_UNDER_REVIEW] Guardian id={guardian.id}, email={email}")
                    if email:
                        profile_exists = Profile.objects.filter(email=email).exists()
                        logger.debug(f"[RF_UNDER_REVIEW] Profile existe pour {email} ? {profile_exists}")
                        if not profile_exists:
                            logger.debug(f"[RF_UNDER_REVIEW] Envoi du mail d'inscription à {email} pour l'établissement {registerForm.establishment.pk}")
                            sendRegisterForm(email, registerForm.establishment.pk)

                # Mise à jour de l'automate
                # Vérification de la présence du fichier SEPA
                if registerForm.sepa_file:
                    # Mise à jour de l'automate pour SEPA
                    updateStateMachine(registerForm, 'EVENT_SIGNATURE_SEPA')
                else:
                    # Mise à jour de l'automate pour une signature classique
                    updateStateMachine(registerForm, 'EVENT_SIGNATURE')
            except Exception as e:
                logger.error(f"[RF_UNDER_REVIEW] Exception: {e}")
                import traceback
                logger.error(traceback.format_exc())
                return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        elif _status == RegistrationForm.RegistrationFormStatus.RF_SENT:
            if registerForm.status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
                # Envoi de l'email de refus aux responsables légaux
                try:
                    student = registerForm.student
                    student_name = f"{student.first_name} {student.last_name}"
                    notes = registerForm.notes or "Aucun motif spécifié"

                    guardians = student.guardians.all()
                    for guardian in guardians:
                        email = None
                        if hasattr(guardian, "profile_role") and guardian.profile_role and hasattr(guardian.profile_role, "profile") and guardian.profile_role.profile:
                            email = guardian.profile_role.profile.email
                        if not email:
                            email = getattr(guardian, "email", None)

                        if email:
                            logger.info(f"[RF_SENT] Envoi email de refus à {email} pour l'élève {student_name}")
                            mailer.sendRefusDossier(email, registerForm.establishment.pk, student_name, notes)
                except Exception as e:
                    logger.error(f"[RF_SENT] Erreur lors de l'envoi de l'email de refus: {e}")

                updateStateMachine(registerForm, 'EVENT_REFUSE')
                util.delete_registration_files(registerForm)
        elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_SENT:
            # Sauvegarde du mandat SEPA
            student = registerForm.student
            guardian = student.getMainGuardian()
            email = guardian.profile_role.profile.email
            errorMessage = mailer.sendMandatSEPA(email, registerForm.establishment.pk)
            if errorMessage != '':
                return JsonResponse({"errorMessage": errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
            registerForm.last_update = util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
            updateStateMachine(registerForm, 'EVENT_SEND_SEPA')
        elif _status == RegistrationForm.RegistrationFormStatus.RF_SEPA_TO_SEND:
            # Le parent a rempli le dossier d'inscription en sélectionnant "Prélèvement par Mandat SEPA"
            # L'école doit désormais envoyer le mandat SEPA pour poursuivre l'inscription
            updateStateMachine(registerForm, 'EVENT_WAITING_FOR_SEPA')

        elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
             # Vérifier si le paramètre fusion est activé via l'URL
            fusion = data.get('fusionParam', False)
            if fusion:
                # Fusion des documents
                # Récupération des fichiers schoolFileTemplates
                school_file_paths = RegistrationSchoolFileTemplate.get_files_from_rf(registerForm.pk)

                # Récupération des fichiers parentFileTemplates
                parent_file_templates = RegistrationParentFileTemplate.get_files_from_rf(registerForm.pk)

                # Initialisation de la liste des fichiers à fusionner
                fileNames = []

                # Régénérer la fiche élève avec le nouveau template avant fusion
                try:
                    base_dir = os.path.join(settings.MEDIA_ROOT, f"registration_files/dossier_rf_{registerForm.pk}")
                    os.makedirs(base_dir, exist_ok=True)
                    initial_pdf = f"{base_dir}/Inscription_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf"
                    registerForm.registration_file = util.rfToPDF(registerForm, initial_pdf)
                    registerForm.save()
                    logger.debug(f"[RF_VALIDATED] Fiche élève régénérée avant fusion")
                except Exception as e:
                    logger.error(f"[RF_VALIDATED] Erreur lors de la régénération de la fiche élève: {e}")

                # Ajout du fichier registration_file en première position
                if registerForm.registration_file:
                    fileNames.append(registerForm.registration_file.path)

                # Ajout des fichiers schoolFileTemplates
                fileNames.extend(school_file_paths)

                # Ajout des fichiers parentFileTemplates
                fileNames.extend(parent_file_templates)

                # Création du fichier PDF fusionné
                merged_pdf_content = None
                try:
                    merged_pdf_content = util.merge_files_pdf(fileNames)

                    # Mise à jour du champ fusion_file avec le fichier fusionné
                    util.save_file_replacing_existing(
                        registerForm.fusion_file,
                        "dossier_complet.pdf",
                        File(merged_pdf_content),
                        save=True
                    )
                except Exception as e:
                    logger.error(f"[RF_VALIDATED] Erreur lors de la fusion des fichiers: {e}")
                finally:
                    # Libérer explicitement la mémoire du BytesIO
                    if merged_pdf_content is not None:
                        merged_pdf_content.close()
            # Valorisation des StudentCompetency pour l'élève
            try:
                student = registerForm.student
                cycle = None
                if student.level:
                    cycle = student.level.cycle.number
                if cycle:
                    # Récupérer les EstablishmentCompetency de l'établissement et du cycle de l'élève
                    establishment_competencies = EstablishmentCompetency.objects.filter(
                        establishment=registerForm.establishment,
                        custom_category__domain__cycle=cycle
                    ) | EstablishmentCompetency.objects.filter(
                        establishment=registerForm.establishment,
                        competency__category__domain__cycle=cycle
                    )
                    establishment_competencies = establishment_competencies.distinct()

                    establishment = registerForm.establishment
                    evaluation_frequency = establishment.evaluation_frequency  # 1=Trimestre, 2=Semestre, 3=Année
                    school_year = registerForm.school_year  # ex: "2024_2025"

                    establishment_competencies = establishment_competencies.distinct()

                    periods = []
                    if evaluation_frequency == 1:  # Trimestre
                        periods = [f"T{i+1}_{school_year}" for i in range(3)]
                    elif evaluation_frequency == 2:  # Semestre
                        periods = [f"S{i+1}_{school_year}" for i in range(2)]
                    elif evaluation_frequency == 3:  # Année
                        periods = [f"A_{school_year}"]

                    for ec in establishment_competencies:
                        for period in periods:
                            StudentCompetency.objects.get_or_create(
                                student=student,
                                establishment_competency=ec,
                                period=period
                            )
            except Exception as e:
                logger.error(f"Erreur lors de la valorisation des StudentCompetency: {e}")

            # Envoi de l'email de validation aux responsables légaux (en arrière-plan)
            def send_validation_emails():
                try:
                    student = registerForm.student
                    student_name = f"{student.first_name} {student.last_name}"
                    class_name = None
                    if student.associated_class:
                        class_name = student.associated_class.atmosphere_name

                    guardians = student.guardians.all()
                    for guardian in guardians:
                        email = None
                        if hasattr(guardian, "profile_role") and guardian.profile_role and hasattr(guardian.profile_role, "profile") and guardian.profile_role.profile:
                            email = guardian.profile_role.profile.email
                        if not email:
                            email = getattr(guardian, "email", None)

                        if email:
                            logger.info(f"[RF_VALIDATED] Envoi email de validation à {email} pour l'élève {student_name}")
                            mailer.sendValidationDossier(email, registerForm.establishment.pk, student_name, class_name)
                except Exception as e:
                    logger.error(f"[RF_VALIDATED] Erreur lors de l'envoi de l'email de validation: {e}")

            # Lancer l'envoi d'email dans un thread séparé pour ne pas bloquer la réponse
            email_thread = threading.Thread(target=send_validation_emails)
            email_thread.start()

            updateStateMachine(registerForm, 'EVENT_VALIDATE')

        elif _status == RegistrationForm.RegistrationFormStatus.RF_ARCHIVED:
            # Vérifier si on vient de l'état "À valider" (RF_UNDER_REVIEW) pour un refus définitif
            if registerForm.status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
                # Envoi de l'email de refus définitif aux responsables légaux (en arrière-plan)
                def send_refus_definitif_emails():
                    try:
                        student = registerForm.student
                        student_name = f"{student.first_name} {student.last_name}"
                        notes = data.get('notes', '') or "Aucun motif spécifié"

                        guardians = student.guardians.all()
                        for guardian in guardians:
                            email = None
                            if hasattr(guardian, "profile_role") and guardian.profile_role and hasattr(guardian.profile_role, "profile") and guardian.profile_role.profile:
                                email = guardian.profile_role.profile.email
                            if not email:
                                email = getattr(guardian, "email", None)

                            if email:
                                logger.info(f"[RF_ARCHIVED] Envoi email de refus définitif à {email} pour l'élève {student_name}")
                                mailer.sendRefusDefinitif(email, registerForm.establishment.pk, student_name, notes)
                    except Exception as e:
                        logger.error(f"[RF_ARCHIVED] Erreur lors de l'envoi de l'email de refus définitif: {e}")

                # Lancer l'envoi d'email dans un thread séparé pour ne pas bloquer la réponse
                email_thread = threading.Thread(target=send_refus_definitif_emails)
                email_thread.start()

            updateStateMachine(registerForm, 'EVENT_ARCHIVE')

        # Retourner les données mises à jour
        return JsonResponse(studentForm_serializer.data, safe=False)

    @swagger_auto_schema(
        request_body=openapi.Schema(
            type=openapi.TYPE_OBJECT,
            properties={
                'student_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données étudiant'),
                'guardians_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données responsables'),
                'siblings_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données fratrie'),
                'payment_data': openapi.Schema(type=openapi.TYPE_STRING, description='JSON string des données de paiement'),
                'current_page': openapi.Schema(type=openapi.TYPE_INTEGER, description='Page actuelle du formulaire'),
                'auto_save': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='Indicateur auto-save'),
            }
        ),
        responses={200: RegistrationFormSerializer()},
        operation_description="Auto-sauvegarde partielle d'un dossier d'inscription.",
        operation_summary="Auto-sauvegarder un dossier d'inscription"
    )
    @method_decorator(csrf_protect, name='dispatch')
    @method_decorator(ensure_csrf_cookie, name='dispatch')
    def patch(self, request, id):
        """
        Auto-sauvegarde partielle d'un dossier d'inscription.
        Cette méthode est optimisée pour les sauvegardes automatiques périodiques.
        """
        try:
            # Récupérer le dossier d'inscription
            registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
            if not registerForm:
                return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)

            # Préparer les données à mettre à jour
            update_data = {}

            # Traiter les données étudiant si présentes
            if 'student_data' in request.data:
                try:
                    student_data = json.loads(request.data['student_data'])

                    # Extraire les données de paiement des données étudiant
                    payment_fields = ['registration_payment', 'tuition_payment', 'registration_payment_plan', 'tuition_payment_plan']
                    payment_data = {}

                    for field in payment_fields:
                        if field in student_data:
                            payment_data[field] = student_data.pop(field)

                    # Si nous avons des données de paiement, les traiter
                    if payment_data:
                        logger.debug(f"Auto-save: extracted payment_data from student_data = {payment_data}")

                        # Traiter les données de paiement
                        payment_updates = {}

                        # Gestion du mode de paiement d'inscription
                        if 'registration_payment' in payment_data and payment_data['registration_payment']:
                            try:
                                from School.models import PaymentMode
                                payment_mode = PaymentMode.objects.get(id=payment_data['registration_payment'])
                                registerForm.registration_payment = payment_mode
                                payment_updates['registration_payment'] = payment_mode.id
                            except PaymentMode.DoesNotExist:
                                logger.warning(f"Auto-save: PaymentMode with id {payment_data['registration_payment']} not found")

                        # Gestion du mode de paiement de scolarité
                        if 'tuition_payment' in payment_data and payment_data['tuition_payment']:
                            try:
                                from School.models import PaymentMode
                                payment_mode = PaymentMode.objects.get(id=payment_data['tuition_payment'])
                                registerForm.tuition_payment = payment_mode
                                payment_updates['tuition_payment'] = payment_mode.id
                            except PaymentMode.DoesNotExist:
                                logger.warning(f"Auto-save: PaymentMode with id {payment_data['tuition_payment']} not found")

                        # Gestion du plan de paiement d'inscription
                        if 'registration_payment_plan' in payment_data and payment_data['registration_payment_plan']:
                            try:
                                from School.models import PaymentPlan
                                payment_plan = PaymentPlan.objects.get(id=payment_data['registration_payment_plan'])
                                registerForm.registration_payment_plan = payment_plan
                                payment_updates['registration_payment_plan'] = payment_plan.id
                            except PaymentPlan.DoesNotExist:
                                logger.warning(f"Auto-save: PaymentPlan with id {payment_data['registration_payment_plan']} not found")

                        # Gestion du plan de paiement de scolarité
                        if 'tuition_payment_plan' in payment_data and payment_data['tuition_payment_plan']:
                            try:
                                from School.models import PaymentPlan
                                payment_plan = PaymentPlan.objects.get(id=payment_data['tuition_payment_plan'])
                                registerForm.tuition_payment_plan = payment_plan
                                payment_updates['tuition_payment_plan'] = payment_plan.id
                            except PaymentPlan.DoesNotExist:
                                logger.warning(f"Auto-save: PaymentPlan with id {payment_data['tuition_payment_plan']} not found")

                        # Sauvegarder les modifications de paiement
                        if payment_updates:
                            registerForm.save()
                            logger.debug(f"Auto-save: Payment data updated - {payment_updates}")

                    update_data['student'] = student_data
                except json.JSONDecodeError:
                    logger.warning("Auto-save: Invalid JSON in student_data")

            # Traiter les données des responsables si présentes
            if 'guardians_data' in request.data:
                try:
                    guardians_data = json.loads(request.data['guardians_data'])
                    logger.debug(f"Auto-save: guardians_data = {guardians_data}")

                    # Enregistrer directement chaque guardian avec le modèle
                    for i, guardian_data in enumerate(guardians_data):
                        guardian_id = guardian_data.get('id')
                        if guardian_id:
                            try:
                                # Récupérer le guardian existant et mettre à jour ses champs
                                guardian = Guardian.objects.get(id=guardian_id)

                                # Mettre à jour les champs si ils sont présents
                                if 'birth_date' in guardian_data and guardian_data['birth_date']:
                                    guardian.birth_date = guardian_data['birth_date']
                                if 'profession' in guardian_data:
                                    guardian.profession = guardian_data['profession']
                                if 'address' in guardian_data:
                                    guardian.address = guardian_data['address']
                                if 'phone' in guardian_data:
                                    guardian.phone = guardian_data['phone']
                                if 'first_name' in guardian_data:
                                    guardian.first_name = guardian_data['first_name']
                                if 'last_name' in guardian_data:
                                    guardian.last_name = guardian_data['last_name']

                                guardian.save()
                                logger.debug(f"Guardian {i}: Updated birth_date={guardian.birth_date}, profession={guardian.profession}, address={guardian.address}")

                            except Guardian.DoesNotExist:
                                logger.warning(f"Auto-save: Guardian with id {guardian_id} not found")

                except json.JSONDecodeError:
                    logger.warning("Auto-save: Invalid JSON in guardians_data")

            # Traiter les données de la fratrie si présentes
            if 'siblings_data' in request.data:
                try:
                    siblings_data = json.loads(request.data['siblings_data'])
                    logger.debug(f"Auto-save: siblings_data = {siblings_data}")

                    # Enregistrer directement chaque sibling avec le modèle
                    for i, sibling_data in enumerate(siblings_data):
                        sibling_id = sibling_data.get('id')
                        if sibling_id:
                            try:
                                # Récupérer le sibling existant et mettre à jour ses champs
                                from Subscriptions.models import Sibling
                                sibling = Sibling.objects.get(id=sibling_id)

                                # Mettre à jour les champs si ils sont présents
                                if 'first_name' in sibling_data:
                                    sibling.first_name = sibling_data['first_name']
                                if 'last_name' in sibling_data:
                                    sibling.last_name = sibling_data['last_name']
                                if 'birth_date' in sibling_data and sibling_data['birth_date']:
                                    sibling.birth_date = sibling_data['birth_date']

                                sibling.save()
                                logger.debug(f"Sibling {i}: Updated first_name={sibling.first_name}, last_name={sibling.last_name}, birth_date={sibling.birth_date}")

                            except Sibling.DoesNotExist:
                                logger.warning(f"Auto-save: Sibling with id {sibling_id} not found")

                except json.JSONDecodeError:
                    logger.warning("Auto-save: Invalid JSON in siblings_data")

            # Traiter les données de paiement si présentes
            if 'payment_data' in request.data:
                try:
                    payment_data = json.loads(request.data['payment_data'])
                    logger.debug(f"Auto-save: payment_data = {payment_data}")

                    # Mettre à jour directement les champs de paiement du formulaire
                    payment_updates = {}

                    # Gestion du mode de paiement d'inscription
                    if 'registration_payment' in payment_data and payment_data['registration_payment']:
                        try:
                            from School.models import PaymentMode
                            payment_mode = PaymentMode.objects.get(id=payment_data['registration_payment'])
                            registerForm.registration_payment = payment_mode
                            payment_updates['registration_payment'] = payment_mode.id
                        except PaymentMode.DoesNotExist:
                            logger.warning(f"Auto-save: PaymentMode with id {payment_data['registration_payment']} not found")

                    # Gestion du mode de paiement de scolarité
                    if 'tuition_payment' in payment_data and payment_data['tuition_payment']:
                        try:
                            from School.models import PaymentMode
                            payment_mode = PaymentMode.objects.get(id=payment_data['tuition_payment'])
                            registerForm.tuition_payment = payment_mode
                            payment_updates['tuition_payment'] = payment_mode.id
                        except PaymentMode.DoesNotExist:
                            logger.warning(f"Auto-save: PaymentMode with id {payment_data['tuition_payment']} not found")

                    # Gestion du plan de paiement d'inscription
                    if 'registration_payment_plan' in payment_data and payment_data['registration_payment_plan']:
                        try:
                            from School.models import PaymentPlan
                            payment_plan = PaymentPlan.objects.get(id=payment_data['registration_payment_plan'])
                            registerForm.registration_payment_plan = payment_plan
                            payment_updates['registration_payment_plan'] = payment_plan.id
                        except PaymentPlan.DoesNotExist:
                            logger.warning(f"Auto-save: PaymentPlan with id {payment_data['registration_payment_plan']} not found")

                    # Gestion du plan de paiement de scolarité
                    if 'tuition_payment_plan' in payment_data and payment_data['tuition_payment_plan']:
                        try:
                            from School.models import PaymentPlan
                            payment_plan = PaymentPlan.objects.get(id=payment_data['tuition_payment_plan'])
                            registerForm.tuition_payment_plan = payment_plan
                            payment_updates['tuition_payment_plan'] = payment_plan.id
                        except PaymentPlan.DoesNotExist:
                            logger.warning(f"Auto-save: PaymentPlan with id {payment_data['tuition_payment_plan']} not found")

                    # Sauvegarder les modifications de paiement
                    if payment_updates:
                        registerForm.save()
                        logger.debug(f"Auto-save: Payment data updated - {payment_updates}")

                except json.JSONDecodeError:
                    logger.warning("Auto-save: Invalid JSON in payment_data")

            # Mettre à jour la page actuelle si présente
            if 'current_page' in request.data:
                try:
                    current_page = int(request.data['current_page'])
                    # Vous pouvez sauvegarder cette info dans un champ du modèle si nécessaire
                    logger.debug(f"Auto-save: current_page = {current_page}")
                except (ValueError, TypeError):
                    logger.warning("Auto-save: Invalid current_page value")

            # Effectuer la mise à jour partielle seulement si nous avons des données
            if update_data:
                serializer = RegistrationFormSerializer(registerForm, data=update_data, partial=True)
                if serializer.is_valid():
                    serializer.save()
                    logger.debug(f"Auto-save successful for student {id}")
                    return JsonResponse({"status": "auto_save_success", "timestamp": util._now().isoformat()}, safe=False)
                else:
                    logger.warning(f"Auto-save validation errors: {serializer.errors}")
                    # Pour l'auto-save, on retourne un succès même en cas d'erreur de validation
                    return JsonResponse({"status": "auto_save_partial", "errors": serializer.errors}, safe=False)
            else:
                # Pas de données à sauvegarder, mais on retourne un succès
                return JsonResponse({"status": "auto_save_no_data"}, safe=False)

        except Exception as e:
            logger.error(f"Auto-save error for student {id}: {str(e)}")
            # Pour l'auto-save, on ne retourne pas d'erreur HTTP pour éviter d'interrompre l'UX
            return JsonResponse({"status": "auto_save_failed", "error": str(e)}, safe=False)

    @swagger_auto_schema(
        responses={204: 'No Content'},
        operation_description="Supprime un dossier d'inscription donné.",
        operation_summary="Supprimer un dossier d'inscription"
    )
    @method_decorator(csrf_protect, name='dispatch')
    @method_decorator(ensure_csrf_cookie, name='dispatch')
    def delete(self, request, id):
        """
        Supprime un dossier d'inscription donné.
        """
        register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
        if register_form != None:
            student = register_form.student
            student.guardians.clear()
            student.profiles.clear()
            student.registration_files.clear()
            student.delete()

            return JsonResponse("La suppression du dossier a été effectuée avec succès", safe=False)

        return JsonResponse({"errorMessage":'Aucun dossier d\'inscription rattaché à l\'élève'}, safe=False, status=status.HTTP_404_NOT_FOUND)

@swagger_auto_schema(
    method='get',
    responses={200: openapi.Response('Success', schema=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'message': openapi.Schema(type=openapi.TYPE_STRING)
        }
    ))},
    operation_description="Envoie le dossier d'inscription par e-mail",
    operation_summary="Envoyer un dossier d'inscription"
)
@api_view(['GET'])
def send(request,id):
    """Envoie le dossier d'inscription par e-mail."""
    register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
    if register_form != None:
        student = register_form.student
        guardian = student.getMainGuardian()
        email = guardian.profile_role.profile.email
        errorMessage = mailer.sendRegisterForm(email, register_form.establishment.pk)
        if errorMessage == '':
            register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
            updateStateMachine(register_form, 'EVENT_SEND')
            return JsonResponse({"message": f"Le dossier d'inscription a bien été envoyé à l'addresse {email}"}, safe=False)
        return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
    return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)

@swagger_auto_schema(
    method='get',
    responses={200: openapi.Response('Success', schema=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'message': openapi.Schema(type=openapi.TYPE_STRING)
        }
    ))},
    operation_description="Archive le dossier d'inscription",
    operation_summary="Archiver un dossier d'inscription"
)
@api_view(['GET'])
def archive(request,id):
    """Archive le dossier d'inscription."""
    register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
    if register_form != None:
        register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
        updateStateMachine(register_form, 'EVENT_ARCHIVE')
        return JsonResponse({"message": "Le dossier a été archivé avec succès"}, safe=False)
    return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)

@swagger_auto_schema(
    method='get',
    responses={200: openapi.Response('Success', schema=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'message': openapi.Schema(type=openapi.TYPE_STRING)
        }
    ))},
    operation_description="Relance un dossier d'inscription par e-mail",
    operation_summary="Relancer un dossier d'inscription"
)
@api_view(['GET'])
def resend(request,id):
    """Relance un dossier d'inscription par e-mail."""
    register_form = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)
    if register_form != None:
        student = register_form.student
        guardian = student.getMainGuardian()
        email = guardian.email
        errorMessage = mailer.envoieRelanceDossierInscription(email, register_form.codeLienInscription)
        if errorMessage == '':
            register_form.status=RegistrationForm.RegistrationFormStatus.RF_SENT
            register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
            register_form.save()
            return JsonResponse({"message": f"Le dossier a été renvoyé à l'adresse {email}"}, safe=False)
        return JsonResponse({"errorMessage":errorMessage}, safe=False, status=status.HTTP_400_BAD_REQUEST)
    return JsonResponse({"errorMessage":'Dossier d\'inscription non trouvé'}, safe=False, status=status.HTTP_404_NOT_FOUND)

@swagger_auto_schema(
    method='get',
    responses={200: openapi.Response('Success', schema=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'message': openapi.Schema(type=openapi.TYPE_STRING)
        }
    ))},
    operation_description="Récupère les fichiers à signer d'un dossier d'inscription donné",
    operation_summary="Récupérer les fichiers à signer d'un dossier d'inscription donné"
)
@api_view(['GET'])
def get_school_file_templates_by_rf(request, id):
    try:
        # Récupérer les templates associés au RegistrationForm donné
        templates = RegistrationSchoolFileTemplate.objects.filter(registration_form=id)

        # Sérialiser les données
        serializer = RegistrationSchoolFileTemplateSerializer(templates, many=True)

        # Retourner les données sérialisées
        return JsonResponse(serializer.data, safe=False)
    except RegistrationSchoolFileTemplate.DoesNotExist:
        return JsonResponse({'error': 'Aucun template trouvé pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND)

@swagger_auto_schema(
    method='get',
    responses={200: openapi.Response('Success', schema=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            'message': openapi.Schema(type=openapi.TYPE_STRING)
        }
    ))},
    operation_description="Récupère les pièces à fournir d'un dossier d'inscription donné",
    operation_summary="Récupérer les pièces à fournir d'un dossier d'inscription donné"
)
@api_view(['GET'])
def get_parent_file_templates_by_rf(request, id):
    try:
        # Récupérer les pièces à fournir associés au RegistrationForm donné
        parent_files = RegistrationParentFileTemplate.objects.filter(registration_form=id)

        # Sérialiser les données
        serializer = RegistrationParentFileTemplateSerializer(parent_files, many=True)

        # Retourner les données sérialisées
        return JsonResponse(serializer.data, safe=False)
    except RegistrationParentFileTemplate.DoesNotExist:
        return JsonResponse({'error': 'Aucune pièce à fournir trouvée pour ce dossier d\'inscription'}, status=status.HTTP_404_NOT_FOUND)

@swagger_auto_schema(
    method='get',
    responses={200: openapi.Response('PDF file', schema=openapi.Schema(type=openapi.TYPE_FILE))},
    operation_description="Génère et retourne le PDF de la fiche élève à la volée",
    operation_summary="Télécharger la fiche élève (régénérée)"
)
@api_view(['GET'])
def generate_registration_pdf(request, id):
    try:
        registerForm = RegistrationForm.objects.select_related('student', 'establishment').get(student__id=id)
    except RegistrationForm.DoesNotExist:
        return JsonResponse({"error": "Dossier d'inscription introuvable"}, status=status.HTTP_404_NOT_FOUND)

    try:
        pdf_content = util.generateRegistrationPDF(registerForm)
    except ValueError as e:
        return JsonResponse({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    filename = f"Inscription_{registerForm.student.last_name}_{registerForm.student.first_name}.pdf"
    response = HttpResponse(pdf_content, content_type='application/pdf')
    response['Content-Disposition'] = f'inline; filename="{filename}"'
    return response
