import json
import requests
from rest_framework.response import Response
from rest_framework import status
from concurrent.futures import ThreadPoolExecutor
from utils.stringifiers import markdownToQuilldelta, closeJSON
from utils.parsing import parse_syntactic, parse_parts
from utils.formate import format_data, create_prompts
from utils.mistral_api import call_mistral_api
import os
import uuid
from datetime import datetime

def getCourseMetadata(sampleText, user):
    print(f"[getCourseMetadataAndSetTags] Début de l'extraction des métadonnées")
    content, error = call_mistral_api(
        user=user,
        content=sampleText,
        agent_id="ag:65468023:20250304:extract-meta:9cc0452c",
        max_tokens=240,
        response_format={"type": "json_object"}
    )
    if error:
        print(f"[getCourseMetadataAndSetTags] Erreur lors de l'appel API: {error}")
        return False

    try:
        data = json.loads(closeJSON(content))
        return data
    except Exception as e:
        print(f"[getCourseMetadataAndSetTags] Erreur lors de la conversion en JSON: {e}")
        return False


    # Ici le but premier est d'identifier l'école
    # Pour les sujets étudiés, on peut se baser sur les titres des knowledges et autres qui seront générés après
    # Pour une recherche, on pourrait corréler ça aux titres des cours

def generateFlashcard1(notion, user):
    print(f"[generateFlashcard] Début de la génération de la flashcard")
    # Génération de la réponse
    answer_content, error = call_mistral_api(
        user=user,
        content=notion,
        agent_id="ag:65468023:20240925:untitled-agent:d9055718",
        max_tokens=1024
    )
    
    if error:
        print(f"[generateFlashcard] Erreur lors de la génération de la réponse: {error}")
        return {"question": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la flashcard."}]}, "answer": {"ops": [{"insert": "Erreur Hb801"}]}}

    print(f"[generateFlashcard] Réponse générée avec succès")
    # Génération de la question basée sur la réponse
    question_content, error = call_mistral_api(
        user=user,
        content=answer_content,
        agent_id="ag:65468023:20241209:questionner:acbd2459",
        max_tokens=1024
    )
    
    if error:
        print(f"[generateFlashcard] Erreur lors de la génération de la question: {error}")
        return {"question": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la flashcard."}]}, "answer": {"ops": [{"insert": "Erreur Hb802"}]}}

    print(f"[generateFlashcard] Question : {question_content}")
    print(f"[generateFlashcard] Réponse : {answer_content}")
    
    print(f"[generateFlashcard] Question générée avec succès")
    try:
        return {
            "question": markdownToQuilldelta(question_content),
            "answer": markdownToQuilldelta(answer_content)
        }
    except Exception as e:
        print(f"[generateFlashcard] Erreur lors de la conversion en JSON: {e}")
        return {"question": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la flashcard."}]}, "answer": {"ops": [{"insert": "Erreur Hb803"}]}}

def generateFlashcard(notion, user):
    print(f"[generateFlashcard] Début de la génération de la flashcard")
    # Génération de la réponse
    content, error = call_mistral_api(
        user=user,
        content=notion,
        agent_id="ag:65468023:20250407:flashcard-generator:03671b80",
        max_tokens=1024,
        response_format={"type": "json_object"}
    )
    
    if error:
        return {"question": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la flashcard."}]}, "answer": {"ops": [{"insert": "Erreur Hb804"}]}}

    print("[generateFlashcard] Début du traitement du contenu généré")
    print(f"[generateFlashcard] Contenu brut reçu: {content}")
    print("[generateFlashcard] Tentative de fermeture du JSON")
    closed_json = closeJSON(content)
    print(f"[generateFlashcard] JSON fermé: {closed_json}")
    
    try:
        print("[generateFlashcard] Tentative de parsing du JSON")
        data = json.loads(closed_json)
        print(f"[generateFlashcard] JSON parsé avec succès: {data}")
        
        print("[generateFlashcard] Vérification des clés 'question' et 'answer'")
        if 'question' in data and 'answer' in data:
            print(f"[generateFlashcard] Question trouvée: {data['question']}")
            print(f"[generateFlashcard] Réponse trouvée: {data['answer']}")
            
            print("[generateFlashcard] Conversion en format Quill")
            question_delta = markdownToQuilldelta(data['question'])
            answer_delta = markdownToQuilldelta(data['answer'])
            print(f"[generateFlashcard] Question convertie: {question_delta}")
            print(f"[generateFlashcard] Réponse convertie: {answer_delta}")
            
            return {
                "question": question_delta,
                "answer": answer_delta
            }
        else:
            print("[generateFlashcard] Erreur: Les clés 'question' et/ou 'answer' sont manquantes")
            return {"question": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la flashcard."}]}, "answer": {"ops": [{"insert": "Erreur Hb805"}]}}
    except Exception as e:
        print(f"[generateFlashcard] Exception lors du traitement: {str(e)}")
        print(f"[generateFlashcard] Type d'exception: {type(e)}")
        return {"question": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la flashcard."}]}, "answer": {"ops": [{"insert": "Erreur Hb806"}]}}


def generateExercise(notion, user):
    # Génération de l'exercice
    exercise_content, error = call_mistral_api(
        user=user,
        content=notion,
        agent_id="ag:65468023:20241209:exercise-designer:6eb4e1e9",
        max_tokens=2048
    )
    
    if error:
        return {"visibleAnswer": {"ops": [{"insert": "Une erreur est survenue lors de la génération de l'exercice."}]}, "instructions": {"ops": [{"insert": "Erreur Hb811"}]}}

    # Génération de la correction
    correction_content, error = call_mistral_api(
        user=user,
        content=exercise_content,
        agent_id="ag:65468023:20250407:corrector:49f29136",
        max_tokens=2048
    )

    if error:
        return {"visibleAnswer": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la correction."}]}, "instructions": {"ops": [{"insert": "Erreur Hb812"}]}}

    try:
        return {
            "visibleAnswer": markdownToQuilldelta(correction_content),
            "instructions": markdownToQuilldelta(exercise_content)
        }
    except Exception as e:
        return {"visibleAnswer": {"ops": [{"insert": "Une erreur est survenue lors de la génération de la correction."}]}, "instructions": {"ops": [{"insert": "Erreur Hb813"}]}}

def generateText(content, user):
    text_content, error = call_mistral_api(
        user=user,
        content=content,
        agent_id="ag:65468023:20240925:untitled-agent:d9055718",
        max_tokens=255
    )
    
    if error:
        return {"ops": [{"insert": "Une erreur est survenue lors de la génération du texte : erreur Hb820"}]}, error
    
    try:
        return markdownToQuilldelta(text_content), 0
    except Exception as e:
        return {"ops": [{"insert": "Une erreur est survenue lors de la génération du texte : erreur Hb821"}]}, error

def extractStructFromPage(text, user, images=[], language=None):
    content, error = call_mistral_api(
        user=user,
        content=text,
        agent_id="ag:65468023:20241212:luminas:6750ec78",
        max_tokens=1023,
        images=images,
        language=language
    )
    
    if error:
        return {'error': 1, 'response': error}
    
    return content

def createCourseArchitecture(notions, user, language=None):
    result, error = call_mistral_api(
        user=user,
        content=notions,
        agent_id="ag:65468023:20250308:re-manager2:9a443cfb",
        max_tokens=9000, 
        language=language,
        response_format={"type": "json_object"}
    )
    
    if error:
        return None, error

    try:
        result_dict = json.loads(result)
        title = result_dict.get('title', '')
        architecture = result_dict.get('course', [])
    except Exception as e:
        return None, {"error": "Erreur lors du parsing JSON"}

    return title, architecture

def getCourseV2(documents, user, language=None):
    # Création du dossier s'il n'existe pas
    output_dir = 'course_generated'
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
        
    # Création d'un fichier unique pour cette génération
    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    file_id = str(uuid.uuid4())[:8]
    output_file = os.path.join(output_dir, f'{timestamp}_{file_id}.json')
    
    # Structure pour stocker toutes les données
    generation_data = {
        'timestamp': timestamp,
        'user_id': str(user.publicIdentifier),
        'blocks_input': [],
        'extracted_structures': [],
        'architecture': None,
        'generated_content': []
    }

    course = []
    compt_img = 0
    compt_knwl = 0
    max_img = 50
    max_knwl = 400
    pageCount = 0
    entry = ""
    unordered_course = {
        'texts': [],
        'flashcards': [],
        'exercises': [],
    }

    def check_page_size(page):
        if len(page['text']) > 10000:
            print(f"[check_page_size] Page trop grande: {len(page['text'])} caractères")
            return False
        for image in page['images']:
            if len(image) > 10**7:
                print(f"[check_page_size] Image trop grande: {len(image)} octets")
                return False
        return True
    
    max_documents = 20
    blocks = []
    if len(documents) > max_documents:
        print(f"[getCourseV2] Erreur: trop de documents ({len(documents)} > {max_documents})")
        course.append({'ops': [{'insert': f"A cause d'un trop grand nombre de documents, le cours n'a pas pu être généré correctement. Merci de fournir moins de {max_documents} documents. Erreur Gc100", "attributes": {'italic': True}}]})
        return None, course, None, -1

    print("[getCourseV2] Formatage des documents")
    for document in documents:
        blocks += format_data(document)
    
    # Sauvegarde des blocks d'entrée
    generation_data['blocks_input'] = blocks
    
    print("[getCourseV2] Début du traitement parallèle des blocs")
    extracted_structures = []
    with ThreadPoolExecutor(max_workers=4) as executor:
        futures = [
            executor.submit(
                extractStructFromPage,
                block['text'],
                user,
                images=block.get('images', []),
                language=language
            ) for block in blocks
        ]
        
        unordered_course = ""
        for future in futures:
            try:
                result = future.result()
                print(f"[getCourseV2] Résultat obtenu: {result}")
                unordered_course += result
                extracted_structures.append(result)
            except Exception as e:
                print(f"[getCourseV2] Erreur dans l'exécution parallèle: {str(e)}")
    
    # Sauvegarde des structures extraites
    generation_data['extracted_structures'] = extracted_structures

    title, architecture = createCourseArchitecture(unordered_course, user, language=language)
    
    # Sauvegarde de l'architecture
    generation_data['architecture'] = {
        'title': title,
        'architecture': architecture
    }
    
    if not title:
        print("[getCourseV2] Erreur: pas de titre généré")
        course.append({'ops': [{'insert': "Une erreur interne est survenue. Ressayez plus tard. Erreur Gc200", "attributes": {'italic': True}}]})
        return None, course, None, -2
    
    knowledges = create_prompts(title, architecture)
            
    print(f"[getCourseV2] Titre du cours: {title}")
    course.append({'type': 'topic', 'data': markdownToQuilldelta(title)})

    def generate_knowledge(knowledge):
        print(f"[generate_knowledge] Génération du contenu de type: {knowledge['type']}")
        if knowledge['type'] == 'text':
            text, error = generateText(knowledge['prompt'], user)
            return {'type': 'text', 'data': {'content': text}}
        elif knowledge['type'] == 'flashcard':
            fls = generateFlashcard(knowledge['prompt'], user)
            return {'type': 'flashcard', 'data': fls}
        elif knowledge['type'] == 'exercise':
            exercise = generateExercise(knowledge['prompt'], user)
            return {'type': 'exercise', 'data': exercise}
        elif knowledge['type'] == 'part':
            return {'type': 'text', 'data': {'content': {'ops': [{"insert": knowledge['prompt']}]}}}

    print("[getCourseV2] Début de la génération parallèle des contenus")
    generated_contents = []
    with ThreadPoolExecutor(max_workers=4) as executor:
        futures = [executor.submit(generate_knowledge, k) for k in knowledges]
        
        for future in futures:
            try:
                print("[getCourseV2] Récupération du résultat d'une future")
                result = future.result()
                print(f"[getCourseV2] Résultat obtenu: {result}")
                course.append(result)
                generated_contents.append(result)
            except Exception as e:
                print(f"[getCourseV2] Erreur dans le traitement parallèle: {e}")
    
    # Sauvegarde des contenus générés
    generation_data['generated_content'] = generated_contents
    
    # Écriture de toutes les données dans le fichier
    try:
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(generation_data, f, ensure_ascii=False, indent=2)
        print(f"[getCourseV2] Données de génération sauvegardées dans {output_file}")
    except Exception as e:
        print(f"[getCourseV2] Erreur lors de la sauvegarde des données: {str(e)}")

    print("[getCourseV2] Vérification des métadonnées")
    metadata = getCourseMetadata(blocks[0]['text'], user)

    print("[getCourseV2] Génération du cours terminée avec succès")
    return title, course, metadata, 0



def create_write_in_document(content):
    output_dir = 'course_generated'
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    file_id = str(uuid.uuid4())[:8]
    safe_title = timestamp + "_" + file_id + ".txt"

    file_path = os.path.join(output_dir, safe_title)
    with open(file_path, 'w', encoding='utf-8') as f:
        json.dump(content, f, ensure_ascii=False, indent=4)

    return file_path
