from django.http.response import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
from django.utils.decorators import method_decorator
from django.core.cache import cache
from rest_framework.parsers import JSONParser
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
from django.core.files import File

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

from Subscriptions.serializers import RegistrationFormSerializer
from Subscriptions.pagination import CustomPagination
from Subscriptions.signals import clear_cache
from Subscriptions.models import Student, Guardian, RegistrationForm, RegistrationTemplate, RegistrationFileGroup
from Subscriptions.automate import updateStateMachine

from N3wtSchool import settings, bdd

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 = CustomPagination

    @swagger_auto_schema(
        manual_parameters=[
            openapi.Parameter('filter', openapi.IN_QUERY, description="filtre", type=openapi.TYPE_STRING, enum=['pending', 'archived', 'subscribed'], 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": "pending",
                    "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": "archived",
                    "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()
        search = request.GET.get('search', '').strip()
        page_size = request.GET.get('page_size', None)
        establishment_id = request.GET.get('establishment_id', None)

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

        # Définir le cache_key en fonction du filtre
        page_number = request.GET.get('page', 1)
        cache_key = f'N3WT_ficheInscriptions_{establishment_id}_{filter}_page_{page_number}_search_{search if filter == "pending" else ""}'
        cached_page = cache.get(cache_key)
        if cached_page:
            return JsonResponse(cached_page, safe=False)

        # Récupérer les dossier d'inscriptions en fonction du filtre
        registerForms_List = None
        if filter == 'pending':
            exclude_states = [RegistrationForm.RegistrationFormStatus.RF_VALIDATED, RegistrationForm.RegistrationFormStatus.RF_ARCHIVED]
            registerForms_List = bdd.searchObjects(RegistrationForm, search, _excludeStates=exclude_states)
        elif filter == 'archived':
            registerForms_List = bdd.getObjects(RegistrationForm, 'status', RegistrationForm.RegistrationFormStatus.RF_ARCHIVED)
        elif filter == 'subscribed':
            registerForms_List = bdd.getObjects(RegistrationForm, 'status', RegistrationForm.RegistrationFormStatus.RF_VALIDATED)
        else:
            registerForms_List = None

        if registerForms_List:
            print(f'filtrate sur lestablishment : {establishment_id}')
            registerForms_List = registerForms_List.filter(establishment=establishment_id)

        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)
            cache.set(cache_key, response_data, timeout=60 * 15)
            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": "pending",
                "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, 'creationDI')

            # 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 = CustomPagination

    @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 = JSONParser().parse(request)
        _status = studentForm_data.pop('status', 0)
        studentForm_data["last_update"] = str(util.convertToStr(util._now(), '%d-%m-%Y %H:%M'))
        registerForm = bdd.getObject(_objectName=RegistrationForm, _columnName='student__id', _value=id)

        if _status == RegistrationForm.RegistrationFormStatus.RF_UNDER_REVIEW:
            try:
                # Génération de la fiche d'inscription au format PDF
                base_dir = f"data/registration_files/dossier_rf_{registerForm.pk}"
                os.makedirs(base_dir, exist_ok=True)

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

                # Récupération des fichiers d'inscription
                fileNames = RegistrationTemplate.get_files_from_rf(registerForm.pk)
                if registerForm.registration_file:
                    fileNames.insert(0, registerForm.registration_file.path)

                # Création du fichier PDF Fusionné
                merged_pdf = f"{base_dir}/dossier_complet_{registerForm.pk}.pdf"
                util.merge_files_pdf(fileNames, merged_pdf)

                # Mise à jour du champ registration_file avec le fichier fusionné
                with open(merged_pdf, 'rb') as f:
                    registerForm.registration_file.save(
                        os.path.basename(merged_pdf),
                        File(f),
                        save=True
                    )

                # Mise à jour de l'automate
                updateStateMachine(registerForm, 'saisiDI')
            except Exception as e:
                return JsonResponse({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        elif _status == RegistrationForm.RegistrationFormStatus.RF_VALIDATED:
            # L'école a validé le dossier d'inscription
            # Mise à jour de l'automate
            updateStateMachine(registerForm, 'valideDI')

        studentForm_serializer = RegistrationFormSerializer(registerForm, data=studentForm_data)
        if studentForm_serializer.is_valid():
            studentForm_serializer.save()
            return JsonResponse(studentForm_serializer.data, safe=False)

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

    @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()
            clear_cache()

            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.email
        errorMessage = mailer.sendRegisterForm(email)
        if errorMessage == '':
            register_form.last_update=util.convertToStr(util._now(), '%d-%m-%Y %H:%M')
            updateStateMachine(register_form, 'envoiDI')
            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, 'archiveDI')
        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_templates_by_rf(request, id):
    try:
        templates = RegistrationTemplate.objects.filter(registration_form=id)
        templates_data = list(templates.values())
        return JsonResponse(templates_data, safe=False)
    except RegistrationFileGroup.DoesNotExist:
        return JsonResponse({'error': 'Le groupe de fichiers n\'a pas été trouvé'}, status=404)