import os
import json
import threading
from django.shortcuts import render
from django.http import JsonResponse
from groq import Groq
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
from .forms import SearchForm
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login, authenticate
import http.client
import requests
import openai
import re
import queue
import logging
import numpy as np
import nltk
import pdfplumber
from docx import Document
from nltk.corpus import stopwords
from nltk.tokenize import sent_tokenize
from nltk.stem import PorterStemmer
from transformers import T5Tokenizer, T5ForConditionalGeneration
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans

# Create your views here.
#Formulaire d'inscription
def signup(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect('home/index.html')  # Redirige vers la page d'accueil après inscription
    else:
        form = UserCreationForm()
    return render(request, 'home/signup.html', {'form': form})
    
#Page simple

def home(request):
    return render(request, 'home/index.html')

def about(request):
    return render(request, 'home/about.html')

def services(request):
    return render(request, 'home/services.html')

def bio(request):
    return render(request, 'home/bio.html')

def contact(request):
    return render(request, 'home/contact.html')
    
def form1(request):
    return render(request, 'home/form1.html')

def form2(request):
    return render(request, 'home/form2.html')

def error_view(request):
    return render(request, 'home/error.html')
    
def search(request):
    return render(request, 'home/search_form.html')
    
#Les fonctions IA
# open ai
def api_call_view(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        prompt = data.get('prompt')
        response = openai.Completion.create(
            engine="text-davinci-002",
            prompt=prompt,
            max_tokens=150,
            n=1,
            stop=None,
            temperature=0.5
        )
        return JsonResponse({'response': response.choices[0].text.strip()})
    else:
        return JsonResponse({'error': 'Method not allowed'}, status=405)
        
        
#Groq API
def groq_streaming(request):
    # Vérification de la méthode de requête
    if request.method != "POST":
        return JsonResponse({"error": "Méthode non autorisée"}, status=405)

    try:
        # Décodage et chargement du corps de la requête
        data = json.loads(request.body.decode('utf-8'))
        session_messages = data.get('messages', [])

        # Configuration du client API
        api_key = os.getenv('GROQ_API_KEY')
        client = Groq(api_key=api_key)

        # Préparation des messages pour l'envoi à l'API
        messages_to_send = [
            {"role": "system", "content": "You are a helpful assistant. Tu réponds toujours en français."}
        ] + session_messages

        # Appel de l'API pour obtenir les completions
        stream = client.chat.completions.create(
            messages=messages_to_send,
            model="mixtral-8x7b-32768",
            temperature=0.7,
            max_tokens=10000,
            top_p=1,
            stream=True,
        )

        # Traitement du contenu de la réponse
        response_content = ""
        for chunk in stream:
            if chunk.choices[0].delta.content:
                # Formatage du contenu en HTML avant de l'ajouter à la réponse
                response_content += format_html_content(chunk.choices[0].delta.content)

        # Envoi de la réponse formatée
        return JsonResponse({"data": response_content}, safe=False)

    except json.JSONDecodeError:
        return JsonResponse({"error": "JSON invalide"}, status=400)
    except KeyError:
        return JsonResponse({"error": "Données malformées, clé manquante"}, status=400)
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=500)

def format_html_content(text):
    """
    Convertit le texte brut en HTML formaté.
    Cette fonction gère les titres, les sous-titres, les listes et les emojis.
    """
    import re
    
    # Traitement des titres principaux
    text = re.sub(r'^(.*?):\s*(.*)\n=+$', r'<h1>\2</h1>', text, flags=re.MULTILINE)
    
    # Traitement des sous-titres avec des tirets
    text = re.sub(r'^II*\. (.*)\n-+$', r'<h2>\1</h2>', text, flags=re.MULTILINE)
    
    # Conversion des listes à puces
    text = re.sub(r'^-\s+(.*)', r'<li>\1</li>', text, flags=re.MULTILINE)
    text = re.sub(r'(<li>.*?</li>)', r'<ul>\1</ul>', text, flags=re.DOTALL)
    text = text.replace('</ul><ul>', '')  # Fusion des listes consécutives
    
    # Ajout de support pour les emojis avec leurs descriptions textuelles
    emojis = {
        '\ud83e\udd2f': '<span role="img" aria-label="astonished face">😯</span>',
        '\ud83e\udd14': '<span role="img" aria-label="thinking face">🤔</span>',
        '\ud83e\udd16': '<span role="img" aria-label="robot face">🤖</span>',
        '\ud83d\udcbb': '<span role="img" aria-label="computer">💻</span>',
        '\u26a0\ufe0f': '<span role="img" aria-label="warning">⚠️</span>',
        '\ud83c\udf1f': '<span role="img" aria-label="glowing star">🌟</span>'
    }
    for emoji_code, emoji_html in emojis.items():
        text = text.replace(emoji_code, emoji_html)

    # Convertir les retours à la ligne en balises <br> si nécessaire
    text = re.sub(r'\n{2,}', '<br><br>', text)
    text = re.sub(r'\n', '<br>', text)
    
    return text


#Brave search api
def search_view(request):
    if request.method == 'POST':
        try:
            data = json.loads(request.body.decode('utf-8'))
            query = data.get('query', '')

            token = os.getenv("BRAVE_SEARCH_TOKEN")
            if not token:
                return JsonResponse({"error": "Le token d'authentification Brave Search n'a pas été spécifié."}, status=500)

            url = "https://api.search.brave.com/res/v1/web/search"
            params = {"q": query}
            headers = {"x-subscription-token": token}

            response = requests.get(url, params=params, headers=headers)
            if response.status_code == 200:
                results = response.json()
                return JsonResponse({"results": results})
            else:
                return JsonResponse({"error": f"Erreur lors de la recherche. Code de statut : {response.status_code}"}, status=500)
        except json.JSONDecodeError as e:
            return JsonResponse({"error": "Erreur de décodage JSON"}, status=400)
        except Exception as e:
            return JsonResponse({"error": f"Erreur lors de la recherche : {e}"}, status=500)
    else:
        return JsonResponse({"error": "Méthode non autorisée"}, status=405)

#Réception d'un document tokenisation puis envoi à openai

logger = logging.getLogger(__name__)

class DocumentAPIView(APIView):
    def post(self, request, *args, **kwargs):
        document_file = request.FILES.get('document')
        if not document_file:
            return Response({"error": "No file provided"}, status=status.HTTP_400_BAD_REQUEST)

        # Utilisation d'une queue pour récupérer la sortie du thread
        text_queue = queue.Queue()
        text_thread = threading.Thread(target=lambda q, arg: q.put(self.extract_text(arg)), args=(text_queue, document_file))
        text_thread.start()
        text_thread.join()

        text = text_queue.get()
        if text is None:
            return Response({"error": "Unsupported file type or corrupt file"}, status=status.HTTP_400_BAD_REQUEST)

        # De même pour la requête OpenAI
        openai_queue = queue.Queue()
        openai_thread = threading.Thread(target=lambda q, arg: q.put(self.query_openai(arg)), args=(openai_queue, text))
        openai_thread.start()
        openai_thread.join()

        openai_response = openai_queue.get()
        if openai_response is None:
            return Response({"error": "Failed to get response from OpenAI"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response({"openai_response": openai_response}, status=status.HTTP_200_OK)

    def extract_text_from_pdf(self, document_file):
        text_content = []
        with pdfplumber.open(document_file) as pdf:
            for page in pdf.pages:
                text = page.extract_text()
                if text:
                    text_content.append(text)
        return ' '.join(text_content)

    def extract_text_from_docx(self, document_file):
        doc = Document(document_file)
        return ' '.join([para.text for para in doc.paragraphs if para.text.strip()])

   



    def extract_text(self, document_file):
        filename = document_file.name.lower()
        logger.info(f"Attempting to extract text from file: {filename}")
    
        if not filename.endswith(('.pdf', '.docx', '.txt', '.json')):
            logger.error(f"Unsupported file type: {filename}")
            return None

        try:
            if filename.endswith('.pdf'):
                return self.extract_text_from_pdf(document_file)
            elif filename.endswith('.docx'):
                return self.extract_text_from_docx(document_file)
            elif filename.endswith('.txt'):
                return document_file.read().decode('utf-8')
            elif filename.endswith('.json'):
                return self.extract_text_from_json(document_file)
        except Exception as e:
            logger.error(f"Failed to extract text from {filename}: {str(e)}")
            return None

    def query_openai(self, text):
        api_key = os.getenv('OPENAI_API_KEY')
        if not api_key:
            logger.error("API key for OpenAI is not configured.")
            return None

        response = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {api_key}"},
            json={
                "model": "gpt-3.5-turbo",
                "messages": [
                    {"role": "system", "content": "Given a detailed document related to any topic, provide a analyse in an organized manner. Focus on key sections such as background analysis, current situation, and strategic recommendations. Conclude with a synthetic overview of the document and list important keywords. Please deliver the response in French in 2500 words mini, formatted in HTML."},
                    {"role": "user", "content": text}
                ]
            }
        )
        if response.status_code == 200:
            return response.json()['choices'][0]['message']
        else:
            logger.error(f"OpenAI request failed with status {response.status_code}")
            return None

def make_api_scholar(request):
    url = "google.serper.dev"
    endpoint = "/scholar"

    # Assurez-vous d'obtenir le token CSRF depuis la requête Django
    csrf_token = request.COOKIES.get('csrftoken')

    # Récupérez la valeur de la requête de l'utilisateur
    query = request.POST.get('q', '')  # Assurez-vous que 'q' est le nom du champ dans votre formulaire HTML

    # Construire le payload JSON avec la valeur récupérée de la requête de l'utilisateur
    payload = json.dumps({
        "q": query,
        "location": "France",
        "gl": "fr",
        "hl": "fr"
    })

    headers = {
        'X-API-KEY': 'ccc5b7632da9962c1ddbefb107ac44669ad35223',
        'Content-Type': 'application/json',
        'X-CSRFToken': csrf_token  # Envoyer le token CSRF dans les en-têtes de la requête
    }

    try:
        # Établir une connexion HTTPS
        conn = http.client.HTTPSConnection(url)

        # Envoyer la requête POST avec le payload et les en-têtes
        conn.request("POST", endpoint, payload, headers)

        # Récupérer la réponse
        res = conn.getresponse()

        # Lire les données de la réponse
        data = res.read().decode("utf-8")

        # Fermer la connexion
        conn.close()

        # Retourner la réponse JSON
        return JsonResponse(json.loads(data))
    except Exception as e:
        # Capturer les exceptions et gérer les erreurs
        return JsonResponse({'error': f'Une erreur s\'est produite : {str(e)}'}, status=500)



# class JWTAuthenticationView(APIView):
#    authentication_classes = [JSONWebTokenAuthentication]
#    permission_classes = [IsAuthenticated]
#
#    def get(self, request):
#        # L'utilisateur est authentifié avec succès via JWT
#        return Response({'message': 'Authentification réussie avec JWT.'})
