#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Module pour extraire les images intégrées dans les messages HTML d'Odoo.
Complémentaire à l'extracteur d'images actuel, il détecte spécifiquement les images 
référencées dans le HTML avec des balises <img> et les associe aux pièces jointes.
"""

import os
import re
import logging
import json
from typing import Dict, List, Any, Optional, Set, Tuple
from bs4 import BeautifulSoup

class HtmlImageExtractor:
    """
    Extracteur d'images intégrées dans les messages HTML d'Odoo.
    """
    
    def __init__(self, ticket_dir: str):
        """
        Initialise l'extracteur d'images HTML.
        
        Args:
            ticket_dir: Répertoire du ticket contenant les messages et pièces jointes
        """
        self.ticket_dir = ticket_dir
        self.messages_file = os.path.join(ticket_dir, "messages_raw.json")
        self.attachments_file = os.path.join(ticket_dir, "attachments_info.json")
        self.output_file = os.path.join(ticket_dir, "embedded_images.json")
        
        # Cache pour les données
        self._messages = None
        self._attachments = None
        
    def _load_messages(self) -> List[Dict[str, Any]]:
        """
        Charge les messages du ticket.
        
        Returns:
            Liste des messages
        """
        if self._messages is not None:
            return self._messages
            
        # Essayer différents fichiers potentiels contenant les messages
        possible_message_files = [
            self.messages_file,                        # messages_raw.json
            os.path.join(self.ticket_dir, "messages.json"),
            os.path.join(self.ticket_dir, "all_messages.json")
        ]
        
        for msg_file in possible_message_files:
            if os.path.exists(msg_file):
                try:
                    with open(msg_file, 'r', encoding='utf-8') as f:
                        data = json.load(f)
                        
                        # Gérer différents formats de fichiers de messages
                        if isinstance(data, dict):
                            if "messages" in data:
                                self._messages = data["messages"]
                                return self._messages
                            # Si pas de clé messages mais d'autres clés, essayer de trouver les messages
                            for key, value in data.items():
                                if isinstance(value, list) and len(value) > 0:
                                    # Vérifier si ça ressemble à des messages (ont une clé body ou content)
                                    if isinstance(value[0], dict) and any(k in value[0] for k in ["body", "content"]):
                                        self._messages = value
                                        return self._messages
                        
                        # Si c'est directement une liste, supposer que ce sont des messages
                        elif isinstance(data, list):
                            self._messages = data
                            return self._messages
                            
                except Exception as e:
                    logging.error(f"Erreur lors du chargement du fichier {msg_file}: {e}")
        
        # Si on arrive ici, aucun fichier valide n'a été trouvé
        logging.error(f"Aucun fichier de messages valide trouvé dans: {self.ticket_dir}")
        return []
    
    def _load_attachments(self) -> Dict[int, Dict[str, Any]]:
        """
        Charge les pièces jointes du ticket.
        
        Returns:
            Dictionnaire des pièces jointes indexées par ID
        """
        if self._attachments is not None:
            return self._attachments
        
        # Essayer différents fichiers potentiels de pièces jointes
        possible_attachment_files = [
            self.attachments_file,                      # attachments_info.json
            os.path.join(self.ticket_dir, "attachments.json")
        ]
        
        for att_file in possible_attachment_files:
            if os.path.exists(att_file):
                try:
                    with open(att_file, 'r', encoding='utf-8') as f:
                        attachments = json.load(f)
                        
                        if not isinstance(attachments, list):
                            # Si ce n'est pas une liste mais un dict, essayer de trouver une liste
                            for key, value in attachments.items():
                                if isinstance(value, list) and len(value) > 0:
                                    attachments = value
                                    break
                                    
                        if not isinstance(attachments, list):
                            continue
                            
                        # Indexer par ID pour un accès rapide
                        self._attachments = {}
                        for att in attachments:
                            if "id" in att:
                                self._attachments[att["id"]] = att
                            elif "attachment_id" in att:
                                self._attachments[att["attachment_id"]] = att
                        
                        if self._attachments:
                            return self._attachments
                except Exception as e:
                    logging.error(f"Erreur lors du chargement des pièces jointes depuis {att_file}: {e}")
        
        # Si on arrive ici, aucun fichier valide n'a été trouvé
        logging.error(f"Aucun fichier de pièces jointes valide trouvé dans: {self.ticket_dir}")
        return {}
    
    def _get_tag_attribute(self, tag: Any, attr_name: str, default: str = "") -> str:
        """
        Récupère un attribut d'une balise HTML de manière sécurisée.
        
        Args:
            tag: Balise HTML
            attr_name: Nom de l'attribut à récupérer
            default: Valeur par défaut si l'attribut n'existe pas
            
        Returns:
            Valeur de l'attribut
        """
        # Vérifier que c'est bien une balise et qu'elle a la méthode get
        if not hasattr(tag, 'get'):
            return default
        
        # Récupérer l'attribut si présent
        try:
            value = tag.get(attr_name)
            return str(value) if value is not None else default
        except (AttributeError, KeyError, TypeError):
            return default
    
    def extract_image_references(self) -> Dict[str, Any]:
        """
        Extrait les références aux images dans le HTML des messages.
        
        Returns:
            Dictionnaire contenant les références d'images trouvées
        """
        messages = self._load_messages()
        attachments_by_id = self._load_attachments()
        
        if not messages:
            logging.error("Aucun message trouvé pour l'extraction d'images")
            return {"status": "error", "message": "Aucun message trouvé", "references": []}
        
        # Stocker les références trouvées
        image_references = []
        
        # Ensemble pour dédupliquer
        processed_ids = set()
        
        for message in messages:
            # Déterminer quel champ contient le contenu HTML
            message_body = None
            message_id = None
            
            # Différentes possibilités de noms pour le contenu HTML et l'ID
            for body_key in ["body", "content", "html_content", "message"]:
                if body_key in message and message[body_key]:
                    message_body = message[body_key]
                    break
                    
            for id_key in ["id", "message_id", "uid"]:
                if id_key in message:
                    message_id = message[id_key]
                    break
                    
            if not message_body or not message_id:
                continue
            
            # Méthode 1: Extraction depuis les balises <img> dans le HTML
            try:
                # Analyser le HTML du message
                soup = BeautifulSoup(message_body, "html.parser")
                
                # Trouver toutes les balises <img>
                img_tags = soup.find_all("img")
                
                for img in img_tags:
                    # Utiliser la méthode sécurisée pour récupérer les attributs
                    src = self._get_tag_attribute(img, "src")
                    
                    # Ignorer les images vides ou data URLs
                    if not src or src.startswith("data:"):
                        continue
                    
                    # Différents patterns de référence d'images
                    image_id = None
                    
                    # Pattern 1: /web/image/ID?access_token=...
                    match = re.search(r"/web/image/(\d+)", src)
                    if match:
                        image_id = int(match.group(1))
                    
                    # Pattern 2: /web/content/ID?...
                    if not image_id:
                        match = re.search(r"/web/content/(\d+)", src)
                        if match:
                            image_id = int(match.group(1))
                    
                    # Pattern 3: /web/static/ID?...
                    if not image_id:
                        match = re.search(r"/web/static/(\d+)", src)
                        if match:
                            image_id = int(match.group(1))
                            
                    # Pattern 4: /web/binary/image?id=ID&...
                    if not image_id:
                        match = re.search(r"[?&]id=(\d+)", src)
                        if match:
                            image_id = int(match.group(1))
                    
                    if image_id:
                        self._ajouter_reference_image(image_id, message_id, attachments_by_id, img, processed_ids, image_references)
                    
            except Exception as e:
                logging.error(f"Erreur lors de l'analyse des balises <img> dans le message {message_id}: {e}")
            
            # Méthode 2: Extraction depuis les références textuelles à la fin du message
            # Format: "- Nom_image.ext (image/type) [ID: 12345]"
            try:
                # Chercher des références d'images dans le texte du message
                # Pattern des références: "- Nom_fichier.ext (type/mime) [ID: 12345]"
                img_ref_pattern = r"- ([^\(\)]+) \(([^\(\)]*)\) \[ID: (\d+)\]"
                for match in re.finditer(img_ref_pattern, message_body):
                    try:
                        nom_fichier = match.group(1).strip()
                        mimetype = match.group(2).strip()
                        image_id = int(match.group(3))
                        
                        if image_id in processed_ids:
                            continue
                            
                        processed_ids.add(image_id)
                        
                        # Vérifier si cette image est dans nos pièces jointes
                        if image_id in attachments_by_id:
                            attachment = attachments_by_id[image_id]
                            
                            # Récupérer le chemin local de l'image
                            local_path = attachment.get("local_path")
                            
                            # Vérifier que le fichier existe réellement
                            if local_path and not os.path.exists(local_path):
                                # Essayer de chercher dans d'autres endroits potentiels du répertoire
                                for root, dirs, files in os.walk(self.ticket_dir):
                                    for file in files:
                                        if os.path.basename(local_path) == file:
                                            local_path = os.path.join(root, file)
                                            break
                                    if os.path.exists(local_path):
                                        break
                            
                            image_ref = {
                                "image_id": image_id,
                                "message_id": message_id,
                                "attachment_id": attachment.get("id", attachment.get("attachment_id", 0)),
                                "name": nom_fichier,  # Utiliser le nom trouvé dans le texte
                                "mimetype": mimetype,  # Utiliser le type MIME trouvé dans le texte
                                "file_size": attachment.get("file_size", 0),
                                "local_path": local_path,
                                "source": "text_reference"
                            }
                            
                            image_references.append(image_ref)
                            logging.info(f"Image référencée trouvée dans le texte: ID {image_id} dans message {message_id}")
                    except Exception as e:
                        logging.error(f"Erreur lors du traitement de la référence d'image textuelle: {e}")
                
            except Exception as e:
                logging.error(f"Erreur lors de l'analyse des références textuelles dans le message {message_id}: {e}")
        
        # Sauvegarder les références trouvées
        result = {
            "status": "success" if image_references else "warning",
            "message": f"Trouvé {len(image_references)} références d'images intégrées",
            "references": image_references
        }
        
        try:
            with open(self.output_file, 'w', encoding='utf-8') as f:
                json.dump(result, f, indent=2, ensure_ascii=False)
            logging.info(f"Références d'images sauvegardées dans: {self.output_file}")
        except Exception as e:
            logging.error(f"Erreur lors de la sauvegarde des références d'images: {e}")
        
        return result
        
    def _ajouter_reference_image(self, image_id: int, message_id: str, attachments_by_id: Dict[int, Dict[str, Any]], 
                                img: Any, processed_ids: Set[int], image_references: List[Dict[str, Any]]) -> None:
        """
        Ajoute une référence d'image à la liste des références
        
        Args:
            image_id: ID de l'image
            message_id: ID du message
            attachments_by_id: Dictionnaire des pièces jointes indexées par ID
            img: Balise HTML de l'image
            processed_ids: Ensemble des IDs déjà traités
            image_references: Liste des références d'images à enrichir
        """
        # Éviter les duplications
        if image_id in processed_ids:
            return
            
        processed_ids.add(image_id)
        
        # Vérifier si cette image est dans nos pièces jointes
        if image_id in attachments_by_id:
            attachment = attachments_by_id[image_id]
            
            # Récupérer les attributs de manière sécurisée
            width = self._get_tag_attribute(img, "width")
            height = self._get_tag_attribute(img, "height")
            alt = self._get_tag_attribute(img, "alt")
            
            # Récupérer le chemin local de l'image
            local_path = attachment.get("local_path")
            
            # Vérifier que le fichier existe réellement
            if local_path and not os.path.exists(local_path):
                # Essayer de chercher dans d'autres endroits potentiels du répertoire
                for root, dirs, files in os.walk(self.ticket_dir):
                    for file in files:
                        if os.path.basename(local_path) == file:
                            local_path = os.path.join(root, file)
                            break
                    if os.path.exists(local_path):
                        break
            
            image_ref = {
                "image_id": image_id,
                "message_id": message_id,
                "attachment_id": attachment.get("id", attachment.get("attachment_id", 0)),
                "name": attachment.get("name", ""),
                "mimetype": attachment.get("mimetype", ""),
                "file_size": attachment.get("file_size", 0),
                "local_path": local_path,
                "img_width": width,
                "img_height": height,
                "img_alt": alt,
                "source": "html_img_tag"
            }
            
            image_references.append(image_ref)
            logging.info(f"Image intégrée trouvée: ID {image_id} dans message {message_id}")

    def get_image_paths(self) -> List[str]:
        """
        Récupère les chemins des images référencées.
        
        Returns:
            Liste des chemins locaux des images référencées
        """
        try:
            # Tenter d'extraire les images
            data = self.extract_image_references()
            
            # Récupérer les chemins locaux des images
            paths = []
            for ref in data.get("references", []):
                path = ref.get("local_path")
                if path and os.path.exists(path):
                    paths.append(path)
                    
            if not paths:
                logging.warning("Aucune image intégrée trouvée ou les chemins sont invalides")
                
            return paths
            
        except Exception as e:
            logging.error(f"Erreur lors de la récupération des chemins d'images: {e}")
            return []
        
def extract_images_from_ticket(ticket_dir: str) -> List[str]:
    """
    Fonction utilitaire pour extraire les images intégrées dans les messages HTML d'un ticket.
    
    Args:
        ticket_dir: Répertoire du ticket contenant les messages et pièces jointes
        
    Returns:
        Liste des chemins locaux des images référencées
    """
    extractor = HtmlImageExtractor(ticket_dir)
    return extractor.get_image_paths()

if __name__ == "__main__":
    # Test avec un répertoire de ticket spécifique
    import sys
    
    if len(sys.argv) > 1:
        ticket_dir = sys.argv[1]
    else:
        # Utiliser un répertoire de test par défaut
        ticket_dir = "./output/ticket_T0241/T0241_20250409_141018"
    
    if not os.path.exists(ticket_dir):
        print(f"Répertoire introuvable: {ticket_dir}")
        sys.exit(1)
        
    print(f"Extraction des images intégrées dans le HTML pour le ticket: {os.path.basename(ticket_dir)}")
    
    extractor = HtmlImageExtractor(ticket_dir)
    result = extractor.extract_image_references()
    
    print(f"Statut: {result['status']}")
    print(f"Message: {result['message']}")
    print(f"Nombre de références trouvées: {len(result['references'])}")
    
    for i, ref in enumerate(result["references"]):
        print(f"\nImage {i+1}:")
        print(f"  ID: {ref['image_id']}")
        print(f"  Nom: {ref['name']}")
        print(f"  Type: {ref['mimetype']}")
        print(f"  Taille: {ref['file_size']} octets")
        print(f"  Chemin local: {ref['local_path']}")
        
    # Récupérer les chemins des images
    paths = extractor.get_image_paths()
    print(f"\nChemins des images ({len(paths)}):")
    for path in paths:
        print(f"  {path}") 