// ========================================
// SELECTIVE UTILS - UTILITAIRES MODULAIRES
// Responsabilité: Fonctions utilitaires partagées par tous les modules selective
// Architecture: Helper functions réutilisables et composables
// ========================================

const { logSh } = require('../ErrorReporting');

/**
 * UTILITAIRE: Afficher liste complète des éléments (réutilisable)
 */
function logElementsList(elements, title = 'LISTE DES ÉLÉMENTS', generatedKeywords = null) {
  logSh(`\n📋 === ${title} (${elements.length} éléments) ===`, 'INFO');

  elements.forEach((el, idx) => {
    // Déterminer la source si generatedKeywords fourni
    let source = '📋 GSheet';
    if (generatedKeywords) {
      const isGenerated = generatedKeywords[el.name] ||
        (generatedKeywords._subVariables && Object.keys(generatedKeywords._subVariables).some(k => k.startsWith(el.name + '_')));
      source = isGenerated ? '🤖 IA' : '📋 GSheet';
    }

    logSh(`   [${idx + 1}] ${source} ${el.name}`, 'INFO');
    logSh(`      📄 resolvedContent: "${el.resolvedContent}"`, 'INFO');
    if (el.instructions) {
      const instPreview = el.instructions.length > 100 ? el.instructions.substring(0, 100) + '...' : el.instructions;
      logSh(`      📜 instructions: "${instPreview}"`, 'INFO');
    }
  });

  logSh(`=========================================\n`, 'INFO');
}

/**
 * ANALYSEURS DE CONTENU SELECTIVE
 */

/**
 * Analyser qualité technique d'un contenu
 */
function analyzeTechnicalQuality(content, contextualTerms = []) {
  if (!content || typeof content !== 'string') return { score: 0, details: {} };
  
  const analysis = {
    score: 0,
    details: {
      technicalTermsFound: 0,
      technicalTermsExpected: contextualTerms.length,
      genericWordsCount: 0,
      hasSpecifications: false,
      hasDimensions: false,
      contextIntegration: 0
    }
  };
  
  const lowerContent = content.toLowerCase();
  
  // 1. Compter termes techniques présents
  contextualTerms.forEach(term => {
    if (lowerContent.includes(term.toLowerCase())) {
      analysis.details.technicalTermsFound++;
    }
  });
  
  // 2. Détecter mots génériques
  const genericWords = ['produit', 'solution', 'service', 'offre', 'article', 'élément'];
  analysis.details.genericWordsCount = genericWords.filter(word => 
    lowerContent.includes(word)
  ).length;
  
  // 3. Vérifier spécifications techniques
  analysis.details.hasSpecifications = /\b(norme|iso|din|ce)\b/i.test(content);
  
  // 4. Vérifier dimensions/données techniques
  analysis.details.hasDimensions = /\d+\s*(mm|cm|m|%|°|kg|g)\b/i.test(content);
  
  // 5. Calculer score global (0-100)
  const termRatio = contextualTerms.length > 0 ? 
    (analysis.details.technicalTermsFound / contextualTerms.length) * 40 : 20;
  const genericPenalty = Math.min(20, analysis.details.genericWordsCount * 5);
  const specificationBonus = analysis.details.hasSpecifications ? 15 : 0;
  const dimensionBonus = analysis.details.hasDimensions ? 15 : 0;
  const lengthBonus = content.length > 100 ? 10 : 0;
  
  analysis.score = Math.max(0, Math.min(100, 
    termRatio + specificationBonus + dimensionBonus + lengthBonus - genericPenalty
  ));
  
  return analysis;
}

/**
 * Analyser fluidité des transitions
 */
function analyzeTransitionFluidity(content) {
  if (!content || typeof content !== 'string') return { score: 0, details: {} };
  
  const sentences = content.split(/[.!?]+/)
    .map(s => s.trim())
    .filter(s => s.length > 5);
    
  if (sentences.length < 2) {
    return { score: 100, details: { reason: 'Contenu trop court pour analyse transitions' } };
  }
  
  const analysis = {
    score: 0,
    details: {
      sentencesCount: sentences.length,
      connectorsFound: 0,
      repetitiveConnectors: 0,
      abruptTransitions: 0,
      averageSentenceLength: 0,
      lengthVariation: 0
    }
  };
  
  // 1. Analyser connecteurs
  const commonConnectors = ['par ailleurs', 'en effet', 'de plus', 'cependant', 'ainsi', 'donc', 'ensuite'];
  const connectorCounts = {};
  
  commonConnectors.forEach(connector => {
    const matches = (content.match(new RegExp(`\\b${connector}\\b`, 'gi')) || []);
    connectorCounts[connector] = matches.length;
    analysis.details.connectorsFound += matches.length;
    if (matches.length > 1) analysis.details.repetitiveConnectors++;
  });
  
  // 2. Détecter transitions abruptes
  for (let i = 1; i < sentences.length; i++) {
    const sentence = sentences[i].toLowerCase().trim();
    const hasConnector = commonConnectors.some(connector => 
      sentence.startsWith(connector) || sentence.includes(` ${connector} `)
    );
    
    if (!hasConnector && sentence.length > 20) {
      analysis.details.abruptTransitions++;
    }
  }
  
  // 3. Analyser variation de longueur
  const lengths = sentences.map(s => s.split(/\s+/).length);
  analysis.details.averageSentenceLength = lengths.reduce((a, b) => a + b, 0) / lengths.length;
  
  const variance = lengths.reduce((acc, len) => 
    acc + Math.pow(len - analysis.details.averageSentenceLength, 2), 0
  ) / lengths.length;
  analysis.details.lengthVariation = Math.sqrt(variance);
  
  // 4. Calculer score fluidité (0-100)
  const connectorScore = Math.min(30, (analysis.details.connectorsFound / sentences.length) * 100);
  const repetitionPenalty = Math.min(20, analysis.details.repetitiveConnectors * 5);
  const abruptPenalty = Math.min(30, (analysis.details.abruptTransitions / sentences.length) * 50);
  const variationScore = Math.min(20, analysis.details.lengthVariation * 2);
  
  analysis.score = Math.max(0, Math.min(100,
    connectorScore + variationScore - repetitionPenalty - abruptPenalty + 50
  ));
  
  return analysis;
}

