import json
import os
from .base_agent import BaseAgent
from datetime import datetime
from typing import Dict, Any, Tuple, Optional
import logging
import traceback
import re

logger = logging.getLogger("AgentReportGenerator")

class AgentReportGenerator(BaseAgent):
    """
    Agent pour générer un rapport complet à partir des analyses de ticket et d'images.
    
    Cet agent prend en entrée :
    - L'analyse du ticket
    - Les analyses des images pertinentes
    - Les métadonnées associées
    
    Format de données attendu:
    - JSON est le format principal de données en entrée et en sortie
    - Le rapport Markdown est généré à partir du JSON uniquement pour la présentation
    
    Structure des données d'analyse d'images:
    - Deux structures possibles sont supportées:
      1. Liste d'objets: rapport_data["images_analyses"] = [{image_name, analyse}, ...]
      2. Dictionnaire: rapport_data["analyse_images"] = {chemin_image: {sorting: {...}, analysis: {...}}, ...}
    
    Flux de traitement:
    1. Préparation des données d'entrée
    2. Génération du rapport avec le LLM
    3. Sauvegarde au format JSON (format principal)
    4. Conversion et sauvegarde au format Markdown (pour présentation)
    """
    def __init__(self, llm):
        super().__init__("AgentReportGenerator", llm)
        
        # Configuration locale de l'agent (remplace AgentConfig)
        self.temperature = 0.4  # Génération de rapport factuelle mais bien structurée
        self.top_p = 0.9
        self.max_tokens = 2500
        self.system_prompt = """Tu es un expert en génération de rapports techniques pour BRG-Lab pour la société CBAO.
Ta mission est de synthétiser les analyses (ticket et images) en un rapport structuré et exploitable.

EXIGENCE ABSOLUE - GÉNÉRATION DE DONNÉES EN FORMAT JSON:
- Tu DOIS IMPÉRATIVEMENT inclure dans ta réponse un objet JSON structuré pour les échanges client/support
- Le format de chaque échange dans le JSON DOIT être:
  {
    "chronologie_echanges": [
      {
        "date": "date de l'échange",
        "emetteur": "CLIENT ou SUPPORT",
        "type": "Question ou Réponse ou Information technique",
        "contenu": "contenu synthétisé de l'échange"
      },
      ... autres échanges ...
    ]
  }
- La structure doit être EXACTEMENT comme indiquée, avec le nom de clé "chronologie_echanges" obligatoirement
- Chaque message du ticket doit apparaître comme un objet dans la liste
- Indique clairement qui est CLIENT et qui est SUPPORT dans le champ "emetteur"
- Si une question n'a pas de réponse, assure-toi de le noter clairement
- Toute mention de "CBAD" doit être remplacée par "CBAO" qui est le nom correct de la société
- Tu dois synthétiser au mieux les échanges (le plus court et clair possible)

IMPORTANT POUR LE FORMAT:
- Le JSON doit être valide et parsable
- Utilise ```json et ``` pour délimiter le bloc JSON
- Ne modifie pas la structure des clés ("chronologie_echanges", "date", "emetteur", "type", "contenu")
- Assure-toi que les accolades et crochets sont correctement équilibrés

Structure ton rapport:
1. Résumé exécutif: Synthèse du problème initial (nom de la demande + description)
2. Chronologie des échanges: Objet JSON avec la structure imposée ci-dessus (partie CRUCIALE)
3. Analyse des images: Ce que montrent les captures d'écran et leur pertinence
4. Diagnostic technique: Interprétation des informations techniques pertinentes

Reste factuel et précis dans ton analyse.
Les données d'échanges client/support sont l'élément le plus important du rapport.
Tu DOIS inclure le JSON des échanges dans ta réponse exactement au format:
```json
{
  "chronologie_echanges": [
    {"date": "...", "emetteur": "CLIENT", "type": "Question", "contenu": "..."},
    {"date": "...", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "..."}
  ]
}
```"""
        
        # Appliquer la configuration au LLM
        self._appliquer_config_locale()
        
        logger.info("AgentReportGenerator initialisé")
    
    def _appliquer_config_locale(self) -> None:
        """
        Applique la configuration locale au modèle LLM.
        """
        # Appliquer le prompt système
        if hasattr(self.llm, "prompt_system"):
            self.llm.prompt_system = self.system_prompt
        
        # Appliquer les paramètres - mêmes paramètres pour tous les modèles
        if hasattr(self.llm, "configurer"):
            params = {
                "temperature": self.temperature,
                "top_p": self.top_p,
                "max_tokens": self.max_tokens
            }
            
            # Ajout des exemples dans le prompt système pour tous les modèles
            if not "EXEMPLES D'ÉCHANGES" in self.llm.prompt_system:
                exemple_json = """
EXEMPLES D'ÉCHANGES POUR RÉFÉRENCE:

Exemple 1:
```json
{
  "chronologie_echanges": [
    {"date": "2023-01-15", "emetteur": "CLIENT", "type": "Question", "contenu": "Je n'arrive pas à me connecter à l'application"},
    {"date": "2023-01-16", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Avez-vous essayé de réinitialiser votre mot de passe?"}
  ]
}
```

Exemple 2:
```json
{
  "chronologie_echanges": [
    {"date": "2023-02-10", "emetteur": "CLIENT", "type": "Information technique", "contenu": "Version de l'application: 2.3.1"},
    {"date": "2023-02-11", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Cette version contient un bug connu, veuillez mettre à jour"}
  ]
}
```

N'oublie pas que le format EXACT est important. Utilise TOUJOURS la clé "chronologie_echanges" comme clé principale.
"""
                self.llm.prompt_system += exemple_json
                logger.info("Exemples JSON ajoutés au prompt système")
            
            self.llm.configurer(**params)
            logger.info(f"Configuration appliquée au modèle: {str(params)}")
        else:
            logger.warning("Le modèle LLM ne supporte pas la méthode configurer()")
    
    def executer(self, rapport_data: Dict, rapport_dir: str) -> Tuple[Optional[str], Optional[str]]:
        """
        Génère un rapport à partir des analyses effectuées
        
        Args:
            rapport_data: Dictionnaire contenant toutes les données analysées
                     Doit contenir au moins une des clés:
                     - "ticket_analyse" ou "analyse_json": Analyse du ticket
                     - "analyse_images": Analyses des images (facultatif)
            rapport_dir: Répertoire où sauvegarder le rapport
            
        Returns:
            Tuple (chemin vers le rapport JSON, chemin vers le rapport Markdown)
        """
        # Récupérer l'ID du ticket depuis les données
        ticket_id = rapport_data.get("ticket_id", "")
        if not ticket_id and "ticket_data" in rapport_data and isinstance(rapport_data["ticket_data"], dict):
            ticket_id = rapport_data["ticket_data"].get("code", "")
        
        if not ticket_id:
            ticket_id = os.path.basename(os.path.dirname(rapport_dir))
            if not ticket_id.startswith("T"):
                # Dernier recours, utiliser le dernier segment du chemin
                ticket_id = os.path.basename(rapport_dir)
        
        logger.info(f"Génération du rapport pour le ticket: {ticket_id}")
        print(f"AgentReportGenerator: Génération du rapport pour {ticket_id}")
        
        # Validation des données d'entrée
        logger.info("Vérification de la complétude des données d'entrée:")
        if "ticket_data" in rapport_data:
            logger.info(f"  - Données de ticket présentes: {len(str(rapport_data['ticket_data']))} caractères")
        else:
            logger.warning("  - Données de ticket manquantes")
        
        # Vérification des analyses
        ticket_analyse_exists = False
        if "ticket_analyse" in rapport_data and rapport_data["ticket_analyse"]:
            ticket_analyse_exists = True
            logger.info(f"  - Analyse du ticket présente: {len(rapport_data['ticket_analyse'])} caractères")
        elif "analyse_json" in rapport_data and rapport_data["analyse_json"]:
            ticket_analyse_exists = True
            logger.info(f"  - Analyse JSON présente: {len(rapport_data['analyse_json'])} caractères")
        else:
            logger.warning("  - Analyse du ticket manquante")
        
        # Vérification des analyses d'images
        if "analyse_images" in rapport_data and rapport_data["analyse_images"]:
            n_images = len(rapport_data["analyse_images"])
            n_relevant = sum(1 for _, data in rapport_data["analyse_images"].items() 
                           if "sorting" in data and isinstance(data["sorting"], dict) and data["sorting"].get("is_relevant", False))
            n_analyzed = sum(1 for _, data in rapport_data["analyse_images"].items() 
                           if "analysis" in data and data["analysis"])
            
            logger.info(f"  - Analyses d'images présentes: {n_images} images, {n_relevant} pertinentes, {n_analyzed} analysées")
        else:
            logger.warning("  - Analyses d'images manquantes")
        
        # S'assurer que le répertoire existe
        if not os.path.exists(rapport_dir):
            os.makedirs(rapport_dir)
            logger.info(f"Répertoire de rapport créé: {rapport_dir}")
        
        try:
            # Préparer les données formatées pour l'analyse
            ticket_analyse = None
            
            # Vérifier que l'analyse du ticket est disponible sous l'une des clés possibles
            if "ticket_analyse" in rapport_data and rapport_data["ticket_analyse"]:
                ticket_analyse = rapport_data["ticket_analyse"]
                logger.info("Utilisation de ticket_analyse")
            elif "analyse_json" in rapport_data and rapport_data["analyse_json"]:
                ticket_analyse = rapport_data["analyse_json"]
                logger.info("Utilisation de analyse_json en fallback")
            else:
                # Créer une analyse par défaut si aucune n'est disponible
                logger.warning("Aucune analyse de ticket disponible, création d'un message par défaut")
                ticket_data = rapport_data.get("ticket_data", {})
                ticket_name = ticket_data.get("name", "Sans titre")
                ticket_desc = ticket_data.get("description", "Pas de description disponible")
                ticket_analyse = f"Analyse par défaut du ticket:\nNom: {ticket_name}\nDescription: {ticket_desc}\n(Aucune analyse détaillée n'a été fournie par l'agent d'analyse de ticket)"
            
            # Préparer les données d'analyse d'images
            images_analyses = []
            analyse_images_data = rapport_data.get("analyse_images", {})
            
            # Statistiques pour les métadonnées
            total_images = len(analyse_images_data) if analyse_images_data else 0
            images_pertinentes = 0
            
            # Collecter des informations sur les agents et LLM utilisés
            agents_info = self._collecter_info_agents(rapport_data)
            
            # Transformer les analyses d'images en liste structurée pour le prompt
            for image_path, analyse_data in analyse_images_data.items():
                image_name = os.path.basename(image_path)
                
                # Vérifier si l'image est pertinente
                is_relevant = False
                if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
                    is_relevant = analyse_data["sorting"].get("is_relevant", False)
                    if is_relevant:
                        images_pertinentes += 1
                
                # Récupérer l'analyse détaillée si elle existe et que l'image est pertinente
                analyse_detail = None
                if is_relevant:
                    if "analysis" in analyse_data and analyse_data["analysis"]:
                        # Vérifier différentes structures possibles de l'analyse
                        if isinstance(analyse_data["analysis"], dict):
                            if "analyse" in analyse_data["analysis"]:
                                analyse_detail = analyse_data["analysis"]["analyse"]
                            elif "error" in analyse_data["analysis"] and not analyse_data["analysis"].get("error", True):
                                # Si pas d'erreur et que l'analyse est directement dans le dictionnaire
                                analyse_detail = str(analyse_data["analysis"])
                            else:
                                # Essayer de récupérer directement le contenu du dictionnaire
                                analyse_detail = json.dumps(analyse_data["analysis"], ensure_ascii=False, indent=2)
                        elif isinstance(analyse_data["analysis"], str):
                            # Si l'analyse est directement une chaîne
                            analyse_detail = analyse_data["analysis"]
                    
                    # Si l'analyse n'a pas été trouvée mais que l'image est pertinente
                    if not analyse_detail:
                        analyse_detail = f"Image marquée comme pertinente. Raison: {analyse_data['sorting'].get('reason', 'Non spécifiée')}"
                    
                    # Analyse détaillée
                    if analyse_detail:
                        images_analyses.append({
                            "image_name": image_name,
                            "analyse": analyse_detail
                        })
                        logger.info(f"Analyse de l'image {image_name} ajoutée au rapport (longueur: {len(str(analyse_detail))} caractères)")
                    else:
                        logger.warning(f"Analyse non trouvée pour l'image pertinente {image_name}")
                else:
                    logger.info(f"Image {image_name} ignorée car non pertinente")
            
            # Créer le chemin du fichier de rapport JSON (sortie principale)
            json_path = os.path.join(rapport_dir, f"{ticket_id}_rapport_final.json")
            md_path = os.path.join(rapport_dir, f"{ticket_id}_rapport_final.md")
            
            # Formater les données pour le LLM
            prompt = self._formater_prompt_pour_rapport(ticket_analyse, images_analyses, ticket_id)
            
            # Générer le rapport avec le LLM
            logger.info("Génération du rapport avec le LLM")
            print(f"  Génération du rapport avec le LLM...")
            
            # Interroger le LLM
            rapport_genere = self.llm.interroger(prompt)
            logger.info(f"Rapport généré: {len(rapport_genere)} caractères")
            print(f"  Rapport généré: {len(rapport_genere)} caractères")
            
            # Traiter le JSON pour extraire la chronologie des échanges et le convertir en tableau Markdown
            rapport_traite, echanges_json, echanges_markdown = self._extraire_et_traiter_json(rapport_genere)
            if echanges_json and echanges_markdown:
                logger.info(f"Échanges JSON convertis en tableau Markdown: {len(str(echanges_markdown))} caractères")
                print(f"  Échanges client/support convertis du JSON vers le format Markdown")
                # Utiliser le rapport traité avec le tableau Markdown à la place du JSON
                rapport_genere = rapport_traite
            else:
                logger.warning("Aucun JSON d'échanges trouvé dans le rapport, conservation du format original")
            
            # Tracer l'historique avec le prompt pour la transparence
            self.ajouter_historique("generation_rapport", 
                                  {
                                      "ticket_id": ticket_id,
                                      "prompt_taille": len(prompt),
                                      "timestamp": self._get_timestamp()
                                  }, 
                                  rapport_genere)
            
            # Préparer les métadonnées complètes pour le rapport
            timestamp = self._get_timestamp()
            
            # Préparer le JSON complet du rapport (format principal)
            rapport_data_complet = {
                "ticket_id": ticket_id,
                "timestamp": timestamp,
                "rapport_genere": rapport_genere,
                "ticket_analyse": ticket_analyse,
                "images_analyses": images_analyses,
                "statistiques": {
                    "total_images": total_images,
                    "images_pertinentes": images_pertinentes,
                    "analyses_generees": len(images_analyses)
                }
            }
            
            # Ajouter les métadonnées pour la traçabilité
            metadata = {
                "timestamp": timestamp,
                "model": getattr(self.llm, "modele", str(type(self.llm))),
                "temperature": self.temperature,
                "top_p": self.top_p,
                "max_tokens": self.max_tokens,
                "agents": agents_info
            }
            
            rapport_data_complet["metadata"] = metadata
            
            # S'assurer que les clés nécessaires pour le markdown sont présentes
            if "ticket_analyse" not in rapport_data_complet:
                rapport_data_complet["ticket_analyse"] = ticket_analyse
            
            # ÉTAPE 1: Sauvegarder le rapport au format JSON (FORMAT PRINCIPAL)
            with open(json_path, "w", encoding="utf-8") as f:
                json.dump(rapport_data_complet, f, ensure_ascii=False, indent=2)
            
            logger.info(f"Rapport JSON (format principal) sauvegardé: {json_path}")
            print(f"  Rapport JSON sauvegardé: {json_path}")
                
            # ÉTAPE 2: Générer et sauvegarder le rapport au format Markdown (pour présentation)
            markdown_content = self._generer_markdown_depuis_json(rapport_data_complet)
            
            with open(md_path, "w", encoding="utf-8") as f:
                f.write(markdown_content)
                
            logger.info(f"Rapport Markdown (pour présentation) sauvegardé: {md_path}")
            print(f"  Rapport Markdown sauvegardé: {md_path}")
            
            logger.info(f"Taille du rapport Markdown: {len(markdown_content)} caractères")
            
            # Retourner les chemins des deux fichiers (JSON en premier, Markdown en second)
            return json_path, md_path
            
        except Exception as e:
            error_message = f"Erreur lors de la génération du rapport: {str(e)}"
            logger.error(error_message)
            print(f"  ERREUR: {error_message}")
            return None, None
    
    def _collecter_info_agents(self, rapport_data: Dict) -> Dict:
        """
        Collecte des informations sur les agents utilisés dans l'analyse
        
        Args:
            rapport_data: Données du rapport
            
        Returns:
            Dictionnaire contenant les informations sur les agents
        """
        agents_info = {}
        
        # Informations sur l'agent JSON Analyser
        if "analyse_json" in rapport_data:
            json_analysis = rapport_data["analyse_json"]
            # Vérifier si l'analyse JSON contient des métadonnées
            if isinstance(json_analysis, dict) and "metadata" in json_analysis:
                agents_info["json_analyser"] = json_analysis["metadata"]
        
        # Informations sur les agents d'image
        if "analyse_images" in rapport_data and rapport_data["analyse_images"]:
            # Image Sorter
            sorter_info = {}
            analyser_info = {}
            
            for img_path, img_data in rapport_data["analyse_images"].items():
                # Collecter info du sorter
                if "sorting" in img_data and isinstance(img_data["sorting"], dict) and "metadata" in img_data["sorting"]:
                    if "model_info" in img_data["sorting"]["metadata"]:
                        sorter_info = img_data["sorting"]["metadata"]["model_info"]
                
                # Collecter info de l'analyser
                if "analysis" in img_data and img_data["analysis"] and isinstance(img_data["analysis"], dict) and "metadata" in img_data["analysis"]:
                    if "model_info" in img_data["analysis"]["metadata"]:
                        analyser_info = img_data["analysis"]["metadata"]["model_info"]
                
                # Une fois qu'on a trouvé les deux, on peut sortir
                if sorter_info and analyser_info:
                    break
            
            if sorter_info:
                agents_info["image_sorter"] = sorter_info
            if analyser_info:
                agents_info["image_analyser"] = analyser_info
        
        # Ajouter les informations de l'agent report generator
        agents_info["report_generator"] = {
            "model": getattr(self.llm, "modele", str(type(self.llm))),
            "temperature": self.temperature,
            "top_p": self.top_p,
            "max_tokens": self.max_tokens
        }
        
        return agents_info
    
    def _generer_markdown_depuis_json(self, rapport_data: Dict) -> str:
        """
        Génère un rapport Markdown directement à partir des données JSON
        
        Args:
            rapport_data: Données JSON complètes du rapport
                Format attendu:
                - ticket_id: ID du ticket
                - metadata: Métadonnées (timestamp, modèle, etc.)
                - rapport_genere: Texte du rapport généré par le LLM
                - ticket_analyse: Analyse du ticket
                - images_analyses: Liste des analyses d'images (format privilégié)
                OU
                - analyse_images: Dictionnaire des analyses d'images (format alternatif)
            
        Returns:
            Contenu Markdown du rapport
        """
        ticket_id = rapport_data.get("ticket_id", "")
        timestamp = rapport_data.get("metadata", {}).get("timestamp", self._get_timestamp())
        
        logger.info(f"Génération du rapport Markdown pour le ticket {ticket_id}")
        
        # Contenu de base du rapport (partie générée par le LLM)
        rapport_contenu = rapport_data.get("rapport_genere", "")
        
        # Entête du document
        markdown = f"# Rapport d'analyse du ticket #{ticket_id}\n\n"
        markdown += f"*Généré le: {timestamp}*\n\n"
        
        # Ajouter le rapport principal généré par le LLM
        markdown += rapport_contenu + "\n\n"
        
        # Section séparatrice pour les détails d'analyse
        markdown += "---\n\n"
        markdown += "# Détails des analyses effectuées\n\n"
        
        # Ajouter un résumé du processus d'analyse complet
        markdown += "## Processus d'analyse\n\n"
        
        # 1. Analyse de ticket
        ticket_analyse = rapport_data.get("ticket_analyse", "")
        if not ticket_analyse and "analyse_json" in rapport_data:
            ticket_analyse = rapport_data.get("analyse_json", "")
        
        if ticket_analyse:
            markdown += "### Étape 1: Analyse du ticket\n\n"
            markdown += "L'agent d'analyse de ticket a extrait les informations suivantes du ticket d'origine:\n\n"
            markdown += "<details>\n<summary>Cliquez pour voir l'analyse complète du ticket</summary>\n\n"
            markdown += "```\n" + ticket_analyse + "\n```\n\n"
            markdown += "</details>\n\n"
            logger.info(f"Analyse du ticket ajoutée au rapport Markdown ({len(str(ticket_analyse))} caractères)")
        else:
            logger.warning("Aucune analyse de ticket disponible pour le rapport Markdown")
        
        # 2. Tri des images
        markdown += "### Étape 2: Tri des images\n\n"
        markdown += "L'agent de tri d'images a évalué chaque image pour déterminer sa pertinence par rapport au problème client:\n\n"
        
        # Vérifier quelle structure de données est disponible pour les images
        has_analyse_images = "analyse_images" in rapport_data and rapport_data["analyse_images"]
        has_images_analyses = "images_analyses" in rapport_data and isinstance(rapport_data["images_analyses"], list) and rapport_data["images_analyses"]
        
        logger.info(f"Structure des données d'images: analyse_images={has_analyse_images}, images_analyses={has_images_analyses}")
        
        analyse_images_data = {}
        if has_analyse_images:
            analyse_images_data = rapport_data["analyse_images"]
        
        if analyse_images_data:
            # Créer un tableau pour le tri des images
            markdown += "| Image | Pertinence | Raison |\n"
            markdown += "|-------|------------|--------|\n"
            
            for image_path, analyse_data in analyse_images_data.items():
                image_name = os.path.basename(image_path)
                
                # Information de tri
                is_relevant = "Non"
                reason = "Non spécifiée"
                
                if "sorting" in analyse_data:
                    sorting_data = analyse_data["sorting"]
                    if isinstance(sorting_data, dict):
                        is_relevant = "Oui" if sorting_data.get("is_relevant", False) else "Non"
                        reason = sorting_data.get("reason", "Non spécifiée")
                
                markdown += f"| {image_name} | {is_relevant} | {reason} |\n"
            
            markdown += "\n"
            logger.info(f"Tableau de tri des images ajouté au rapport Markdown ({len(analyse_images_data)} images)")
        elif has_images_analyses and rapport_data["images_analyses"]:
            # Si nous avons les analyses d'images mais pas les données de tri, créer un tableau simplifié
            markdown += "| Image | Pertinence |\n"
            markdown += "|-------|------------|\n"
            
            for img_data in rapport_data["images_analyses"]:
                image_name = img_data.get("image_name", "Image inconnue")
                markdown += f"| {image_name} | Oui |\n"
            
            markdown += "\n"
            logger.info(f"Tableau de tri simplifié ajouté au rapport Markdown ({len(rapport_data['images_analyses'])} images)")
        else:
            markdown += "*Aucune image n'a été trouvée ou analysée.*\n\n"
            logger.warning("Aucune analyse d'images disponible pour le tableau de tri")
        
        # 3. Analyse des images pertinentes
        markdown += "### Étape 3: Analyse détaillée des images pertinentes\n\n"
        
        # Traiter directement les images_analyses du rapport_data si disponible
        images_pertinentes = 0
        
        # D'abord essayer d'utiliser la liste images_analyses qui est déjà traitée
        if has_images_analyses:
            images_list = rapport_data["images_analyses"]
            for i, img_data in enumerate(images_list, 1):
                images_pertinentes += 1
                image_name = img_data.get("image_name", f"Image {i}")
                analyse_detail = img_data.get("analyse", "Analyse non disponible")
                
                markdown += f"#### Image pertinente {images_pertinentes}: {image_name}\n\n"
                markdown += "<details>\n<summary>Cliquez pour voir l'analyse complète de l'image</summary>\n\n"
                markdown += "```\n" + analyse_detail + "\n```\n\n"
                markdown += "</details>\n\n"
                
                logger.info(f"Analyse de l'image {image_name} ajoutée au rapport Markdown (from images_analyses)")
        # Sinon, traiter les données brutes d'analyse_images
        elif has_analyse_images:
            analyse_images_data = rapport_data["analyse_images"]
            for image_path, analyse_data in analyse_images_data.items():
                # Vérifier si l'image est pertinente
                is_relevant = False
                if "sorting" in analyse_data and isinstance(analyse_data["sorting"], dict):
                    is_relevant = analyse_data["sorting"].get("is_relevant", False)
                
                if is_relevant:
                    images_pertinentes += 1
                    image_name = os.path.basename(image_path)
                    
                    # Récupérer l'analyse détaillée avec gestion des différents formats possibles
                    analyse_detail = "Analyse non disponible"
                    if "analysis" in analyse_data and analyse_data["analysis"]:
                        if isinstance(analyse_data["analysis"], dict):
                            if "analyse" in analyse_data["analysis"]:
                                analyse_detail = analyse_data["analysis"]["analyse"]
                                logger.info(f"Analyse de l'image {image_name} récupérée via analyse_data['analysis']['analyse']")
                            elif "error" in analyse_data["analysis"] and not analyse_data["analysis"].get("error", True):
                                analyse_detail = str(analyse_data["analysis"])
                                logger.info(f"Analyse de l'image {image_name} récupérée via str(analyse_data['analysis'])")
                            else:
                                analyse_detail = json.dumps(analyse_data["analysis"], ensure_ascii=False, indent=2)
                                logger.info(f"Analyse de l'image {image_name} récupérée via json.dumps")
                        elif isinstance(analyse_data["analysis"], str):
                            analyse_detail = analyse_data["analysis"]
                            logger.info(f"Analyse de l'image {image_name} récupérée directement (string)")
                    else:
                        logger.warning(f"Aucune analyse disponible pour l'image pertinente {image_name}")
                    
                    markdown += f"#### Image pertinente {images_pertinentes}: {image_name}\n\n"
                    markdown += "<details>\n<summary>Cliquez pour voir l'analyse complète de l'image</summary>\n\n"
                    markdown += "```\n" + analyse_detail + "\n```\n\n"
                    markdown += "</details>\n\n"
        
        if images_pertinentes == 0:
            markdown += "*Aucune image pertinente n'a été identifiée pour ce ticket.*\n\n"
            logger.warning("Aucune image pertinente identifiée pour l'analyse détaillée")
        else:
            logger.info(f"{images_pertinentes} images pertinentes ajoutées au rapport Markdown")
        
        # 4. Synthèse (rapport final)
        markdown += "### Étape 4: Génération du rapport de synthèse\n\n"
        markdown += "L'agent de génération de rapport a synthétisé toutes les analyses précédentes pour produire le rapport ci-dessus.\n\n"
        
        # Informations techniques
        markdown += "## Informations techniques\n\n"
        
        # Statistiques d'analyse
        markdown += "### Statistiques\n\n"
        
        total_images = 0
        if has_analyse_images:
            total_images = len(analyse_images_data)
        elif "statistiques" in rapport_data and "total_images" in rapport_data["statistiques"]:
            total_images = rapport_data["statistiques"]["total_images"]
        
        markdown += f"- **Images analysées**: {total_images}\n"
        markdown += f"- **Images pertinentes**: {images_pertinentes}\n\n"
        
        logger.info(f"Rapport Markdown généré ({len(markdown)} caractères)")
        return markdown
    
    def _formater_prompt_pour_rapport(self, ticket_analyse, images_analyses, ticket_id):
        """
        Formate le prompt pour la génération du rapport
        
        Args:
            ticket_analyse: Analyse du ticket
            images_analyses: Liste des analyses d'images, format [{image_name, analyse}, ...]
            ticket_id: ID du ticket
            
        Returns:
            Prompt formaté pour le LLM
        """
        num_images = len(images_analyses)
        logger.info(f"Formatage du prompt avec {num_images} analyses d'images")
        
        # Inclure une vérification des données reçues
        prompt = f"""Génère un rapport technique complet pour le ticket #{ticket_id}, en te basant sur les analyses suivantes.

## VÉRIFICATION DES DONNÉES REÇUES
Je vais d'abord vérifier que j'ai bien reçu les données d'analyses:
- Analyse du ticket : {"PRÉSENTE" if ticket_analyse else "MANQUANTE"}
- Analyses d'images : {"PRÉSENTES (" + str(num_images) + " images)" if num_images > 0 else "MANQUANTES"}

## ANALYSE DU TICKET
{ticket_analyse}

## ANALYSES DES IMAGES ({num_images} images analysées)
"""
        
        # Ajouter l'analyse de chaque image
        for i, img_analyse in enumerate(images_analyses, 1):
            image_name = img_analyse.get("image_name", f"Image {i}")
            analyse = img_analyse.get("analyse", "Analyse non disponible")
            prompt += f"\n### IMAGE {i}: {image_name}\n{analyse}\n"
            logger.info(f"Ajout de l'analyse de l'image {image_name} au prompt ({len(str(analyse))} caractères)")
        
        # Instructions claires pour tous les modèles
        prompt += f"""
## INSTRUCTIONS POUR LA GÉNÉRATION DU RAPPORT

1. Résume d'abord le problème principal du ticket en quelques phrases.

2. GÉNÉRER OBLIGATOIREMENT LE JSON DES ÉCHANGES CLIENT/SUPPORT:
   - Les données d'échanges sont l'élément le plus important du rapport
   - Utilise EXACTEMENT la structure suivante, sans la modifier:
   ```json
   {{
     "chronologie_echanges": [
       {{"date": "date1", "emetteur": "CLIENT", "type": "Question", "contenu": "contenu de la question"}},
       {{"date": "date2", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "contenu de la réponse"}}
     ]
   }}
   ```
   - La clé principale DOIT être "chronologie_echanges"
   - N'ajoute pas de commentaires ou de texte dans le JSON
   - Assure-toi que le JSON est valide et correspond EXACTEMENT au format demandé
   - Entoure le JSON avec ```json et ``` pour faciliter l'extraction

3. Après le JSON, analyse les images pertinentes et leur contribution à la compréhension du problème.

4. Termine par une analyse technique des causes probables du problème.

IMPORTANT: Le JSON des échanges client/support est OBLIGATOIRE et doit être parfaitement formaté.
"""

        # Ajouter l'exemple non formaté (sans f-string) pour éviter les erreurs de formatage
        prompt += """
EXEMPLE EXACT DU FORMAT JSON ATTENDU:
```json
{
  "chronologie_echanges": [
    {"date": "2023-05-10", "emetteur": "CLIENT", "type": "Question", "contenu": "L'application affiche une erreur lors de la connexion"},
    {"date": "2023-05-11", "emetteur": "SUPPORT", "type": "Réponse", "contenu": "Pouvez-vous préciser le message d'erreur?"},
    {"date": "2023-05-12", "emetteur": "CLIENT", "type": "Information technique", "contenu": "Message: Erreur de connexion au serveur"}
  ]
}
```
"""
        
        logger.info(f"Prompt formaté: {len(prompt)} caractères au total")
        return prompt
    
    def _extraire_et_traiter_json(self, texte_rapport):
        """
        Extrait l'objet JSON des échanges du texte du rapport et le convertit en Markdown
        
        Args:
            texte_rapport: Texte complet du rapport généré par le LLM
            
        Returns:
            Tuple (rapport_traité, echanges_json, echanges_markdown)
        """
        # Remplacer CBAD par CBAO dans tout le rapport
        texte_rapport = texte_rapport.replace("CBAD", "CBAO")
        
        # Patterns de recherche plus variés pour s'adapter aux différents modèles
        patterns = [
            r'```json\s*({.*?})\s*```',  # Pattern standard avec backticks triples
            r'```\s*({.*?"chronologie_echanges".*?})\s*```',  # Pattern sans spécifier json mais avec le contenu attendu
            r'{[\s\n]*"chronologie_echanges"[\s\n]*:[\s\n]*\[.*?\][\s\n]*}',  # Pattern sans backticks
            r'<json>(.*?)</json>'  # Pattern alternatif avec balises xml
        ]
        
        # Essayer chaque pattern
        json_text = None
        for pattern in patterns:
            json_match = re.search(pattern, texte_rapport, re.DOTALL)
            if json_match:
                json_text = json_match.group(1).strip()
                logger.info(f"JSON trouvé avec le pattern: {pattern[:20]}...")
                break
        
        # Si aucun pattern n'a fonctionné, tenter une approche plus agressive pour extraire le JSON
        if not json_text:
            # Chercher des indices de début de JSON dans le texte
            potential_starts = [
                texte_rapport.find('{"chronologie_echanges"'),
                texte_rapport.find('{\n  "chronologie_echanges"'),
                texte_rapport.find('{ "chronologie_echanges"')
            ]
            
            # Filtrer les indices valides (non -1)
            valid_starts = [idx for idx in potential_starts if idx != -1]
            
            if valid_starts:
                # Prendre l'indice le plus petit (premier dans le texte)
                start_idx = min(valid_starts)
                # Chercher la fin du JSON (accolade fermante suivie d'une nouvelle ligne ou de la fin du texte)
                json_extract = texte_rapport[start_idx:]
                # Compter les accolades pour trouver la fermeture du JSON
                open_braces = 0
                close_idx = -1
                
                for i, char in enumerate(json_extract):
                    if char == '{':
                        open_braces += 1
                    elif char == '}':
                        open_braces -= 1
                        if open_braces == 0:
                            close_idx = i
                            break
                
                if close_idx != -1:
                    json_text = json_extract[:close_idx + 1]
                    logger.info(f"JSON extrait par analyse d'accolades: {len(json_text)} caractères")
        
        if not json_text:
            logger.warning("Aucun JSON trouvé dans le rapport")
            return texte_rapport, None, None
        
        # Nettoyage supplémentaire du JSON
        # Enlever caractères non imprimables ou indésirables qui pourraient être ajoutés par certains modèles
        json_text = re.sub(r'[\x00-\x1F\x7F]', '', json_text)
        
        try:
            # Vérifier que le texte commence par { et se termine par }
            if not (json_text.startswith('{') and json_text.endswith('}')):
                logger.warning(f"Format JSON incorrect, tentative de correction. Texte: {json_text[:50]}...")
                # Chercher les délimiteurs du JSON
                start = json_text.find('{')
                end = json_text.rfind('}')
                if start != -1 and end != -1 and start < end:
                    json_text = json_text[start:end+1]
            
            echanges_json = json.loads(json_text)
            logger.info(f"JSON extrait avec succès: {len(json_text)} caractères")
            
            # Vérifier si le JSON a la structure attendue
            if not isinstance(echanges_json, dict) or "chronologie_echanges" not in echanges_json:
                # Tenter de corriger la structure si possible
                if len(echanges_json) > 0 and isinstance(list(echanges_json.values())[0], list):
                    # Prendre la première liste comme chronologie
                    key = list(echanges_json.keys())[0]
                    echanges_json = {"chronologie_echanges": echanges_json[key]}
                    logger.info(f"Structure JSON corrigée en utilisant la clé: {key}")
                else:
                    logger.warning("Structure JSON incorrecte et non réparable")
                    return texte_rapport, None, None
            
            # Convertir en tableau Markdown
            echanges_markdown = "| Date | Émetteur | Type | Contenu | Statut |\n"
            echanges_markdown += "|------|---------|------|---------|--------|\n"
            
            if "chronologie_echanges" in echanges_json and isinstance(echanges_json["chronologie_echanges"], list):
                # Pré-traitement pour vérifier les questions sans réponse
                questions_sans_reponse = {}
                for i, echange in enumerate(echanges_json["chronologie_echanges"]):
                    if echange.get("type", "").lower() == "question" and echange.get("emetteur", "").lower() == "client":
                        has_response = False
                        # Vérifier si la question a une réponse
                        for j in range(i+1, len(echanges_json["chronologie_echanges"])):
                            next_echange = echanges_json["chronologie_echanges"][j]
                            if next_echange.get("type", "").lower() == "réponse" and next_echange.get("emetteur", "").lower() == "support":
                                has_response = True
                                break
                        questions_sans_reponse[i] = not has_response
                
                # Générer le tableau
                for i, echange in enumerate(echanges_json["chronologie_echanges"]):
                    date = echange.get("date", "-")
                    emetteur = echange.get("emetteur", "-")
                    type_msg = echange.get("type", "-")
                    contenu = echange.get("contenu", "-")
                    
                    # Ajouter un statut pour les questions sans réponse
                    statut = ""
                    if emetteur.lower() == "client" and type_msg.lower() == "question" and questions_sans_reponse.get(i, False):
                        statut = "**Sans réponse**"
                    
                    echanges_markdown += f"| {date} | {emetteur} | {type_msg} | {contenu} | {statut} |\n"
                
                # Ajouter une note si aucune réponse du support n'a été trouvée
                if not any(echange.get("emetteur", "").lower() == "support" for echange in echanges_json["chronologie_echanges"]):
                    echanges_markdown += "\n**Note: Aucune réponse du support n'a été trouvée dans ce ticket.**\n\n"
            
            # Remplacer le JSON dans le texte par le tableau Markdown
            # Si le JSON était entouré de backticks, remplacer tout le bloc
            if json_match:
                rapport_traite = texte_rapport.replace(json_match.group(0), echanges_markdown)
            else:
                # Sinon, remplacer juste le texte JSON
                rapport_traite = texte_rapport.replace(json_text, echanges_markdown)
            
            return rapport_traite, echanges_json, echanges_markdown
            
        except json.JSONDecodeError as e:
            logger.error(f"Erreur lors du décodage JSON: {e}")
            logger.debug(f"Contenu JSON problématique: {json_text[:100]}...")
            return texte_rapport, None, None
    
    def _get_timestamp(self) -> str:
        """Retourne un timestamp au format YYYYMMDD_HHMMSS"""
        return datetime.now().strftime("%Y%m%d_%H%M%S")