"""
Utilitaires généraux pour l'extraction de tickets.
"""

import os
import json
import logging
import re
from typing import Dict, Any, List, Optional, Union
from html import unescape
from bs4 import BeautifulSoup, Tag
import html2text
import unicodedata

def setup_logging(level: Union[str, int] = logging.INFO, log_file: Optional[str] = None) -> None:
    """
    Configure la journalisation avec un format spécifique et éventuellement un fichier de logs.
    
    Args:
        level: Niveau de journalisation en tant que chaîne (ex: "INFO", "DEBUG") ou valeur entière (default: logging.INFO)
        log_file: Chemin du fichier de log (default: None)
    """
    # Convertir le niveau de log si c'est une chaîne
    if isinstance(level, str):
        numeric_level = getattr(logging, level.upper(), None)
        if not isinstance(numeric_level, int):
            raise ValueError(f"Niveau de journalisation invalide: {level}")
    else:
        numeric_level = level
    
    logging.basicConfig(
        level=numeric_level,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    
    # Ajout d'un gestionnaire de fichier si log_file est spécifié
    if log_file:
        # S'assurer que le répertoire existe
        log_dir = os.path.dirname(log_file)
        if log_dir and not os.path.exists(log_dir):
            os.makedirs(log_dir, exist_ok=True)
            
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        file_handler.setLevel(numeric_level)
        file_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', '%Y-%m-%d %H:%M:%S')
        file_handler.setFormatter(file_formatter)
        logging.getLogger().addHandler(file_handler)

def log_separator(length: int = 60) -> None:
    """
    Ajoute une ligne de séparation dans les logs.
    
    Args:
        length: Longueur de la ligne (default: 60)
    """
    logging.info("-" * length)

def save_json(data: Any, file_path: str) -> bool:
    """
    Sauvegarde des données au format JSON dans un fichier.
    
    Args:
        data: Données à sauvegarder
        file_path: Chemin du fichier
        
    Returns:
        True si la sauvegarde a réussi, False sinon
    """
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        return True
    except Exception as e:
        logging.error(f"Erreur lors de la sauvegarde du fichier JSON {file_path}: {e}")
        return False

def save_text(text: str, file_path: str) -> bool:
    """
    Sauvegarde du texte dans un fichier.
    
    Args:
        text: Texte à sauvegarder
        file_path: Chemin du fichier
        
    Returns:
        True si la sauvegarde a réussi, False sinon
    """
    try:
        # S'assurer que le répertoire existe
        directory = os.path.dirname(file_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory, exist_ok=True)
            
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(text)
        return True
    except Exception as e:
        logging.error(f"Erreur lors de la sauvegarde du fichier texte {file_path}: {e}")
        return False

def is_important_image(tag: Tag, message_text: str) -> bool:
    """
    Détermine si une image est importante ou s'il s'agit d'un logo/signature.
    
    Args:
        tag: La balise d'image à analyser
        message_text: Le texte complet du message pour contexte
        
    Returns:
        True si l'image semble importante, False sinon
    """
    # Vérifier les attributs de l'image
    src = str(tag.get('src', ''))
    alt = str(tag.get('alt', ''))
    title = str(tag.get('title', ''))
    
    # Patterns pour les images inutiles
    useless_img_patterns = [
        'logo', 'signature', 'outlook', 'footer', 'header', 'icon', 
        'emoticon', 'emoji', 'cid:', 'pixel', 'spacer', 'vignette',
        'banner', 'separator', 'decoration', 'mail_signature'
    ]
    
    # Vérifier si c'est une image inutile
    for pattern in useless_img_patterns:
        if (pattern in src.lower() or 
            pattern in alt.lower() or 
            pattern in title.lower()):
            return False
    
    # Vérifier la taille
    width_str = str(tag.get('width', ''))
    height_str = str(tag.get('height', ''))
    
    try:
        if width_str.isdigit() and height_str.isdigit():
            width = int(width_str)
            height = int(height_str)
            if width <= 50 and height <= 50:
                return False
    except (ValueError, TypeError):
        pass
    
    # Vérifier si l'image est mentionnée dans le texte
    image_indicators = [
        'capture', 'screenshot', 'image', 'photo', 'illustration', 
        'voir', 'regarder', 'ci-joint', 'écran', 'erreur', 'problème', 
        'bug', 'pièce jointe', 'attachment', 'veuillez trouver'
    ]
    
    for indicator in image_indicators:
        if indicator in message_text.lower():
            return True
    
    return True

def clean_html(html_content: str, 
               strategy: str = "html2text", 
               preserve_links: bool = False,
               preserve_images: bool = False) -> str:
    """
    Nettoie le contenu HTML et le convertit en texte selon la stratégie spécifiée.
    
    Args:
        html_content: Contenu HTML à nettoyer
        strategy: Stratégie de nettoyage ('strip_tags', 'html2text', 'soup') (default: 'html2text')
        preserve_links: Conserver les liens dans la version texte (default: False)
        preserve_images: Conserver les références aux images (default: False)
        
    Returns:
        Texte nettoyé
    """
    if not html_content:
        return ""
    
    # Remplacer les balises br par des sauts de ligne
    html_content = re.sub(r'<br\s*/?>|<BR\s*/?>', '\n', html_content)
    
    if strategy == "strip_tags":
        # Solution simple: suppression des balises HTML
        text = re.sub(r'<[^>]+>', '', html_content)
        # Nettoyer les espaces multiples et les lignes vides multiples
        text = re.sub(r'\s+', ' ', text)
        text = re.sub(r'\n\s*\n', '\n\n', text)
        return text.strip()
    
    elif strategy == "html2text":
        # Utiliser html2text pour une meilleure conversion
        h = html2text.HTML2Text()
        h.ignore_links = not preserve_links
        h.ignore_images = not preserve_images
        h.body_width = 0  # Ne pas limiter la largeur du texte
        return h.handle(html_content).strip()
    
    elif strategy == "soup":
        # Utiliser BeautifulSoup pour un nettoyage plus avancé
        try:
            soup = BeautifulSoup(html_content, 'html.parser')
            
            # Préserver les liens si demandé
            if preserve_links:
                for a_tag in soup.find_all('a', href=True):
                    if isinstance(a_tag, Tag):
                        href = a_tag.get('href', '')
                        new_text = f"{a_tag.get_text()} [{href}]"
                        new_tag = soup.new_string(new_text)
                        a_tag.replace_with(new_tag)
                    
            # Préserver les images si demandé
            if preserve_images:
                for img_tag in soup.find_all('img'):
                    if isinstance(img_tag, Tag):
                        src = img_tag.get('src', '')
                        alt = img_tag.get('alt', '')
                        new_text = f"[Image: {alt} - {src}]"
                        new_tag = soup.new_string(new_text)
                        img_tag.replace_with(new_tag)
                    
            # Convertir les listes en texte formaté
            for ul in soup.find_all('ul'):
                if isinstance(ul, Tag):
                    for li in ul.find_all('li'):
                        if isinstance(li, Tag):
                            li_text = li.get_text()
                            new_text = f"• {li_text}"
                            new_tag = soup.new_string(new_text)
                            li.replace_with(new_tag)
                    
            for ol in soup.find_all('ol'):
                if isinstance(ol, Tag):
                    for i, li in enumerate(ol.find_all('li')):
                        if isinstance(li, Tag):
                            li_text = li.get_text()
                            new_text = f"{i+1}. {li_text}"
                            new_tag = soup.new_string(new_text)
                            li.replace_with(new_tag)
                    
            text = soup.get_text()
            # Nettoyer les espaces et les lignes vides
            text = re.sub(r'\n\s*\n', '\n\n', text)
            return text.strip()
        except Exception as e:
            logging.warning(f"Erreur lors du nettoyage HTML avec BeautifulSoup: {e}")
            # En cas d'erreur, utiliser une méthode de secours
            return clean_html(html_content, "strip_tags")
    
    else:
        # Stratégie par défaut
        logging.warning(f"Stratégie de nettoyage '{strategy}' inconnue, utilisation de 'strip_tags'")
        return clean_html(html_content, "strip_tags")

def detect_duplicate_content(messages: List[Dict[str, Any]]) -> List[int]:
    """
    Détecte les messages avec un contenu dupliqué et retourne leurs indices.
    
    Args:
        messages: Liste de messages à analyser
        
    Returns:
        Liste des indices des messages dupliqués
    """
    content_map = {}
    duplicate_indices = []
    
    for idx, message in enumerate(messages):
        body = message.get("body", "")
        if not body:
            continue
            
        # Nettoyer le contenu HTML pour la comparaison
        cleaned_content = clean_html(body, "strip_tags")
        # Considérer uniquement les messages avec du contenu significatif
        if len(cleaned_content.strip()) < 10:
            continue
            
        # Vérifier si le contenu existe déjà
        if cleaned_content in content_map:
            duplicate_indices.append(idx)
        else:
            content_map[cleaned_content] = idx
            
    return duplicate_indices

def normalize_filename(name: str) -> str:
    """
    Normalise un nom de fichier en remplaçant les caractères non autorisés.
    
    Args:
        name: Nom à normaliser
        
    Returns:
        Nom normalisé
    """
    # Enlever les accents
    name = unicodedata.normalize('NFKD', name).encode('ASCII', 'ignore').decode('ASCII')
    
    # Remplacer les caractères non alphanumériques par des underscores
    name = re.sub(r'[^\w\.-]', '_', name)
    
    # Limiter la longueur à 255 caractères (limitation commune des systèmes de fichiers)
    # Remplacer les caractères non autorisés par des underscores
    sanitized = re.sub(r'[\\/*?:"<>|]', '_', name)
    # Limiter la longueur du nom à 100 caractères
    if len(sanitized) > 100:
        sanitized = sanitized[:97] + "..."
    return sanitized.strip()