/**
 * Analyser cohérence de style
 */
function analyzeStyleConsistency(content, expectedPersonality = null) {
  if (!content || typeof content !== 'string') return { score: 0, details: {} };
  
  const analysis = {
    score: 0,
    details: {
      personalityAlignment: 0,
      toneConsistency: 0,
      vocabularyLevel: 'standard',
      formalityScore: 0,
      personalityWordsFound: 0
    }
  };
  
  // 1. Analyser alignement personnalité
  if (expectedPersonality && expectedPersonality.vocabulairePref) {
    // Convertir en string si ce n'est pas déjà le cas
    const vocabPref = typeof expectedPersonality.vocabulairePref === 'string'
      ? expectedPersonality.vocabulairePref
      : String(expectedPersonality.vocabulairePref);

    const personalityWords = vocabPref.toLowerCase().split(',');
    const contentLower = content.toLowerCase();
    
    personalityWords.forEach(word => {
      if (word.trim() && contentLower.includes(word.trim())) {
        analysis.details.personalityWordsFound++;
      }
    });
    
    analysis.details.personalityAlignment = personalityWords.length > 0 ?
      (analysis.details.personalityWordsFound / personalityWords.length) * 100 : 0;
  }
  
  // 2. Analyser niveau vocabulaire
  const technicalWords = content.match(/\b\w{8,}\b/g) || [];
  const totalWords = content.split(/\s+/).length;
  const techRatio = technicalWords.length / totalWords;
  
  if (techRatio > 0.15) analysis.details.vocabularyLevel = 'expert';
  else if (techRatio < 0.05) analysis.details.vocabularyLevel = 'accessible';
  else analysis.details.vocabularyLevel = 'standard';
  
  // 3. Analyser formalité
  const formalIndicators = ['il convient de', 'par conséquent', 'néanmoins', 'toutefois'];
  const casualIndicators = ['du coup', 'sympa', 'cool', 'nickel'];
  
  let formalCount = formalIndicators.filter(indicator => 
    content.toLowerCase().includes(indicator)
  ).length;
  
  let casualCount = casualIndicators.filter(indicator => 
    content.toLowerCase().includes(indicator)
  ).length;
  
  analysis.details.formalityScore = formalCount - casualCount; // Positif = formel, négatif = casual
  
  // 4. Calculer score cohérence (0-100)
  let baseScore = 50;
  
  if (expectedPersonality) {
    baseScore += analysis.details.personalityAlignment * 0.3;
    
    // Ajustements selon niveau technique attendu
    const expectedLevel = expectedPersonality.niveauTechnique || 'standard';
    if (expectedLevel === analysis.details.vocabularyLevel) {
      baseScore += 20;
    } else {
      baseScore -= 10;
    }
  }
  
  // Bonus cohérence tonale
  const sentences = content.split(/[.!?]+/).filter(s => s.length > 10);
  if (sentences.length > 1) {
    baseScore += Math.min(20, analysis.details.lengthVariation || 10);
  }
  
  analysis.score = Math.max(0, Math.min(100, baseScore));
  
  return analysis;
}

/**
 * COMPARATEURS ET MÉTRIQUES
 */

/**
 * Comparer deux contenus et calculer taux amélioration
 */
function compareContentImprovement(original, enhanced, analysisType = 'general') {
  if (!original || !enhanced) return { improvementRate: 0, details: {} };
  
  const comparison = {
    improvementRate: 0,
    details: {
      lengthChange: ((enhanced.length - original.length) / original.length) * 100,
      wordCountChange: 0,
      structuralChanges: 0,
      contentPreserved: true
    }
  };
  
  // 1. Analyser changements structurels
  const originalSentences = original.split(/[.!?]+/).length;
  const enhancedSentences = enhanced.split(/[.!?]+/).length;
  comparison.details.structuralChanges = Math.abs(enhancedSentences - originalSentences);
  
  // 2. Analyser changements de mots
  const originalWords = original.toLowerCase().split(/\s+/).filter(w => w.length > 2);
  const enhancedWords = enhanced.toLowerCase().split(/\s+/).filter(w => w.length > 2);
  comparison.details.wordCountChange = enhancedWords.length - originalWords.length;
  
  // 3. Vérifier préservation du contenu principal
  const originalKeyWords = originalWords.filter(w => w.length > 4);
  const preservedWords = originalKeyWords.filter(w => enhanced.toLowerCase().includes(w));
  comparison.details.contentPreserved = (preservedWords.length / originalKeyWords.length) > 0.7;
  
  // 4. Calculer taux amélioration selon type d'analyse
  switch (analysisType) {
    case 'technical':
      const originalTech = analyzeTechnicalQuality(original);
      const enhancedTech = analyzeTechnicalQuality(enhanced);
      comparison.improvementRate = enhancedTech.score - originalTech.score;
      break;
      
    case 'transitions':
      const originalFluid = analyzeTransitionFluidity(original);
      const enhancedFluid = analyzeTransitionFluidity(enhanced);
      comparison.improvementRate = enhancedFluid.score - originalFluid.score;
      break;
      
    case 'style':
      const originalStyle = analyzeStyleConsistency(original);
      const enhancedStyle = analyzeStyleConsistency(enhanced);
      comparison.improvementRate = enhancedStyle.score - originalStyle.score;
      break;
      
    default:
      // Amélioration générale (moyenne pondérée)
      comparison.improvementRate = Math.min(50, Math.abs(comparison.details.lengthChange) * 0.1 +
        (comparison.details.contentPreserved ? 20 : -20) +
        Math.min(15, Math.abs(comparison.details.wordCountChange)));
  }
  
  return comparison;
}

/**
 * UTILITAIRES DE CONTENU
 */

/**
 * Nettoyer contenu généré par LLM
 */
