from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import NotFound
from course.models import Course
from exercise.models import Exercise
from flashcard.models import Flashcard
from course.serializers import CourseSerializer, CourseListSerializer, PublicCourseSerializer, CreateCourseSerializer
from flashcard.serializers import FlashcardSerializer, FlashcardKnowledgeSerializer
from exercise.serializers import ExerciseSerializer
from knowledge.models import Knowledge
from knowledge.serializers import KnowledgeDetailSerializer, KnowledgeSerializer
from user.models import User
from django.core.mail import send_mail
from django.utils import timezone
import random
import json
from config.const import AcademicLevel, SubjectStudied
from utils.stringifiers import markdownToQuilldelta, closeJSON
from django.core.cache import cache
import requests
from config.authentication import (
    JWTAuthenticationWithUserCheck,
    IsAuthenticatedAndOwner,
    IsAuthenticated
)
from utils.parsing import parse_syntactic
import sys
from concurrent.futures import ThreadPoolExecutor
from utils.formate import format_data
from utils.token_management import has_tokens, add_tokens_used
from utils.generate_course import getCourseV2
from utils.course_limit_reached import course_limit_reached
from utils.create_course import create_course_with_content, copy_course

class CourseListView(APIView):
    authentication_classes = [JWTAuthenticationWithUserCheck]
    permission_classes = [IsAuthenticatedAndOwner]

    @staticmethod
    def saveContentFlashcards(contentFlashcards, course):
        newContent = []
        for fls in contentFlashcards:
            flashcard_data = {
                'course': str(course.publicIdentifier),
                'knowledge': fls.get('knowledge', None),
                'active': fls.get('active', False),
                'title': fls.get('title', '')
            }
            flashcard_serializer = FlashcardKnowledgeSerializer(data=flashcard_data)
            if flashcard_serializer.is_valid():
                flashcard = flashcard_serializer.save()
                if ('inContent' in fls and fls['inContent']):
                    newContent.append(str(flashcard.publicIdentifier))
        return newContent

    def get(self, request):
        courses = Course.objects.filter(user=request.user.publicIdentifier)
        serializer = CourseListSerializer(courses, many=True)
        return Response(serializer.data)

    def post(self, request):
        if course_limit_reached(request.user):
            return Response({"error": "Course limit reached"}, status=status.HTTP_402_PAYMENT_REQUIRED)
        
        data = request.data.copy()

        content_list = [{'type': 'text'}]

        if 'content' in request.data and isinstance(request.data['content'], list):
            content_list = request.data['content']

        if 'content' in data:
            data['content'] = []
        data['user'] = request.user.publicIdentifier

        print("[CourseListView.post] data: ", data)
        print("[CourseListView.post] content_list: ", content_list)
        
        course = create_course_with_content(data, content_list)
        serializer = CourseSerializer(course)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

class CourseDetailView(APIView):
    authentication_classes = [JWTAuthenticationWithUserCheck]
    permission_classes = [IsAuthenticatedAndOwner]

    @staticmethod
    def get_object(pid, request=None):
        error_response = {"error": "Course not found"}
        try:
            if request:
                return Course.objects.get(publicIdentifier=pid, user=request.user.publicIdentifier)
            else:
                return Course.objects.get(publicIdentifier=pid)
        except Course.DoesNotExist:
            raise NotFound(error_response) # Ce n'est peut-être pas utile, car IsAuthenticatedAndOwner vérifie déjà la propriété si on lui fourni une classe get_object

    def get(self, request, pid):
        course = CourseDetailView.get_object(pid, request=request)
        serializer = CourseSerializer(course)
        return Response(serializer.data)

    def put(self, request, pid):
        course = CourseDetailView.get_object(pid, request=request)
        data = request.data.copy()
        data['user'] = request.user.publicIdentifier
        serializer = CourseSerializer(course, data=data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pid):
        course = CourseDetailView.get_object(pid, request=request)
        course.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

class PublicCourseListView(APIView):
    authentication_classes = [JWTAuthenticationWithUserCheck]
    permission_classes = [IsAuthenticated]

    def get(self, request):
        courses = Course.objects.filter(shared=True)
        serializer = CourseSerializer(courses, many=True)
        return Response(serializer.data)