function cleanGeneratedContent(content, cleaningLevel = 'standard') {
  if (!content || typeof content !== 'string') return content;
  
  let cleaned = content.trim();
  
  // Nettoyage de base
  cleaned = cleaned.replace(/^(voici\s+)?le\s+contenu\s+(amélioré|modifié|réécrit)[:\s]*/gi, '');
  cleaned = cleaned.replace(/^(bon,?\s*)?(alors,?\s*)?(voici\s+)?/gi, '');
  cleaned = cleaned.replace(/^(avec\s+les?\s+)?améliorations?\s*[:\s]*/gi, '');
  
  // Nettoyage formatage
  cleaned = cleaned.replace(/\*\*([^*]+)\*\*/g, '$1'); // Gras markdown → texte normal
  cleaned = cleaned.replace(/\s{2,}/g, ' '); // Espaces multiples
  cleaned = cleaned.replace(/([.!?])\s*([.!?])/g, '$1 '); // Double ponctuation
  
  if (cleaningLevel === 'intensive') {
    // Nettoyage intensif
    cleaned = cleaned.replace(/^\s*[-*+]\s*/gm, ''); // Puces en début de ligne
    cleaned = cleaned.replace(/^(pour\s+)?(ce\s+)?(contenu\s*)?[,:]?\s*/gi, '');
    cleaned = cleaned.replace(/\([^)]*\)/g, ''); // Parenthèses et contenu
  }
  
  // Nettoyage final
  cleaned = cleaned.replace(/^[,.\s]+/, ''); // Début
  cleaned = cleaned.replace(/[,\s]+$/, ''); // Fin
  cleaned = cleaned.trim();
  
  return cleaned;
}

/**
 * Valider contenu selective
 */
function validateSelectiveContent(content, originalContent, criteria = {}) {
  const validation = {
    isValid: true,
    score: 0,
    issues: [],
    suggestions: []
  };
  
  const {
    minLength = 20,
    maxLengthChange = 50, // % de changement maximum
    preserveContent = true,
    checkTechnicalTerms = true
  } = criteria;
  
  // 1. Vérifier longueur
  if (!content || content.length < minLength) {
    validation.isValid = false;
    validation.issues.push('Contenu trop court');
    validation.suggestions.push('Augmenter la longueur du contenu généré');
  } else {
    validation.score += 25;
  }
  
  // 2. Vérifier changements de longueur
  if (originalContent) {
    const lengthChange = Math.abs((content.length - originalContent.length) / originalContent.length) * 100;
    
    if (lengthChange > maxLengthChange) {
      validation.issues.push('Changement de longueur excessif');
      validation.suggestions.push('Réduire l\'intensité d\'amélioration');
    } else {
      validation.score += 25;
    }
    
    // 3. Vérifier préservation du contenu
    if (preserveContent) {
      const preservation = compareContentImprovement(originalContent, content);
      
      if (!preservation.details.contentPreserved) {
        validation.isValid = false;
        validation.issues.push('Contenu original non préservé');
        validation.suggestions.push('Améliorer conservation du sens original');
      } else {
        validation.score += 25;
      }
    }
  }
  
  // 4. Vérifications spécifiques
  if (checkTechnicalTerms) {
    const technicalQuality = analyzeTechnicalQuality(content);
    
    if (technicalQuality.score > 60) {
      validation.score += 25;
    } else if (technicalQuality.score < 30) {
      validation.issues.push('Qualité technique insuffisante');
      validation.suggestions.push('Ajouter plus de termes techniques spécialisés');
    }
  }
  
  // Score final et validation
  validation.score = Math.min(100, validation.score);
  validation.isValid = validation.isValid && validation.score >= 60;
  
  return validation;
}

/**
 * UTILITAIRES TECHNIQUES
 */

/**
 * Chunk array avec gestion intelligente
 */
function chunkArray(array, chunkSize, smartChunking = false) {
  if (!Array.isArray(array)) return [];
  if (array.length <= chunkSize) return [array];
  
  const chunks = [];
  
  if (smartChunking) {
    // Chunking intelligent : éviter de séparer éléments liés
    let currentChunk = [];
    
    for (let i = 0; i < array.length; i++) {
      currentChunk.push(array[i]);
      
      // Conditions de fin de chunk intelligente
      const isChunkFull = currentChunk.length >= chunkSize;
      const isLastElement = i === array.length - 1;
      const nextElementRelated = i < array.length - 1 && 
        array[i].tag && array[i + 1].tag && 
        array[i].tag.includes('FAQ') && array[i + 1].tag.includes('FAQ');
      
      if ((isChunkFull && !nextElementRelated) || isLastElement) {
        chunks.push([...currentChunk]);
        currentChunk = [];
      }
    }
    
    // Ajouter chunk restant si non vide
    if (currentChunk.length > 0) {
      if (chunks.length > 0 && chunks[chunks.length - 1].length + currentChunk.length <= chunkSize * 1.2) {
        // Merger avec dernier chunk si pas trop gros
        chunks[chunks.length - 1].push(...currentChunk);
      } else {
        chunks.push(currentChunk);
      }
    }
  } else {
    // Chunking standard
    for (let i = 0; i < array.length; i += chunkSize) {
      chunks.push(array.slice(i, i + chunkSize));
    }
  }
  
  return chunks;
}

/**
 * Sleep avec logging optionnel
 */
async function sleep(ms, logMessage = null) {
  if (logMessage) {
    logSh(`⏳ ${logMessage} (${ms}ms)`, 'DEBUG');
  }
  
  return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * Mesurer performance d'opération
 */
function measurePerformance(operationName, startTime = Date.now()) {
  const endTime = Date.now();
  const duration = endTime - startTime;
  
  const performance = {
    operationName,
    startTime,
    endTime,
    duration,
    durationFormatted: formatDuration(duration)
  };
  
  return performance;
}

/**
 * Formater durée en format lisible
 */
function formatDuration(ms) {
  if (ms < 1000) return `${ms}ms`;
  if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
  return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
}

/**
 * GÉNÉRATION SIMPLE (REMPLACE CONTENTGENERATION.JS)
 */

/**
 * Détecter le type d'élément (legacy InitialGeneration.js logic)
 * Retourne un string simple : 'titre', 'intro', 'paragraphe', 'faq_question', 'faq_reponse', 'conclusion'
 */
function detectElementType(tag) {
  const tagLower = tag.toLowerCase();

  // 🔥 FIX: Vérifier d'abord les préfixes de type spécifique (Intro_, Titre_, Txt_)
  // avant les suffixes génériques (_title, _text)

  // Intro_H2_1, Intro_H3_5, etc. → type 'intro'
  if (tagLower.startsWith('intro_')) {
    return 'intro';
  }

  // Titre_H2_1, Titre_H3_5, etc. → type 'titre'
  if (tagLower.startsWith('titre_')) {
    return 'titre';
  }

  // Txt_H2_1, Txt_H3_5, etc. → type 'paragraphe'
  if (tagLower.startsWith('txt_')) {
    return 'paragraphe';
  }

  // Conclusion_* → type 'conclusion'
  if (tagLower.startsWith('conclu') || tagLower.includes('c_1') || tagLower === 'c1') {
    return 'conclusion';
  }

  // FAQ
  if (tagLower.includes('faq') || tagLower.includes('question') || tagLower.startsWith('q_') || tagLower.startsWith('q-')) {
    return 'faq_question';
  }

  if (tagLower.includes('answer') || tagLower.includes('réponse') || tagLower.includes('reponse') || tagLower.startsWith('a_') || tagLower.startsWith('a-')) {
    return 'faq_reponse';
  }

  // Suffixes génériques pour format alternatif (H2_1_title, H2_1_text)
  // À vérifier APRÈS les préfixes pour éviter les conflits
  if (tagLower.endsWith('_title')) {
    return 'titre';
  } else if (tagLower.endsWith('_text')) {
    return 'paragraphe';
  }

  // Paragraphes (défaut)
  return 'paragraphe';
}

/**
 * Détecter contrainte de longueur dans une instruction
 * Retourne { hasConstraint: boolean, constraint: string|null }
 */
function detectLengthConstraintInInstruction(instruction) {
  if (!instruction) return { hasConstraint: false, constraint: null };

  const lowerInstr = instruction.toLowerCase();

  // Patterns de contraintes : "X mots", "X-Y mots", "environ X mots", "maximum X mots"
  const patterns = [
    /(\d+)\s*-\s*(\d+)\s*mots?/i,           // "80-200 mots"
    /environ\s+(\d+)\s*mots?/i,             // "environ 100 mots"
    /maximum\s+(\d+)\s*mots?/i,             // "maximum 25 mots"
    /minimum\s+(\d+)\s*mots?/i,             // "minimum 50 mots"
    /(\d+)\s+mots?\s+(maximum|minimum)/i,   // "25 mots maximum"
    /^(\d+)\s*mots?$/i,                     // "25 mots" seul
    /\b(\d+)\s*mots?\b/i                    // "X mots" quelque part
  ];

  for (const pattern of patterns) {
    const match = instruction.match(pattern);
    if (match) {
      return { hasConstraint: true, constraint: match[0] };
    }
  }

  return { hasConstraint: false, constraint: null };
}

/**
 * Créer un prompt adapté au type d'élément avec contraintes de longueur (legacy logic)
 * @param {string} associatedTitle - Titre généré précédemment pour les textes/intros (important pour cohérence)
 * @param {string} specificKeyword - Mot-clé spécifique de l'élément (resolvedContent) au lieu du mot-clé général
 */
function createTypedPrompt(tag, type, instruction, csvData, associatedTitle = null, specificKeyword = null) {
  // 🔥 FIX: Utiliser UNIQUEMENT le mot-clé spécifique de l'élément (resolvedContent)
  // PAS de fallback sur csvData.mc0
  let keyword;
  if (specificKeyword) {
    keyword = specificKeyword;
    logSh(`   🎯 Mot-clé SPÉCIFIQUE utilisé: "${specificKeyword}"`, 'INFO');
  } else {
    logSh(`   ❌ ERREUR: Aucun mot-clé spécifique (resolvedContent) fourni pour ${tag}`, 'ERROR');
    keyword = ''; // Pas de fallback
  }

  const title = csvData.t0 || '';
  const personality = csvData.personality;

  // 🔥 NOUVEAU : Détecter si l'instruction contient déjà une contrainte de longueur
  const instructionConstraint = detectLengthConstraintInInstruction(instruction);

  // 📊 LOG: Afficher détection contrainte
  if (instructionConstraint.hasConstraint) {
    logSh(`   🔍 Contrainte détectée dans instruction: "${instructionConstraint.constraint}"`, 'DEBUG');
  } else {
    logSh(`   ⚙️ Aucune contrainte détectée, utilisation contrainte type "${type}"`, 'DEBUG');
  }

  let lengthConstraint = '';
  let specificInstructions = '';

  // Si l'instruction a déjà une contrainte, ne pas en ajouter une autre
  if (instructionConstraint.hasConstraint) {
    lengthConstraint = `RESPECTE STRICTEMENT la contrainte de longueur indiquée dans l'instruction : "${instructionConstraint.constraint}"`;

    // Instructions génériques selon type (sans répéter la longueur)
    switch (type) {
      case 'titre':
        specificInstructions = `Le titre doit être:
- COURT et PERCUTANT
- Pas de phrases complètes
- Intégrer "${keyword}"`;
        break;

      case 'intro':
        specificInstructions = `L'introduction doit:
- Présenter le sujet
- Accrocher le lecteur`;
        break;

      case 'conclusion':
        specificInstructions = `La conclusion doit:
- Résumer les points clés
- Appel à l'action si pertinent`;
        break;

      case 'faq_question':
        specificInstructions = `La question FAQ doit être:
- Courte et directe
- Formulée du point de vue utilisateur`;
        break;

      case 'faq_reponse':
        specificInstructions = `La réponse FAQ doit être:
- Directe et informative
- Répondre précisément à la question`;
        break;

      case 'paragraphe':
      default:
        specificInstructions = `Le paragraphe doit:
- Développer un aspect du sujet
- Contenu informatif et engageant`;
        break;
    }
  } else {
    // Pas de contrainte dans l'instruction → utiliser les contraintes par défaut du type
    switch (type) {
      case 'titre':
        lengthConstraint = '8-15 mots MAXIMUM';
        specificInstructions = `Le titre doit être:
- COURT et PERCUTANT (8-15 mots max)
- Pas de phrases complètes
- Intégrer "${keyword}"`;
        break;

      case 'intro':
        lengthConstraint = '40-80 mots (2-3 phrases courtes)';
        specificInstructions = `L'introduction doit:
- Présenter le sujet
- Accrocher le lecteur
- 40-80 mots seulement`;
        break;

      case 'conclusion':
        lengthConstraint = '40-80 mots (2-3 phrases courtes)';
        specificInstructions = `La conclusion doit:
- Résumer les points clés
- Appel à l'action si pertinent
- 40-80 mots seulement`;
        break;

      case 'faq_question':
        lengthConstraint = '10-20 mots';
        specificInstructions = `La question FAQ doit être:
- Courte et directe (10-20 mots)
- Formulée du point de vue utilisateur`;
        break;

      case 'faq_reponse':
        lengthConstraint = '60-120 mots (3-5 phrases)';
        specificInstructions = `La réponse FAQ doit être:
- Directe et informative (60-120 mots)
- Répondre précisément à la question`;
        break;

      case 'paragraphe':
      default:
        lengthConstraint = '80-200 mots (3-6 phrases)';
        specificInstructions = `Le paragraphe doit:
- Développer un aspect du sujet
- Contenu informatif et engageant
- 80-200 mots (PAS PLUS)`;
        break;
    }
  }

  // 🔥 LASER FOCUS sur le titre et extraction des mots-clés importants
  let titleContext = '';
  if (associatedTitle && (type === 'intro' || type === 'paragraphe')) {
    // Extraire les mots-clés importants (> 4 lettres, sauf mots vides courants)
    const stopWords = ['dans', 'avec', 'pour', 'sans', 'sous', 'vers', 'chez', 'sur', 'par', 'tous', 'toutes', 'cette', 'votre', 'notre'];
    const titleWords = associatedTitle
      .toLowerCase()
      .replace(/[.,;:!?'"]/g, '')
      .split(/\s+/)
      .filter(word => word.length > 4 && !stopWords.includes(word));

    const keywordsHighlight = titleWords.length > 0
      ? `Mots-clés à développer: ${titleWords.join(', ')}\n`
      : '';

    titleContext = `
🎯 TITRE À DÉVELOPPER: "${associatedTitle}"
${keywordsHighlight}⚠️ IMPORTANT: Ton contenu doit développer SPÉCIFIQUEMENT ce titre et ses concepts clés.
Ne génère pas de contenu générique, concentre-toi sur les mots-clés identifiés.
`;
  }

  // 🔥 Helper : Sélectionner aléatoirement max N éléments d'un array
  const selectRandomItems = (arr, max = 2) => {
    if (!Array.isArray(arr) || arr.length === 0) return arr;
    if (arr.length <= max) return arr;

    // Fisher-Yates shuffle puis prendre les N premiers
    const shuffled = [...arr];
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled.slice(0, max);
  };

  // 🔥 NOUVEAU : Contexte personnalité enrichi
  // ⚠️ EXCLUSION : Pas de personnalité pour les questions FAQ (seulement pour les réponses)
  let personalityContext = '';
  const includePersonality = personality && type !== 'faq_question';

  if (includePersonality) {
    // 🎲 Sélection aléatoire de 2 éléments max pour D, E, F, J
    const vocabArray = Array.isArray(personality.vocabulairePref)
      ? personality.vocabulairePref
      : (personality.vocabulairePref || '').split(',').map(s => s.trim()).filter(s => s);
    const vocabList = selectRandomItems(vocabArray, 2).join(', ');

    const connecteursArray = Array.isArray(personality.connecteursPref)
      ? personality.connecteursPref
      : (personality.connecteursPref || '').split(',').map(s => s.trim()).filter(s => s);
    const connecteursList = selectRandomItems(connecteursArray, 2).join(', ');

    const motsClésArray = Array.isArray(personality.motsClesSecteurs)
      ? personality.motsClesSecteurs
      : (personality.motsClesSecteurs || '').split(',').map(s => s.trim()).filter(s => s);
    const motsClesList = selectRandomItems(motsClésArray, 2).join(', ');

    const ctaArray = Array.isArray(personality.ctaStyle)
      ? personality.ctaStyle
      : (personality.ctaStyle || '').split(',').map(s => s.trim()).filter(s => s);
    const ctaList = selectRandomItems(ctaArray, 2).join(', ');

    personalityContext = `
PROFIL PERSONNALITÉ RÉDACTEUR:
- Nom: ${personality.nom || 'Standard'}
- Profil: ${personality.description || 'Expert généraliste'}
- Style: ${personality.style || 'professionnel'}
${motsClesList ? `- Secteurs d'expertise: ${motsClesList}` : ''}
${vocabList ? `- Vocabulaire préféré: ${vocabList}` : ''}
${connecteursList ? `- Connecteurs préférés: ${connecteursList}` : ''}
${personality.longueurPhrases ? `- Longueur phrases: ${personality.longueurPhrases}` : ''}
${personality.niveauTechnique ? `- Niveau technique: ${personality.niveauTechnique}` : ''}
${ctaList ? `- Style CTA: ${ctaList}` : ''}
`;
  }

  const prompt = `Tu es un rédacteur SEO expert. Génère du contenu professionnel et naturel.

CONTEXTE:
- Sujet principal: ${keyword}
- Titre de l'article: ${title}
${personalityContext}${titleContext}
ÉLÉMENT À GÉNÉRER: ${tag} (Type: ${type})

INSTRUCTION SPÉCIFIQUE:
${instruction}

CONTRAINTE DE LONGUEUR (⚠️ CRUCIAL - À RESPECTER ABSOLUMENT):
${lengthConstraint}

${specificInstructions}

CONSIGNES RÉDACTIONNELLES:
${includePersonality ? `- ADOPTE le style et vocabulaire du profil personnalité ci-dessus
- Utilise les connecteurs préférés listés pour fluidifier le texte
- Adapte la longueur des phrases selon le profil (${personality?.longueurPhrases || 'moyennes'})
- Niveau technique: ${personality?.niveauTechnique || 'moyen'}` : '- Formulation neutre et professionnelle (question FAQ)'}
- Ton naturel et humain, pas robotique
- Intégration fluide du mot-clé "${keyword}"
${associatedTitle ? `- 🎯 FOCUS: Développe SPÉCIFIQUEMENT les concepts du titre "${associatedTitle}" (pas de contenu générique)` : ''}
- PAS de formatage markdown (ni **, ni ##, ni -)
- PAS de préambule ou conclusion ajoutée
- ⚠️ IMPÉRATIF: RESPECTE la contrainte de longueur indiquée ci-dessus

RÉPONSE (contenu uniquement, sans intro comme "Voici le contenu..."):`;

  return prompt;
}

/**
 * Génération simple avec LLM configurable (compatible avec l'ancien système)
 */
async function generateSimple(hierarchy, csvData, options = {}) {
  const LLMManager = require('../LLMManager');
  const llmProvider = options.llmProvider || 'claude-sonnet-4-5';

  logSh(`🔥 Génération avec contraintes de longueur par type (${llmProvider.toUpperCase()})`, 'INFO');

  if (!hierarchy || Object.keys(hierarchy).length === 0) {
    throw new Error('Hiérarchie vide ou invalide');
  }

  // 📊 AFFICHER LA HIÉRARCHIE AVANT GÉNÉRATION
  const allElementsFromHierarchy = [];
  for (const [sectionKey, section] of Object.entries(hierarchy)) {
    if (section.title && section.title.originalElement) {
      allElementsFromHierarchy.push(section.title.originalElement);
    }
    if (section.text && section.text.originalElement) {
      allElementsFromHierarchy.push(section.text.originalElement);
    }
    if (section.questions && section.questions.length > 0) {
      section.questions.forEach(faq => {
        if (faq.originalElement) {
          allElementsFromHierarchy.push(faq.originalElement);
        }
      });
    }
  }

  logElementsList(allElementsFromHierarchy, 'ÉLÉMENTS AVANT GÉNÉRATION (depuis hiérarchie)');

  const result = {
    content: {},
    stats: {
      processed: 0,
      enhanced: 0,
      duration: 0,
      llmProvider: llmProvider
    }
  };

  const startTime = Date.now();

  // Fonction utilitaire pour résoudre les variables
  const resolveVariables = (text, csvData) => {
    return text.replace(/\{\{?([^}]+)\}?\}/g, (match, variable) => {
      const cleanVar = variable.trim();

      if (cleanVar === 'MC0') return csvData.mc0 || '';
      if (cleanVar === 'T0') return csvData.t0 || '';
      if (cleanVar === 'T-1') return csvData.tMinus1 || '';
      if (cleanVar === 'L-1') return csvData.lMinus1 || '';

      if (cleanVar.startsWith('MC+1_')) {
        const index = parseInt(cleanVar.split('_')[1]) - 1;
        const mcPlus1 = (csvData.mcPlus1 || '').split(',').map(s => s.trim());
        return mcPlus1[index] || csvData.mc0 || '';
      }

      if (cleanVar.startsWith('T+1_')) {
        const index = parseInt(cleanVar.split('_')[1]) - 1;
        const tPlus1 = (csvData.tPlus1 || '').split(',').map(s => s.trim());
        return tPlus1[index] || csvData.t0 || '';
      }

      if (cleanVar.startsWith('L+1_')) {
        const index = parseInt(cleanVar.split('_')[1]) - 1;
        const lPlus1 = (csvData.lPlus1 || '').split(',').map(s => s.trim());
        return lPlus1[index] || '';
      }

      return csvData.mc0 || '';
    });
  };

  // Fonction pour extraire l'instruction de l'élément
  const extractInstruction = (tag, item) => {
    let extracted = null;

    if (typeof item === 'string') {
      extracted = item;
      logSh(`   🔍 [${tag}] Instruction: "${extracted}"`, 'INFO');
      return extracted;
    }

    if (item.instructions) {
      extracted = item.instructions;
      logSh(`   🔍 [${tag}] Instruction (item.instructions): "${extracted}"`, 'INFO');
      return extracted;
    }

    if (item.title && item.title.instructions) {
      extracted = item.title.instructions;
      logSh(`   🔍 [${tag}] Instruction (title.instructions): "${extracted}"`, 'INFO');
      return extracted;
    }

    if (item.text && item.text.instructions) {
      extracted = item.text.instructions;
      logSh(`   🔍 [${tag}] Instruction (text.instructions): "${extracted}"`, 'INFO');
      return extracted;
    }

    if (item.questions && Array.isArray(item.questions) && item.questions.length > 0) {
      const faqItem = item.questions[0];
      if (faqItem.originalElement && faqItem.originalElement.resolvedContent) {
        extracted = faqItem.originalElement.resolvedContent;
        logSh(`   🔍 [${tag}] Instruction (FAQ resolvedContent): "${extracted}"`, 'INFO');
        return extracted;
      }
      logSh(`   ⚠️ [${tag}] Pas d'instruction FAQ - ignoré`, 'WARNING');
      return "";
    }

    logSh(`   ⚠️ [${tag}] Pas d'instruction trouvée - ignoré`, 'WARNING');
    return "";
  };

  try {
    // Grouper éléments par couples (titre/texte et FAQ)
    const batches = [];

    for (const [sectionKey, section] of Object.entries(hierarchy)) {
      const batch = [];

      // Couple titre + texte
      if (section.title && section.text) {
        // 🔥 FIX: Utiliser le nom original pour préserver le type (Intro_, Txt_, etc.)
        const titleTag = section.title.originalElement?.name || `${sectionKey}_title`;
        const textTag = section.text.originalElement?.name || `${sectionKey}_text`;

        batch.push({ tag: titleTag, item: section.title, isCouple: 'titre' });
        batch.push({ tag: textTag, item: section.text, isCouple: 'texte' });
      } else if (section.title) {
        const tag = section.title.originalElement?.name || sectionKey;
        batch.push({ tag: tag, item: section.title, isCouple: null });
      } else if (section.text) {
        const tag = section.text.originalElement?.name || sectionKey;
        batch.push({ tag: tag, item: section.text, isCouple: null });
      }

      // Paires FAQ (q_1 + a_1, q_2 + a_2, etc.)
      if (section.questions && section.questions.length > 0) {
        for (let i = 0; i < section.questions.length; i += 2) {
          const question = section.questions[i];
          const answer = section.questions[i + 1];

          if (question) {
            batch.push({
              tag: question.hierarchyPath || `faq_q_${i}`,
              item: question,
              isCouple: 'faq_question'
            });
          }

          if (answer) {
            batch.push({
              tag: answer.hierarchyPath || `faq_a_${i}`,
              item: answer,
              isCouple: 'faq_reponse'
            });
          }
        }
      }

      if (batch.length > 0) {
        batches.push(...batch);
      }
    }

    logSh(`📊 Total éléments à générer: ${batches.length}`, 'INFO');

    // 🔥 NOUVEAU : Tracker le dernier titre généré pour l'associer au texte suivant
    let lastGeneratedTitle = null;

    // Générer chaque élément avec prompt typé
    for (let i = 0; i < batches.length; i++) {
      const { tag, item, isCouple } = batches[i];

      try {
        // 📊 AFFICHER LA LISTE AVANT CHAQUE GÉNÉRATION
        logSh(`\n🎯 === GÉNÉRATION DE: ${tag} (${i + 1}/${batches.length}) ===`, 'INFO');
        logElementsList(allElementsFromHierarchy, `ÉTAT DES ÉLÉMENTS AVANT GÉNÉRATION DE ${tag}`);

        logSh(`🎯 Génération: ${tag}${isCouple ? ` (couple: ${isCouple})` : ''}`, 'DEBUG');

        // 🔥 NOUVEAU : Détecter si le prochain élément est un texte associé à un titre
        const isTitle = isCouple === 'titre';
        const nextBatch = i < batches.length - 1 ? batches[i + 1] : null;
        const nextIsText = nextBatch && (nextBatch.isCouple === 'texte');

        if (isTitle && nextIsText) {
          logSh(`   🔗 Détecté couple titre→texte : ${tag} → ${nextBatch.tag}`, 'DEBUG');
        }

        // Extraire et résoudre l'instruction
        let instruction = extractInstruction(tag, item);
        instruction = instruction.trim();
        instruction = resolveVariables(instruction, csvData);

        // Résoudre variables non résolues manuellement
        const unresolvedPattern = /\b(MC\+1_\d+|T\+1_\d+|L\+1_\d+|MC0|T0|T-1|L-1)\b/gi;
        const unresolved = instruction.match(unresolvedPattern);
        if (unresolved) {
          unresolved.forEach(varName => {
            const upperVar = varName.toUpperCase();
            let replacement = csvData.mc0 || '';

            if (upperVar === 'MC0') replacement = csvData.mc0 || '';
            else if (upperVar === 'T0') replacement = csvData.t0 || '';
            else if (upperVar === 'T-1') replacement = csvData.tMinus1 || '';
            else if (upperVar === 'L-1') replacement = csvData.lMinus1 || '';
            else if (upperVar.startsWith('MC+1_')) {
              const idx = parseInt(upperVar.split('_')[1]) - 1;
              replacement = (csvData.mcPlus1 || '').split(',')[idx]?.trim() || csvData.mc0 || '';
            } else if (upperVar.startsWith('T+1_')) {
              const idx = parseInt(upperVar.split('_')[1]) - 1;
              replacement = (csvData.tPlus1 || '').split(',')[idx]?.trim() || csvData.t0 || '';
            } else if (upperVar.startsWith('L+1_')) {
              const idx = parseInt(upperVar.split('_')[1]) - 1;
              replacement = (csvData.lPlus1 || '').split(',')[idx]?.trim() || '';
            }

            instruction = instruction.replace(new RegExp(varName, 'gi'), replacement);
          });
        }

        // Nettoyer accolades mal formées
        instruction = instruction.replace(/\{[^}]*/g, '').replace(/[{}]/g, '').trim();

        if (!instruction || instruction.length < 10) {
          logSh(`   ⚠️ ${tag}: Pas d'instruction spécifique - génération sans instruction`, 'WARNING');
          instruction = ""; // Générer quand même mais sans instruction spécifique
        }

        // Détecter le type d'élément
        const elementType = detectElementType(tag);
        logSh(`   📝 Type détecté: ${elementType}`, 'DEBUG');

        // 🔥 NOUVEAU : Si c'est un texte et qu'on a un titre généré juste avant, l'utiliser
        const shouldUseTitle = (isCouple === 'texte') && lastGeneratedTitle;
        if (shouldUseTitle) {
          logSh(`   🎯 Utilisation du titre associé: "${lastGeneratedTitle}"`, 'INFO');
        }

        // 🔥 FIX: Extraire le mot-clé spécifique (resolvedContent) de l'élément
        let specificKeyword = null;
        if (item && item.originalElement && item.originalElement.resolvedContent) {
          specificKeyword = item.originalElement.resolvedContent;
          logSh(`   📝 Mot-clé spécifique extrait: "${specificKeyword}"`, 'DEBUG');
        }

        // Créer le prompt avec contraintes de longueur + titre associé + mot-clé spécifique
        const prompt = createTypedPrompt(tag, elementType, instruction, csvData, shouldUseTitle ? lastGeneratedTitle : null, specificKeyword);

        // Appeler le LLM avec maxTokens augmenté
        let maxTokens = 1000; // Défaut augmenté
        if (llmProvider.startsWith('gpt-5')) {
          maxTokens = 2500; // GPT-5 avec reasoning tokens
        } else if (llmProvider.startsWith('gpt-4')) {
          maxTokens = 1500;
        } else if (llmProvider.startsWith('claude')) {
          maxTokens = 2000;
        }

        logSh(`   📏 MaxTokens: ${maxTokens} pour ${llmProvider}`, 'DEBUG');

        let response;
        try {
          response = await LLMManager.callLLM(llmProvider, prompt, {
            temperature: 0.9,
            maxTokens: maxTokens,
            timeout: 45000
          }, csvData.personality);
        } catch (llmError) {
          logSh(`❌ Erreur LLM pour ${tag}: ${llmError.message}`, 'ERROR');
          response = null;
        }

        if (response && response.trim()) {
          const cleaned = cleanGeneratedContent(response.trim());
          result.content[tag] = cleaned;
          result.stats.processed++;
          result.stats.enhanced++;

          // 🔥 NOUVEAU : Si c'est un titre, le stocker pour l'utiliser avec le texte suivant
          if (isTitle) {
            lastGeneratedTitle = cleaned;
            logSh(`   📌 Titre stocké pour le texte suivant: "${cleaned}"`, 'DEBUG');
          }

          // 🔥 NOUVEAU : Si on vient de générer un texte, réinitialiser le titre
          if (isCouple === 'texte') {
            lastGeneratedTitle = null;
          }

          const wordCount = cleaned.split(/\s+/).length;
          logSh(`   ✅ Généré: ${tag} (${wordCount} mots)`, 'DEBUG');
        } else {
          // Fallback avec prompt simplifié
          logSh(`   ⚠️ Réponse vide, retry avec gpt-4o-mini`, 'WARNING');

          try {
            const simplePrompt = `Rédige du contenu professionnel sur "${csvData.mc0}" pour ${tag}. ${elementType === 'titre' ? 'Maximum 15 mots.' : elementType === 'intro' || elementType === 'conclusion' ? 'Environ 50-80 mots.' : 'Environ 100-150 mots.'}`;

            const retryResponse = await LLMManager.callLLM('gpt-4o-mini', simplePrompt, {
              temperature: 0.7,
              maxTokens: 500,
              timeout: 20000
            });

            if (retryResponse && retryResponse.trim()) {
              const cleaned = cleanGeneratedContent(retryResponse.trim());
              result.content[tag] = cleaned;
              result.stats.processed++;
              result.stats.enhanced++;

              // 🔥 NOUVEAU : Stocker le titre même dans le fallback
              if (isTitle) {
                lastGeneratedTitle = cleaned;
                logSh(`   📌 Titre stocké (fallback): "${cleaned}"`, 'DEBUG');
              }
              if (isCouple === 'texte') {
                lastGeneratedTitle = null;
              }

              logSh(`   ✅ Retry réussi pour ${tag}`, 'INFO');
            } else {
              result.content[tag] = `Contenu professionnel sur ${csvData.mc0}. [Généré automatiquement]`;
              result.stats.processed++;

              // 🔥 NOUVEAU : Réinitialiser si c'était un texte
              if (isCouple === 'texte') {
                lastGeneratedTitle = null;
              }
            }
          } catch (retryError) {
            result.content[tag] = `Contenu professionnel sur ${csvData.mc0}. [Erreur: ${retryError.message.substring(0, 50)}]`;
            result.stats.processed++;

            // 🔥 NOUVEAU : Réinitialiser si c'était un texte
            if (isCouple === 'texte') {
              lastGeneratedTitle = null;
            }

            logSh(`   ❌ Retry échoué: ${retryError.message}`, 'ERROR');
          }
        }

      } catch (error) {
        logSh(`❌ Erreur génération ${tag}: ${error.message}`, 'ERROR');
        result.content[tag] = `Contenu professionnel sur ${csvData.mc0}. [Erreur: ${error.message.substring(0, 50)}]`;
        result.stats.processed++;

        // 🔥 NOUVEAU : Réinitialiser si c'était un texte
        if (isCouple === 'texte') {
          lastGeneratedTitle = null;
        }
      }
    }

    result.stats.duration = Date.now() - startTime;

    const generatedElements = Object.keys(result.content).length;
    const successfulElements = result.stats.enhanced;
    const fallbackElements = generatedElements - successfulElements;

    logSh(`✅ Génération terminée: ${generatedElements} éléments`, 'INFO');
    logSh(`   ✓ Succès: ${successfulElements}, Fallback: ${fallbackElements}, Durée: ${result.stats.duration}ms`, 'INFO');

    return result;

  } catch (error) {
    result.stats.duration = Date.now() - startTime;
    logSh(`❌ Échec génération simple: ${error.message}`, 'ERROR');
    throw error;
  }
}

/**
 * STATISTIQUES ET RAPPORTS
 */

/**
 * Générer rapport amélioration
 */
function generateImprovementReport(originalContent, enhancedContent, layerType = 'general') {
  const report = {
    layerType,
    timestamp: new Date().toISOString(),
    summary: {
      elementsProcessed: 0,
      elementsImproved: 0,
      averageImprovement: 0,
      totalExecutionTime: 0
    },
    details: {
      byElement: [],
      qualityMetrics: {},
      recommendations: []
    }
  };
  
  // Analyser chaque élément
  Object.keys(originalContent).forEach(tag => {
    const original = originalContent[tag];
    const enhanced = enhancedContent[tag];
    
    if (original && enhanced) {
      report.summary.elementsProcessed++;
      
      const improvement = compareContentImprovement(original, enhanced, layerType);
      
      if (improvement.improvementRate > 0) {
        report.summary.elementsImproved++;
      }
      
      report.summary.averageImprovement += improvement.improvementRate;
      
      report.details.byElement.push({
        tag,
        improvementRate: improvement.improvementRate,
        lengthChange: improvement.details.lengthChange,
        contentPreserved: improvement.details.contentPreserved
      });
    }
  });
  
  // Calculer moyennes
  if (report.summary.elementsProcessed > 0) {
    report.summary.averageImprovement = report.summary.averageImprovement / report.summary.elementsProcessed;
  }
  
  // Métriques qualité globales
  const fullOriginal = Object.values(originalContent).join(' ');
  const fullEnhanced = Object.values(enhancedContent).join(' ');
  
  report.details.qualityMetrics = {
    technical: analyzeTechnicalQuality(fullEnhanced),
    transitions: analyzeTransitionFluidity(fullEnhanced), 
    style: analyzeStyleConsistency(fullEnhanced)
  };
  
  // Recommandations
  if (report.summary.averageImprovement < 10) {
    report.details.recommendations.push('Augmenter l\'intensité d\'amélioration');
  }
  
  if (report.details.byElement.some(e => !e.contentPreserved)) {
    report.details.recommendations.push('Améliorer préservation du contenu original');
  }
  
  return report;
}

module.exports = {
  // Utilitaires logging
  logElementsList,

  // Analyseurs
  analyzeTechnicalQuality,
  analyzeTransitionFluidity,
  analyzeStyleConsistency,

  // Comparateurs
  compareContentImprovement,

  // Utilitaires contenu
  cleanGeneratedContent,
  validateSelectiveContent,

  // Utilitaires techniques
  chunkArray,
  sleep,
  measurePerformance,
  formatDuration,

  // Génération simple (remplace ContentGeneration.js)
  generateSimple,

  // Rapports
  generateImprovementReport
};