class PublicCourseView(APIView):
    authentication_classes = [JWTAuthenticationWithUserCheck]
    permission_classes = [IsAuthenticated]

    def has_access(self, request, course):
        # Si le cours est public, accès autorisé
        if course.shared:
            return True
        # Si l'utilisateur est authentifié, vérifie si son email est dans la liste
        if request.user and request.user.is_authenticated:
            return request.user.email in course.emailsAllowedToAccess
        return False

    def get_object(self, accessCode, request):
        error_response = {"error": "Course not found, or you don't have access to it", "code": "not_found_or_no_access"}
        try:
            course = Course.objects.get(accessCode=accessCode)
            if self.has_access(request, course):
                return course
            else:
                raise NotFound(error_response)
        except:
            raise NotFound(error_response)

    def get(self, request, accessCode):
        # Récupérer le paramètre 'name' s'il est fourni
        name = request.GET.get('name', '')
        
        course = self.get_object(accessCode, request)

        if name:
            course.name = name

        copy = copy_course(course, request.user)

        if copy:
            serializer = CourseListSerializer(copy)
            return Response(serializer.data)
        else:
            return Response({"error": "An error occured while copying the course", "code": "failed_to_copy_course"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class CreateSlidesView(APIView):
    authentication_classes = [JWTAuthenticationWithUserCheck]
    permission_classes = [IsAuthenticatedAndOwner]

    @staticmethod
    def createSlides(slides):
        formatted_slides = []
        for slide in slides:
            # Si l'image n'a pas déjà un en-tête data:image/
            if not slide.startswith('data:image/'):
                # On suppose que c'est une image en base64, on ajoute l'en-tête approprié
                # On vérifie si c'est une image PNG (commence par iVBORw0KGgo)
                if slide.startswith('iVBORw0KGgo'):
                    slide = f"data:image/png;base64,{slide}"
                else:
                    # Pour les autres formats, on utilise JPEG par défaut
                    slide = f"data:image/jpeg;base64,{slide}"
            slide_block = {
                'type': 'text',
                'data': {'name': '', 'content': {'ops': [{"insert": {"image": slide}}]}}
            }
            formatted_slides.append(slide_block)
        return formatted_slides

    def post(self, request):
        print("[CreateSlidesView.post] Début de la création des slides")
        
        if course_limit_reached(request.user):
            print("[CreateSlidesView.post] Limite de cours atteinte pour l'utilisateur")
            return Response({"error": "Course limit reached"}, status=status.HTTP_402_PAYMENT_REQUIRED)
            
        slides = request.data.get('slides')
        print(f"[CreateSlidesView.post] Nombre de slides reçues: {len(slides) if slides else 0}")
        
        if not slides or len(slides) < 1:
            print("[CreateSlidesView.post] Aucune slide fournie")
            return Response({'error': "You must send a 'slides' item that is an array in the request"}, status=status.HTTP_400_BAD_REQUEST)

        # Création explicite du dictionnaire
        course_data = {
            'name': request.data.get('name', ''),
            'user': str(request.user.publicIdentifier),
            'content': [],
            'emailsAllowedToAccess': [],
            # Copier d'autres champs si nécessaire
            'academicLevel': request.data.get('academicLevel'),
            'subjectStudied': request.data.get('subjectStudied')
        }
        print(f"[CreateSlidesView.post] Données du cours: {course_data}")
        
        # Utilisation de createSlides pour formater les slides
        content_list = self.createSlides(slides)
        print(f"[CreateSlidesView.post] Nombre d'éléments de contenu créés: {len(content_list)}")

        try:
            print("[CreateSlidesView.post] Tentative de création du cours")
            # Création du cours et de son contenu
            course = create_course_with_content(course_data, content_list)
            print(f"[CreateSlidesView.post] Cours créé avec succès, ID: {course.publicIdentifier}")
            # Sérialisation pour la réponse
            response_serializer = CourseSerializer(course)
            return Response(response_serializer.data, status=status.HTTP_201_CREATED)
        except Exception as e:
            print(f"[CreateSlidesView.post] Erreur lors de la création du cours: {str(e)}")
            return Response(
                {'error': f"Une erreur est survenue lors de la création du cours: {str(e)}"},
                status=status.HTTP_400_BAD_REQUEST
            )


class GenerateCourseView(APIView):
    authentication_classes = [JWTAuthenticationWithUserCheck]
    permission_classes = [IsAuthenticatedAndOwner]

    def post(self, request):
        print("Début de la génération du cours")
        if not has_tokens(request.user):
            return Response(
                {"error": "Active subscription required"}, 
                status=status.HTTP_402_PAYMENT_REQUIRED
            )

        documents = request.data.get('documents')
        if not documents or len(documents) == 0:
            return Response(
                {"error": "No documents provided"}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        language = request.data.get('language')

        if request.user.school:
            course_data['tags'] = {'school': request.user.school}

        title, course, metadata, code_number = getCourseV2(
            documents=documents,
            user=request.user,
            language=request.data.get('language')
        )

        default_academic_level = request.user.defaultAcademicLevel if request.user.defaultAcademicLevel else 'na'
        course_data = {
            'user': request.user.publicIdentifier,
            'academicLevel': request.data.get('academicLevel', default_academic_level)
        }
        course_data['name'] = request.data.get('name', title) if title else ""

        if metadata is not None:
            course_data['tags'] = {**metadata, **(course_data.get('tags', {}))}

        course_model = create_course_with_content(course_data, course)

        serializer = CreateCourseSerializer(course_model)
        request.user.special_service_used += 1
        request.user.save()
        return Response(serializer.data)

        # except Exception as e:
        #     return Response(
        #         {"error": "A strange error appened. Code : R100"}, 
        #         status=status.HTTP_500_INTERNAL_SERVER_ERROR
        #     )


        # Il reste à ajouter une sortie équivalente à metadata de partout (dans la fonction getCourseV2)
        # Rajouter quelques éléments pour la gestion des cas particuliers (ex : quand metadata est None)
        # Tout retester et redébugger si nécessaire
        # Plus tard, on pourra ajouter la gestion des images (récupération des images dans les documents fournis, génération des descriptions, etc...)
        # Pour l'instant, retourner dans le front et vérifier que tout fonctionne au niveau de la connexion à l'api (avec l'authentification)
        # Puis modifier le formulaire de création de compte + la page d'acceuil + le formulaire de création de cours
        # + créer la page de paiement (il faudra probablement redévelopper pas mal de trucs en back rien que pour ça)


class GenerateResumeView(APIView):
    authentication_classes = [JWTAuthenticationWithUserCheck]
    permission_classes = [IsAuthenticatedAndOwner]
               
    @staticmethod
    def getImageDescription(image_base64, user):
        # Vérifier si l'utilisateur peut utiliser des tokens
        if not check_user_tokens(user):
            return Response({"error": "Token limit exceeded"}, status=status.HTTP_403_FORBIDDEN)

        assistant_key = 'asst_eVX6KzVt0nD5NAM0oCZAGRWZ'
        api_key2 = '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'
        api_key = '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'
        url = "https://api.openai.com/v1/chat/completions"
        headers = {
            "Authorization": f"Bearer {api_key}",
            'Content-Type': 'application/json'
        }
        body = {
            "model": "gpt-4o-mini",
            "messages": [
                {
                    "role": "user",
                    "content": [
                        {"type": "text",
                         "text": "Tu es une IA intégrée à une application d'aide à la formation pour des étudiants et des apprenants. Tu vas recevoir une image en entrée, qui fait partie d'un cours. Définit si l'image est importante ou non pour la compréhension du cours. Une image est considérée comme importante si elle contient des schémas, de graphiques, ou des textes explicatifs. Par contre, une image n'est pas importante si : il s'agit d'un paysage, si elle contient le visage ou une partie du corps d'une personne, si c'est un logo, ou si l'image n'a pas de rapport avec une matière scolaire."
                        + "Dans le cas spécifique ou l'image est importante mais qu'elle est tronquée, même un tout petit peu (et même s'il ne s'agit que d'un bout de texte), place-la dans une catégorie à part. "
                        + "Si l'image est importante, fait une description de l'image. Cette description est destinée à être fournie à une autre IA. Tu dois indiquer concepts évoqués dans l'image, et décrire en quelques mots ce que l'image explique ou permet de comprendre. Ne dépasse pas les 40 mots. Renvoie le résultat sous la forme d'un JSON."
                        + "Si l'image est importante, renvoie un JSON sous la forme {'i':1, 'd': 'description de l'image'}."
                        + "Si l'image n'est pas importante, renvoie un JSON sous la forme {'i': 0}."
                        + "Dans tout les cas, génère le JSON sans retour à la ligne ni espace supplémentaire, et ne génère JAMAIS plus de 60 tokens."
                        },
                        {
                            "type": "image_url",
                            "image_url": {
                                "detail": "low",
                                "url": f"{image_base64}"
                            }
                        }
                    ]
                }
            ],
            "max_completion_tokens": 63,
            "response_format": {"type": "json_object"}
        }
        response = requests.post(url, headers=headers, json=body)
        if response.status_code == 200:
            try:
                result = json.loads(response.json()['choices'][0]['message']['content'])
                # Ajouter les tokens utilisés au compte de l'utilisateur
                tokens_used = len(response.json()['choices'][0]['message']['content'].split())
                add_tokens_used(user, tokens_used)
                return result
            except json.JSONDecodeError:
                return None
        return None
    
    @staticmethod
    def generateContent(prompt, entry, user):
        # Vérifier si l'utilisateur peut utiliser des tokens
        if not check_user_tokens(user):
            return Response({"error": "Token limit exceeded"}, status=status.HTTP_403_FORBIDDEN)

        error = 0
        api_key = "H20rSMbOuAzRkkuUTUS64tfTjyzfPDRb"
        url = "https://api.openai.com/v1/chat/completions"
        headers = {
            'Authorization': f'Bearer {api_key}',
            'Content-Type': 'application/json',
        }
        body = {
            "model": "gpt-4o-mini",
            "messages": [
                {
                    "role": "system",
                    "content": prompt
                },
                {
                    "role": "user",
                    "content": json.dumps(entry)
                }
            ],
            "max_completion_tokens": 63,
            "response_format": {"type": "json_object"}
        }
        response = requests.post(url, headers=headers, json=body)
        if response.status_code == 200:
            try:
                result = json.loads(response.json()['choices'][0]['message']['content'])
                # Ajouter les tokens utilisés au compte de l'utilisateur
                tokens_used = len(response.json()['choices'][0]['message']['content'].split())
                add_tokens_used(user, tokens_used)
                return result
            except json.JSONDecodeError:
                return None
        return None

    @staticmethod
    def generateCourseStruct(content, user, language=None):
        # Vérifier si l'utilisateur peut utiliser des tokens
        if not check_user_tokens(user):
            return Response({"error": "Token limit exceeded"}, status=status.HTTP_403_FORBIDDEN)

        api_key = "H20rSMbOuAzRkkuUTUS64tfTjyzfPDRb"
        url = "https://api.mistral.ai/v1/chat/completions"
        headers = {
            'Authorization': f'Bearer {api_key}',
            'ContentType': 'application/json',
        }
        cmpl_prompt = ""
        if language: 
            cmpl_prompt = f"Génère un texte en {language}."
        body = {
            "model": "mistral-small-latest",
            "messages": [
                {
                    "role": "system",
                    "content": "Tu es une IA intégrée à une application de cours pour des étudiants. Tu vas recevoir un très long texte en entrée, ainsi que la description de plusieurs images, correspondant au texte et aux images contenues dans un cours. Identifie-y l'ensemble des notions importantes à la compréhension du cours. Une notion N'EST PAS IMPORTANTE si c'est une information sur une école, un professeur, sur une personne, sur le déroulé du cours, ou si c'est une information qui n'a rien à voir avec une matière scolaire."
                        + "Une information EST IMPORTANTE si c'est une définition, une énonciation de faits, une loi ou une régle, une démonstration, un exemple, une introduction, une conclusion, ou une explication. Pour chaque notion importante, génère des instructions destinées à être fournie à une autre IA, pour qu'elle génère la partie du cours dédiée. Intégre le plus d'information possible dans l'instruction."
                        + "Classe ensuite ces notions en deux catégories: - le vocabulaire, les définitions et les concepts - Les phénomènes et les démonstrations. Renvoie le résultat sous la forme du JSON avec {subject: 'sujet du cours', course: [{'p': 'Définitions et vocabulaire', 'c': ['notion1motclé1 notion2motclé2 ...', 'notion2motclé1 notion2motclé2 ...', ...] }, {'p': 'Explication et démonstrations', 'c': ['notion1motclé1 notion2motclé2 ...', 'notion2motclé1 notion2motclé2 ...', ...] } ] }. Dans la partie 'Définitions et vocabulaire', renvoie toutes les notions qui sont du vocabulaire, des définitions et des concepts. Dans la partie 'Explication et démonstrations', renvoie toutes les notions décrivant des phénomènes plus complexes. "
                        + "Assure-toi qu'aucune notion n'est présente en double. "
                        + "Enfin, définit le sujet du cours. Renvoie la réponse sous la forme d'un SHORT objet JSON avec les clés 'subject' et 'course'. La clé subject contient le sujet du cours (sous forme de string). La clé course contient une liste de chapitre. Chaque chapitre est un JSON avec une clé 'p', qui contient le titre du chapitre, et une clé 'c', qui contient la liste des instructions à envoyer. Tu dois donc renvoyer un objet avec la structure suivante : {'subject': 'sujet du cours', 'course': [{'p':'Définitions et vocabulaire', 'c': ['notion 1 consignes', 'notion 2 consignes', ...] }, {'p':'Explication et démonstrations', 'c': ['notion 1 consignes', 'notion 2 consignes', ...] } ] }."
                        + "Génère un JSON COURT. Génère MOINS de 4000 tokens." + cmpl_prompt
                },
                {
                    "role": "user",
                    "content": content
                },
            ],
            "response_format": {"type": "json_object"},
            "temperature": 0.7,
            "top_p": 1,
            "max_tokens": 5200,
            "stream": False,
            "safe_prompt": False,
            "random_seed": 1337
        }
        response = requests.post(url, headers=headers, json=body)
        errorCode = 0
        result = None
        APIresponse = None
        if response.status_code == 200:
            APIresponse = response.json()['choices'][0]['message']['content']
            # Ajouter les tokens utilisés au compte de l'utilisateur
            tokens_used = len(APIresponse.split())
            add_tokens_used(user, tokens_used)
            try:
                result = json.loads(APIresponse)
            except:
                try:
                    errorCode = 2
                    result = json.loads(closeJSON(APIresponse))
                except:
                    errorCode = -2
                    result = {"subject": "error", "course": [{'p': "error " + str(errorCode), 'c': [json.dumps(response.json())]}]} 

        else:
            errorCode = -1
            result = {"subject": "error", "course": [{'p': "error " + str(errorCode), 'c': [json.dumps(response.json())]}]}
        
        return result, errorCode  
    
    @staticmethod
    def generateCourseStruct(content, language=None):
        api_key = "H20rSMbOuAzRkkuUTUS64tfTjyzfPDRb"
        url = "https://api.mistral.ai/v1/chat/completions"
        headers = {
            'Authorization': f'Bearer {api_key}',
            'ContentType': 'application/json',
        }
        cmpl_prompt = ""
        if language: 
            cmpl_prompt = f"Génère un texte en {language}."
        body = {
            "model": "mistral-small-latest",
            "messages": [
                {
                    "role": "system",
                    "content": "Tu es une IA intégrée à une application de cours pour des étudiants. Tu vas recevoir un très long texte en entrée, ainsi que la description de plusieurs images, correspondant au texte et aux images contenues dans un cours. Identifie-y l'ensemble des notions importantes à la compréhension du cours. Une notion N'EST PAS IMPORTANTE si c'est une information sur une école, un professeur, sur une personne, sur le déroulé du cours, ou si c'est une information qui n'a rien à voir avec une matière scolaire."
                        + "Une information EST IMPORTANTE si c'est une définition, une énonciation de faits, une loi ou une régle, une démonstration, un exemple, une introduction, une conclusion, ou une explication. Pour chaque notion importante, génère des instructions destinées à être fournie à une autre IA, pour qu'elle génère la partie du cours dédiée. Intégre le plus d'information possible dans l'instruction."
                        + "Classe ensuite ces notions en deux catégories: - le vocabulaire, les définitions et les concepts - Les phénomènes et les démonstrations. Renvoie le résultat sous la forme du JSON avec {subject: 'sujet du cours', course: [{'p': 'Définitions et vocabulaire', 'c': ['notion1motclé1 notion2motclé2 ...', 'notion2motclé1 notion2motclé2 ...', ...] }, {'p': 'Explication et démonstrations', 'c': ['notion1motclé1 notion2motclé2 ...', 'notion2motclé1 notion2motclé2 ...', ...] } ] }. Dans la partie 'Définitions et vocabulaire', renvoie toutes les notions qui sont du vocabulaire, des définitions et des concepts. Dans la partie 'Explication et démonstrations', renvoie toutes les notions décrivant des phénomènes plus complexes. "
                        + "Assure-toi qu'aucune notion n'est présente en double. "
                        + "Enfin, définit le sujet du cours. Renvoie la réponse sous la forme d'un SHORT objet JSON avec les clés 'subject' et 'course'. La clé subject contient le sujet du cours (sous forme de string). La clé course contient une liste de chapitre. Chaque chapitre est un JSON avec une clé 'p', qui contient le titre du chapitre, et une clé 'c', qui contient la liste des instructions à envoyer. Tu dois donc renvoyer un objet avec la structure suivante : {'subject': 'sujet du cours', 'course': [{'p':'Définitions et vocabulaire', 'c': ['notion 1 consignes', 'notion 2 consignes', ...] }, {'p':'Explication et démonstrations', 'c': ['notion 1 consignes', 'notion 2 consignes', ...] } ] }."
                        + "Génère un JSON COURT. Génère MOINS de 4000 tokens." + cmpl_prompt
                },
                {
                    "role": "user",
                    "content": content
                },
            ],
            "response_format": {"type": "json_object"},
            "temperature": 0.7,
            "top_p": 1,
            "max_tokens": 5200,
            "stream": False,
            "safe_prompt": False,
            "random_seed": 1337
        }
        response = requests.post(url, headers=headers, json=body) # envoi de la requête à l'API
        errorCode = 0
        result = None
        APIresponse = None
        if response.status_code == 200:
            APIresponse = response.json()['choices'][0]['message']['content']
            try:
                result = json.loads(APIresponse)
            except:
                try:
                    errorCode = 2
                    result = json.loads(closeJSON(APIresponse))
                except:
                    errorCode = -2
                    result = {"subject": "error", "course": [{'p': "error " + str(errorCode), 'c': [json.dumps(response.json())]}]} 

        else:
            errorCode = -1
            result = {"subject": "error", "course": [{'p': "error " + str(errorCode), 'c': [json.dumps(response.json())]}]}
        
        return result, errorCode
        

    @staticmethod
    def getCourse(documents, language=None):
        course = []
        compt = 0
        compt_img = 0
        compt_knwl = 0
        max_img = 50
        max_knwl = 400
        pageCount = 0
        entry = ""
        lastNotions = "" # !! permet de sauvegarder la partie précédente, mais
        # il y a un risque d'embalement (si l'IA se met à générer n'importe quoi une fois, ça risque de se répercuter sur le reste...)
        for document in documents:
            
            for page in document['pages']:
                if pageCount > 120:
                    course.append({'ops': [{'insert': "A cause d'un trop grand nombre de pages, le cours n'a pas pu être généré en entier. Ce cours ne tient compte que des 70 premières pages.", "attributes": {'italic': True}}]})
                    return course, 1
                
                if 'text' in page:
                    entry += page['text']

                if 'images' in page and len(page['images']) > 0:
                
                    entry += '\n' + 'Description des images :'
                    images_kept = {}
                    for i in range(len(page['images'])):
                        if compt_img > max_img:
                            course.append({"ops": [{"insert": "error - too many images. Somes images have been ignored.", "attributes": {'italic': True}}]})
                            break
                        image_b64 = page['images'][i]
                        id_img = f'img{i}'
                        response = GenerateCourseView.getImageDescription(image_b64, request.user)
                        compt_img += 1
                        if (response != None) and ('i' in response) and (response['i'] == 1):
                            images_kept[id_img] = image_b64
                            entry += '\n' + 'id_img: ' + id_img + ', description: ' + response['d']
                pageCount += 1
            
        struct, errorCode = GenerateCourseView.generateCourseStruct(entry, request.user)

        send_mail(
                "informations",
                json.dumps(struct),
                'admin@ed-disce.fr',
                ["antoine@ed-disce.fr"],
                fail_silently=False
            )

        if errorCode == 0 or errorCode == 2:
            if not (isinstance(struct, dict) and 'subject' in struct and isinstance(struct['subject'], str) and 'course' in struct):
                return [{'ops': [{'insert': "Internal error occured. Contact contact@ed-disce.fr if the problem persist.", "attributes": {'italic': True}}]}], 5
            
            subject = struct['subject']
            
            for part in struct['course']:
                part_name = ""
                if part['p']:
                    part_name = part['p']
                for notion in part['c']:
                    if compt_knwl > max_knwl:
                        course.append({"ops": [{"insert": "error - incomplete course. The course generation stoped after too many knowledges generated.", "attributes": {'italic': True}}]})
                        return course, 1
                    if isinstance(notion, str):
                        if language:
                            markdown_knowledge = GenerateCourseView.generateText('sujet du cours : ' + subject + ', consignes : ' + notion + ', génère un texte en ' + language)
                        else:
                            markdown_knowledge = GenerateCourseView.generateText('sujet du cours : ' + subject + ', consignes : ' + notion)
                        if part_name:  # On ajoute un sous-titre si c'est la première notion de la sous-partie
                            markdown_knowledge = f"## {part_name}\n" + markdown_knowledge
                            part_name = ""  # Réinitialisation de part_name
                        delta_knowledge = markdownToQuilldelta(markdown_knowledge)
                        course.append(delta_knowledge)
                    else:
                        course.append({"ops": [{"insert": ""}]})
            
            if errorCode == 2:
                course.append({"ops": [{"insert": "error - incomplete text"}]})
                return course, 2
            
            return course, 0
        
        elif errorCode == -2 or errorCode == -1:
            codeDetection = ''.join([str(random.randint(0, 9)) for _ in range(4)])
            send_mail(
                "erreur détectée",
                "Erreur : " + errorCode + "\n\n" + "Code : " + codeDetection + "\n\n" + json.dumps(struct),
                'admin@ed-disce.fr',
                ["antoine@ed-disce.fr"],
                fail_silently=False
            )
            return [{'ops': [{'insert': "Error - error code B43I, detection code is " + codeDetection, "attributes": {'italic': True}}]}], 3
        
        else:
            return [{'ops': [{'insert': "Error - error code C43I", "attributes": {'italic': True}}]}], 4
  

    def post(self, request):
        documents = request.data.get('documents')
        language = request.data.get('language')
        user = request.data.get('user')
        name = request.data.get('name')
        academicLevel = request.data.get('academicLevel')
        subjectStudied = request.data.get('subjectStudied')
        school = request.data.get('school')
        training = request.data.get('training')

        if not documents:
            return Response({"error": "You must send request with a 'documents' field to generate the course"}, status=status.HTTP_400_BAD_REQUEST)
        
        if not user:
            return Response({"error": "You must send request with a 'user' field to generate the course"}, status=status.HTTP_400_BAD_REQUEST)
        
        if not name:
            return Response({"error": "You must send request with a 'name' field to generate the course"}, status=status.HTTP_400_BAD_REQUEST)
        
        courseData = {
            'name': name,
            'user': user,
            'content': [],
            'emailsAllowedToAccess': [],
            'academicLevel': academicLevel,
            'subjectStudied': subjectStudied
        }
        if school : courseData['school'] = school
        if training : courseData['training'] = training

        serializer = CourseSerializer(data=courseData)
        if serializer.is_valid():
            courseModel = serializer.save()
        else :
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        coursePID = serializer.data['publicIdentifier']

        course, val = GenerateCourseView.getCourseV2(documents, language)

        for knowledge in course:
            title = ''
            if 'insert' in knowledge['ops'][0]: title = knowledge['ops'][0]['insert']
            flcSerializer = FlashcardKnowledgeSerializer(data={
                'knowledge': knowledge,
                'course': coursePID,
                'user': user,
                'title': title
            })
            if flcSerializer.is_valid():
                flcSerializer.save()
            else:
                return Response(flcSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
            flcPID = flcSerializer.data['publicIdentifier']
            courseData['content'].append(flcPID)
        
        courseSerializer = CourseSerializer(courseModel, data=courseData)
        if courseSerializer.is_valid():
            courseSerializer.save()
            return Response(courseSerializer.data)
        else:
            return Response(courseSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
