#!/usr/bin/env node
/**
 * Conversion finale avec gestion des instruments multiples (option A)
 * Format : instrument1_et_instrument2_ton_partie[_clef].pdf
 */

const fs = require('fs');
const path = require('path');

const scoresPath = process.argv[2] || path.join(__dirname, '..', 'legacy', 'Scores');
const confirmed = process.argv[3] === 'confirm';

if (!fs.existsSync(scoresPath)) {
  console.error(`❌ Le dossier ${scoresPath} n'existe pas !`);
  process.exit(1);
}

if (!confirmed) {
  console.log('Mode SIMULATION (ajoutez "confirm" pour exécuter)\n');
}

const instruments = {
  'clarinet': 'clarinette', 'clarinette': 'clarinette', 'clarinettte': 'clarinette',
  'petite_clarinette': 'petite_clarinette', 'petite_clarineette': 'petite_clarinette', 'grande_clarinette': 'grande_clarinette', 'es_clarinette': 'petite_clarinette',
  'clarinette_alto': 'clarinette_alto', 'clarinet_alto': 'clarinette_alto', 'alto_clarinet': 'clarinette_alto', 'alto_clarinette': 'clarinette_alto',
  'clarinette_basse': 'clarinette_basse', 'clarinet_basse': 'clarinette_basse', 'clarinette_bass': 'clarinette_basse', 'basse_clarinet': 'clarinette_basse', 'bass_clarinet': 'clarinette_basse', 'bass_clarinette': 'clarinette_basse',
  'clarinette_basse': 'clarinette_basse', 'Bb_Clarinet_Bass': 'clarinette_basse', 'bb_clarinet_bass': 'clarinette_basse',
  'trumpet': 'trompette', 'trompette': 'trompette', 'trrumpet': 'trompette',
  'trombone': 'trombone',
  'tuba': 'tuba', 'tenor_tuba': 'tuba_tenor', 'tuba_tenor': 'tuba_tenor', 'contre_tuba': 'contre_tuba',
  'flute': 'flute', 'flauto': 'flute', 'grande_flute': 'grande_flute', 'petite_flute': 'petite_flute',
  'oboe': 'hautbois', 'hautbois': 'hautbois',
  'bassoon': 'basson', 'basson': 'basson',
  'contre_basson': 'contre_basson', 'contrebasson': 'contre_basson',
  'string_electric_bass': 'basse_electrique',
  'string': 'contrebasse', 'string_bass': 'contrebasse', 'contrebasse': 'contrebasse', 'contre_basse': 'contrebasse', 'contrabass': 'contrebasse',
  'doublebass': 'contrebasse', 'double_bass': 'contrebasse',
  'electric_bass': 'basse_electrique', 'electric_bass_guitar': 'basse_electrique', 'electric': 'electrique',
  'optional_electric_bass': 'basse_electrique', 'optional': '',
  'basse_a_cordes': 'basse_a_cordes',
  'guitar': 'guitare', 'electric_guitar': 'guitare_electrique',
  'cornet': 'cornet', 'bass': 'basse', 'basse': 'basse',
  'baritone': 'baryton', 'baryton': 'baryton', 'bariton': 'baryton', 'baritione': 'baryton', 'bartitone': 'baryton', 'tenorhorn': 'tenorhorn',
  'baryton_euphonium': 'baryton_et_euphonium', 'baritone_euphonium': 'baryton_et_euphonium',
  'horn': 'cor', 'french_horn': 'cor', 'frenchhorn': 'cor', 'corno': 'cor',
  'flugelhorn': 'bugle', 'flugel': 'bugle', 'flugel_horn': 'bugle', 'bugle': 'bugle', 'petit_bugle': 'petit_bugle', 'piccolo': 'piccolo',
  'euphonium': 'euphonium', 'euphononium': 'euphonium',
  'sax': 'saxophone', 'saxe': 'sax', 'saxophone': 'saxophone', 
  'saxophone_tenor': 'sax_tenor', 'tenor_saxophone': 'sax_tenor', 'tenor_sax': 'sax_tenor',
  'saxophone_alto': 'sax_alto', 'alto_saxophone': 'sax_alto', 'alto_sax': 'sax_alto',
  'saxophone_basse': 'sax_basse', 'bass_saxophone': 'sax_basse', 'bass_sax': 'sax_basse',
  'bb_bass_saxophone': 'sax_basse', 'Bb_Bass_saxophone': 'sax_basse',
  'saxo': 'saxophone', 'saxo_alto': 'sax_alto', 'saxo_tenor': 'sax_tenor',
  'sax_soprano': 'sax_soprano', 'soprano_sax': 'sax_soprano', 'sax_alto': 'sax_alto', 'sax_tenor': 'sax_tenor', 'sax_baryton': 'sax_baryton',
  'saxbaryton': 'sax_baryton', 'saxalto': 'sax_alto', 'saxtenor': 'sax_tenor',
  'sax_bariton': 'sax_baryton', 'sax_baritone': 'sax_baryton',
  'bartitone_sax': 'sax_baryton', 'bartitone_saxophone': 'sax_baryton',
  'saxophone_baryton': 'sax_baryton', 'saxophone_baritone': 'sax_baryton', 'baritone_saxophone': 'sax_baryton', 'baritone_sax': 'sax_baryton',
  'saxe_baryton': 'sax_baryton', 'saxe_baritone': 'sax_baryton',
  'saxophone_basse': 'sax_basse', 'sax_basse': 'sax_basse',
  'bass_saxophone': 'sax_basse', 'bass_sax': 'sax_basse',
  'trombone_bass': 'trombone',
  'bass_saxophone': 'sax_basse', 'bass_sax': 'sax_basse',
  'saxophone_basse': 'sax_basse',
  'trombone_basse': 'trombone_basse', 'bass_trombone': 'trombone_basse', 'bas_trombone': 'trombone_basse',
  'trombone_a_piston': 'trombone',
  'bass_guitar': 'basse_electrique',
  'cornet_trumpet': 'cornet',
  'trumpet_cornet': 'cornet',
  'trumpet': 'trompette', 'trompette': 'trompette',
  'xylophone': 'xylophone', 'vibraphone': 'vibraphone',
  'glockenspiel': 'glockenspiel',
  'bass_saxophone': 'saxophone_basse', 'saxophone_basse': 'saxophone_basse',
  'bass_drum': 'grosse_caisse', 'grosse_caisse': 'grosse_caisse', 'grossecaisse': 'grosse_caisse',
  'tuba_basse': 'tuba',
  'bass_tuba': 'tuba',
  'maillet_percussion': 'malette_percussion', 'mallet_percussion': 'malette_percussion', 'malette_percussion': 'malette_percussion',
  'bombardon': 'bombardon',
  'percussion': 'percussion', 'drums': 'batterie', 'drum': 'batterie',
  'timbales': 'timbales', 'timbale': 'timbales',
  'cymbale': 'cymbale', 'cymbales': 'cymbale', 'cymbal': 'cymbale',
  'tambour': 'tambour', 'tambour_de_basque': 'tambour_de_basque', 'tom': 'tom', 'tom_grave': 'tom_grave',
  'triangle': 'triangle',
  'caisse_claire': 'caisse_claire', 'caisses_claires': 'caisse_claire', 'caisseclaire': 'caisse_claire',
  'timpani': 'timbales', 'timbales': 'timbales',
  'gong': 'gong', 'tam_tam': 'tam_tam', 'wind_chimes': 'carillon', 'windchimes': 'carillon', 'carillon_a_vent': 'carillon_a_vent',
  'woodblock': 'woodblock', 'claves': 'claves', 'batterie': 'batterie', 'maracas': 'maracas',
  'conducteur': 'conducteur', 'conducteur_bois': 'conducteur_bois', 'conducteur_cuivres': 'conducteur_cuivres', 'conducteur_cordes': 'conducteur_cordes',
  'chant': 'chant', 'en_attente': 'en_attente',
};

  const variantes = ['tenor', 'soprano', 'basse'];
  // Only unambiguous full names: sib, mib, fa, do (la is too ambiguous - matches "la" in titles)
  const tonalitesLong = { 'sib': 'sib', 'mib': 'mib', 'fa': 'fa', 'do': 'ut', 'ut': 'ut', 'sol': 'sol', 're': 're', 're_b': 'reb', 'mi': 'mi', 'si': 'si', 'en_sib': 'sib', 'en_mib': 'mib', 'en_fa': 'fa' };
  // Only unambiguous abbreviations: bb=bb, eb=eb, plus note letters when before instrument
  const tonalitesShort = { 'bb': 'sib', 'eb': 'mib', 'f': 'fa', 'c': 'ut' };
  const clefs = { 'clefsol': 'clesol', 'clesol': 'clesol', 'clefa': 'clefa', 'cle_ut': 'cle_ut', 'tc': 'tc' };

function removeAccents(str) {
  return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}

function extractInstrument(parts) {
  for (let i = 0; i < parts.length; i++) {
    for (let len = 3; len >= 1; len--) {
      if (i + len <= parts.length) {
        const combined = parts.slice(i, i + len).join('_');
        if (instruments[combined]) {
          return { name: instruments[combined], endIndex: i + len };
        }
      }
    }
  }
  return null;
}

function normalizeFilename(filename, forcedPart = null) {
  let originalName = filename;
  let name = filename.replace(/\.pdf$/i, '');
  
  // Remove "part_X_" prefix if present (not a real part number)
  name = name.replace(/^part_\d+_/i, '');
  
  // Preprocessing: replace bugle_b, bugle_c to avoid being detected as tonalities
  // b = sib (si bémol), c = ut (do)
  let bugleTonalite = null;
  if (/bugle_b/i.test(name)) {
    bugleTonalite = 'sib';
    name = name.replace(/bugle_b/gi, 'BUGLETONALITY');
  } else if (/bugle_c/i.test(name)) {
    bugleTonalite = 'ut';
    name = name.replace(/bugle_c/gi, 'BUGLETONALITY');
  }
  
  let voice = '';
  
  // Extraire voix (A1, A2, B, C, etc.) AVANT extraction de partie
  // Pattern like _A1, _A2 at end - EXCEPT when part of "a_default/a_defaut" or when it's an instrument (cor, etc.)
  // ALSO skip if the letter is a tonality (c=C/ut, f=F/fa, etc.)
  const tonaliteLetters = ['c', 'f']; // Letters that are tonalities, not voices
  const voiceMatchOriginal = name.match(/_([A-Za-z])(\d+)$/);
  const instrumentLetters = ['cor', 'b', 'c']; // Letters that are also instrument names or bugle tonalities
  if (voiceMatchOriginal && !name.toLowerCase().includes('default') && !name.toLowerCase().includes('defaut') && !instrumentLetters.includes(voiceMatchOriginal[1].toLowerCase()) && !tonaliteLetters.includes(voiceMatchOriginal[1].toLowerCase())) {
    voice = voiceMatchOriginal[1].toLowerCase();
    name = name.replace(/_[A-Za-z]\d+$/, '_' + voiceMatchOriginal[2]);
  } else {
    // Pattern like _B at end (followed by & or nothing) - EXCEPT when part of "a_default/a_defaut" or when it's an instrument or tonality
    const voiceOnlyMatch = name.match(/_([A-Za-z])(?:&|_|$)/);
    if (voiceOnlyMatch && voiceOnlyMatch[1].length === 1 && !name.toLowerCase().includes('default') && !name.toLowerCase().includes('defaut') && !instrumentLetters.includes(voiceOnlyMatch[1].toLowerCase()) && !tonaliteLetters.includes(voiceOnlyMatch[1].toLowerCase())) {
      voice = voiceOnlyMatch[1].toLowerCase();
      name = name.replace(/_[A-Za-z](?:&|_|$)/, '');
    }
  }
    
   // Detect & for multiple instruments (not for part numbers like 1&2)
   // Check if & appears between letters (instruments), not between digits
   // Can have underscores around & or not (like "Baritone_&_Tuba" or "Basse&Contrebasse")
   // More strict: & must be between actual words (not between a digit and a letter)
   const hasAmpersandBetweenInstruments = name.match(/[a-zA-Z]+[\s_-]*&[\s_-]*[a-zA-Z]+/);
   const hasAmpersand = !!hasAmpersandBetweenInstruments;
  
  // Also check for multiple instruments without & (like "Bb_Baritone_1_Bb_Tenor_Tuba_1")
  // This is indicated by two different tonalities in the name
  // More strict: first tonality must be at start or after separator, second must be after some content
  const hasDualTonalitiesNoAmpersand = name.match(/^(Bb|Eb|F|Ut)_.+_(Bb|Eb|F|Ut)_/i);
  const hasDualInstrumentsNoAmpersand = !!hasDualTonalitiesNoAmpersand;
   
  // Also check for & between digits (parts like 1&2, 2&3)
  const hasAmpersandBetweenDigits = name.match(/\d+&\d+/);
  
  // Initialize segments - will be set in the segment processing block below
  let segments;
     
  // Traiter les parties combinées (1&2, 3&4) - AVANT extraction de partie
  // Only for single instrument files (no & between instruments)
  let combinedPart = '';
  if (!hasAmpersand && hasAmpersandBetweenDigits) {
    const combinedMatch = name.match(/(\d+)&(\d+)/);
    if (combinedMatch) {
      combinedPart = `${combinedMatch[1]}_${combinedMatch[2]}`;
      name = name.replace(/\d+&\d+/, '');
      name = name.replace(/_+/g, '_').replace(/^_+|_+$/g, '');
    }
  }
   
  // Extraire numéro de partie (milieu ou fin)
  // Si forcedPart est fourni (cas version_X), l'utiliser à la place
  let partie = forcedPart || '1';
     
  // If filename starts with a number followed by underscore (like "1_Clarinette"), 
  // this is likely a version/alternate prefix, NOT a part number. Strip it.
  if (!forcedPart && name.match(/^\d+_/)) {
    name = name.replace(/^\d+_/, '');
  }
  
  // Remove "_page_X" patterns (file errors, not part numbers)
  if (!forcedPart && name.match(/_page_\d+/i)) {
    name = name.replace(/_page_\d+/gi, '');
  }
  
  // Also remove patterns like _page_X (with different underscore patterns)
  if (!forcedPart && name.match(/_page\d+/i)) {
    name = name.replace(/_page\d+/gi, '');
  }
  
  if (!forcedPart) {
    // Chercher d'abord un pattern _X_ au milieu (ex: Trombone_1_Sib)
    // But don't remove if what follows looks like a tonality (C, F, Bb, Eb, etc.)
    const middleMatch = name.match(/_(\d+)_([^_])/);
    if (middleMatch) {
      const potentialTonality = middleMatch[2].toLowerCase();
      // Check if this is a tonality abbreviation
      const isTonality = tonalitesLong[potentialTonality] || tonalitesShort[potentialTonality];
      if (!isTonality) {
        // Normal part, extract and remove
        partie = middleMatch[1];
        const regex = new RegExp('_' + partie + '_', 'g');
        name = name.replace(regex, '_');
      } else {
        // This is actually a tonality after the part number
        // Keep the tonality in the name for later extraction
        // But record the partie number
        partie = middleMatch[1];
      }
    } else {
      // Chercher à la fin
      const partieMatch = name.match(/_(\d+)$/);
      if ( partieMatch) {
        partie = partieMatch[1];
        name = name.slice(0, -partieMatch[0].length);
      }
    }
  }
   
  // "solo" = partie speciale - check for number after solo
  let soloPart = null;
  const soloMatch = name.toLowerCase().match(/solo[_\s]*(\d+)/i);
  if (soloMatch) {
    soloPart = 'solo_' + soloMatch[1];
  } else if (name.toLowerCase().includes('solo')) {
    soloPart = 'solo';
  }
  
  // Détecter clef - only if not already handled as clefa/clesol part
  let clef = '';
  const lowerName = name.toLowerCase();
  if (!soloPart) {
    for (const [k, v] of Object.entries(clefs)) {
      if (lowerName.includes(k)) {
        clef = v;
        name = name.replace(new RegExp(k, 'gi'), '');
        break;
      }
    }
  }
     
  // Split into segments for multiple instruments
  // Case 1: has & between instruments
  // Case 2: has dual tonalities indicating multiple instruments (like "Bb_Baritone_1_Bb_Tenor_Tuba_1")
  if (hasAmpersand || hasDualInstrumentsNoAmpersand) {
    let rawSegments;
    if (hasAmpersand) {
      // Split by & but keep instrument groups together
      rawSegments = name.split(/&/).map(s => s.trim().replace(/^_+|_+$/g, '')).filter(s => s);
    } else {
      // Split by second tonality - find position of second tonality pattern
      // Look for pattern like _Bb_ or _Eb_ etc. after the first tonality
      const secondTonalityMatch = name.match(/[_-](Bb|Eb|F|Ut)[_-]/i);
      if (secondTonalityMatch) {
        const splitPos = secondTonalityMatch.index;
        const firstPart = name.substring(0, splitPos);
        const secondPart = name.substring(splitPos + 1);
        rawSegments = [firstPart, secondPart].map(s => s.trim().replace(/^_+|_+$/g, '')).filter(s => s);
      } else {
        rawSegments = [name];
      }
    }
    
    // Process each segment to extract instrument-specific info
    segments = rawSegments;
  }

  // Default segments if not set
  if (!segments) {
    segments = [name];
  }
    
  // Extraire instruments de chaque segment
  const foundInstruments = [];
  const foundTonalites = [];
  const foundVariantes = [];
  const foundParties = [];
  let defaultInstrument = null;
  
  // Extraire l'instrument qui suit "default" ou "defaut" (pour "a_default_cor" -> extraire "cor")
  // ET retire cette partie du nom pour éviter les doublons
  const defaultMatch = name.match(/_?a_?defaut(?:l)?_?([a-zA-Z]+)/i);
  if (defaultMatch) {
    const defaultInstr = defaultMatch[1].toLowerCase();
    const mappedInstr = instruments[defaultInstr];
    defaultInstrument = mappedInstr || defaultInstr;
    // Retirer la partie "a_defaut_..." du nom pour éviter les doublons
    name = name.replace(/_?a_?defaut(?:l)?_?[a-zA-Z]+/i, '');
  }
  
  for (const segment of segments) {
    const parts = segment.split('_').filter(p => p && p !== 'et');
     
    // Chercher TOUS les instruments (pas juste le premier)
    const seenInstruments = new Set();
    const usedIndices = new Set();
    
    // First pass: check 3-word combinations
    for (let i = 0; i < parts.length; i++) {
      if (usedIndices.has(i)) continue;
      if (i + 3 <= parts.length) {
        const combined3 = parts.slice(i, i + 3).join('_').toLowerCase();
        if (instruments[combined3]) {
          foundInstruments.push(instruments[combined3]);
          seenInstruments.add(instruments[combined3]);
          usedIndices.add(i); usedIndices.add(i+1); usedIndices.add(i+2);
        }
      }
    }
    // Second pass: check 2-word combinations
    for (let i = 0; i < parts.length; i++) {
      if (usedIndices.has(i)) continue;
      if (i + 2 <= parts.length) {
        const combined2 = parts.slice(i, i + 2).join('_').toLowerCase();
        if (instruments[combined2]) {
          foundInstruments.push(instruments[combined2]);
          seenInstruments.add(instruments[combined2]);
          usedIndices.add(i); usedIndices.add(i+1);
        }
      }
    }
    // Third pass: check single words
    for (let i = 0; i < parts.length; i++) {
      if (usedIndices.has(i)) continue;
      const lowerPart = parts[i].toLowerCase();
      if (instruments[lowerPart]) {
        foundInstruments.push(instruments[lowerPart]);
        seenInstruments.add(instruments[lowerPart]);
      }
    }
    
    // Chercher tonalité - prioritize full names (sib, mib, fa), then abbreviations only at start
    for (let i = 0; i < parts.length; i++) {
      const part = parts[i];
      const lowerPart = part.toLowerCase();
      
      // First check for explicit full tonality names (exact match, any position)
      let found = false;
      for (const [key, val] of Object.entries(tonalitesLong)) {
        if (lowerPart === key) {
          // Only allow duplicates for multi-instrument files
          if (foundInstruments.length >= 2 || !foundTonalites.includes(val)) {
            foundTonalites.push(val);
          }
          found = true;
          break;
        }
      }
      
      // Check abbreviations - only if not already found AND avoid duplicates with long forms
      if (!found) {
        for (const [key, val] of Object.entries(tonalitesShort)) {
          if (lowerPart === key) {
            // Only allow duplicates for multi-instrument files
            if (foundInstruments.length >= 2 || !foundTonalites.includes(val)) {
              foundTonalites.push(val);
            }
            found = true;
            break;
          }
        }
      }
      
      // Also check for tonality + number combo (like F1, Bb2, Eb3)
      // This handles cases like "French_Horn_F1" where F1 is not split
      if (!found) {
        const tonalityWithNumber = part.match(/^([A-Za-z]+)(\d+)$/);
        if (tonalityWithNumber) {
          const tonality = tonalityWithNumber[1].toLowerCase();
          const num = tonalityWithNumber[2];
          // Check if the letter part is a tonality
          for (const [key, val] of Object.entries(tonalitesShort)) {
            if (tonality === key) {
              if (!foundTonalites.includes(val)) {
                foundTonalites.push(val);
              }
              // Also extract the part number
              if (!foundParties.includes(num)) {
                foundParties.push(num);
              }
              found = true;
              break;
            }
          }
        }
      }
    }
    
    // Extraire partie de ce segment
    for (const part of parts) {
      if (/^\d+$/.test(part)) {
        foundParties.push(part);
      }
    }
    
    // Chercher variante
    for (const part of parts) {
      if (variantes.includes(part) && !foundVariantes.includes(part)) {
        foundVariantes.push(part);
      }
    }
    
    // Chercher qualificatifs spéciaux - TOUJOURS garder ces qualificatifs pour éviter les doublons
    const qualifiers = ['solo', 'tutti', 'petite', 'grande', 'bc', 'tc', 'i', 'ii', 'mini', 'junior'];
    for (const part of parts) {
      const lowerPart = part.toLowerCase();
      for (const q of qualifiers) {
        // Skip "solo" if we're already using it as soloPart
        if (q === 'solo' && soloPart) continue;
        // For short qualifiers (i, ii), use exact match only; for longer ones, allow partial
        if (q.length <= 2) {
          if (lowerPart === q) {
            if (!foundVariantes.includes(q)) {
              foundVariantes.push(q);
            }
          }
        } else {
          if (lowerPart === q || lowerPart.includes(q)) {
            if (!foundVariantes.includes(q)) {
              foundVariantes.push(q);
            }
          }
        }
      }
    }
  }

  // Construire le résultat - Interleave instruments with their tonalities and parties for multi-instrument files
  let result;
  
  if (foundInstruments.length >= 2 && foundTonalites.length >= 2) {
    // Multiple instruments with separate tonalities: interleave them
    // Format: instrument1-tonality1-clef1-part1+instrument2-tonality2-clef2-part2
    const segmentResults = [];
    for (let i = 0; i < foundInstruments.length; i++) {
      let seg = foundInstruments[i];
      if (foundTonalites[i]) seg += '-' + foundTonalites[i];
      // Add clef before partie
      if (clef) seg += '-' + clef;
      // Add party - always add at least 1 for multi-instrument
      const party = foundParties[i] || '1';
      seg += '-' + party;
      segmentResults.push(seg);
    }
    result = segmentResults.join('+');
    
    // Convert French "defaut" to English "default"
    result = result.replace(/defaut/g, 'default');
    result = result.toLowerCase();
    return result + '.pdf';
  }
  
  // Original logic for single instrument or cases without separate tonalities
  if (foundInstruments.length >= 2) {
    // Several instruments - interleave with parties if we have them
    const segmentResults = [];
    for (let i = 0; i < foundInstruments.length; i++) {
      let seg = foundInstruments[i];
      // Add tonality: if each instrument has its own (foundTonalites.length >= foundInstruments.length), use that
      // Otherwise if there's one tonality, use it for all
      if (foundTonalites.length >= foundInstruments.length && foundTonalites[i]) {
        seg += '-' + foundTonalites[i];
      } else if (foundTonalites.length === 1 && foundTonalites[0]) {
        seg += '-' + foundTonalites[0];
      }
      // Add party for this segment - always add at least 1 for multi-instrument
      const party = foundParties[i] || '1';
      seg += '-' + party;
      segmentResults.push(seg);
    }
    result = segmentResults.join('+');
  } else if (foundInstruments.length === 1) {
    result = foundInstruments[0];
  } else {
    // Fallback : utiliser le nom nettoyé
    const fallbackParts = name.split('_').filter(p => p && p.length > 2);
    result = fallbackParts[0] || 'inconnu';
  }
   
  // Skip tonality addition for multi-instrument case (already handled above)
  const skipTonality = foundInstruments.length >= 2;
    
  // Ajouter variantes AVANT tonalité
  const uniqueVariantes = foundVariantes.filter(v => {
    if (result.includes(v)) return false;
    return !foundInstruments.some(instr => instr === v || instr.includes('-' + v));
  });
  if (uniqueVariantes.length > 0) {
    result += '-' + uniqueVariantes.join('-');
  }
     
  // ALWAYS include tonalité if found (even if only one instrument) - skip for multi-instr
  if (!skipTonality && foundTonalites.length > 0) {
    result += '-' + foundTonalites.join('-');
  }
  
  // Ajouter "default" et l'instrument qui suit (ex: default-cor)
  if (defaultInstrument) {
    result += '-default-' + defaultInstrument;
  }

  // Skip adding parties for multi-instrument (already added in segment building)
  const isMultiInstrument = foundInstruments.length >= 2;

  // Add clef BEFORE parte (according to spec: instrument-tonalité-clef-partie)
  if (clef && !isMultiInstrument) {
    result += '-' + clef;
  }

  // Utiliser les parties des segments si plusieurs instruments OU si on a des parties collectées (cas tonalité+numéro comme F1, F2)
  if (isMultiInstrument) {
    // Already handled in segment building above
  } else if (foundParties.length > 0) {
    result += '-' + foundParties.join('-');
  } else if (combinedPart) {
    result += '-' + (voice ? voice : '') + combinedPart;
  } else if (soloPart) {
    // If soloPart has number (solo_1, solo_2), keep it; otherwise add parte number with separator
    if (soloPart.includes('_')) {
      result += '-' + soloPart;
    } else {
      result += '-' + soloPart + '-' + partie;
    }
  } else if (voice) {
    result += '-' + voice + partie;
  } else {
    result += '-' + partie;
  }

  // Convert French "defaut" to English "default"
  result = result.replace(/defaut/g, 'default');
  
  // Add bugle tonality if present
  if (bugleTonalite) {
    if (result.includes('BUGLETONALITY')) {
      result = result.replace('BUGLETONALITY', 'bugle-' + bugleTonalite);
    }
  }
  
  // Always lowercase the result
  result = result.toLowerCase();
  
  return result + '.pdf';
}

// Collecter tous les changements
// Structure: NUM/PIECE/INSTRUMENT/VERSION/PARTIE
const changes = [];
const scores = fs.readdirSync(scoresPath).filter(d => {
  const p = path.join(scoresPath, d);
  return fs.statSync(p).isDirectory() && /^\d+$/.test(d);
});

for (const scoreId of scores) {
  const scorePath = path.join(scoresPath, scoreId);
  const pieceDirs = fs.readdirSync(scorePath).filter(d => {
    const p = path.join(scorePath, d);
    return fs.statSync(p).isDirectory() && d !== 'score.ini';
  });
  
  for (const pieceId of pieceDirs) {
    const piecePath = path.join(scorePath, pieceId);
    const instrDirs = fs.readdirSync(piecePath).filter(d => {
      const p = path.join(piecePath, d);
      return fs.statSync(p).isDirectory();
    });
    
    for (const instrId of instrDirs) {
      const instrPath = path.join(piecePath, instrId);
      const entries = fs.readdirSync(instrPath);
      const versionDirs = entries.filter(e => /^\d+$/.test(e));
      
      for (const versionDir of versionDirs) {
        const versionPath = path.join(instrPath, versionDir);
        if (!fs.statSync(versionPath).isDirectory()) continue;
        
        const files = fs.readdirSync(versionPath).filter(f => f.toLowerCase().endsWith('.pdf'));
        
        for (const file of files) {
          // Pour les fichiers "version_X", utiliser le répertoire comme partie SEULEMENT si pas de numéro de partie dans le nom (après l'instrument)
          const isVersion = file.toLowerCase().startsWith('version');
          let forcedPart = null;
          let fileForNormalization = file;
          
          if (isVersion) {
            // Enlever le prefixe version_X_ pour le check (sans le .pdf)
            const nameWithoutVersion = file.replace(/\.pdf$/i, '').replace(/^version_\d+_/i, '');
            // Chercher un numéro de partie SEULEMENT à la fin du nom (après l'instrument)
            const hasPartAfterInstrument = nameWithoutVersion.match(/_(\d+)$/);
            if (!hasPartAfterInstrument) {
              forcedPart = versionDir;
            }
            // Enlever le prefixe version_X_ pour le traitement (garder le .pdf)
            fileForNormalization = file.replace(/^version_\d+_/i, '');
          }
          
          const newName = normalizeFilename(fileForNormalization, forcedPart);
          changes.push({
            oldPath: path.join(versionPath, file),
            newPath: path.join(piecePath, instrId, versionDir, newName),
            oldName: file,
            newName,
            scoreId,
            pieceId,
            instrId,
            versionDir
          });
        }
      }
    }
  }
}

// Gérer les conflits
const usedNames = new Set();
for (const change of changes) {
  let finalName = change.newName;
  let counter = 1;
  
  while (usedNames.has(change.newPath)) {
    const base = change.newName.replace('.pdf', '');
    finalName = `${base}_alt${counter}.pdf`;
    change.newPath = path.join(scoresPath, change.scoreId, change.pieceId, change.instrId, change.versionDir, finalName);
    counter++;
  }
  
  usedNames.add(change.newPath);
  change.finalName = finalName;
}

// Validate against spec: {instrument}[-{variante}][-{tonalité}][-{clef}]{-partie}
// Multi: instrument1[-variante1][-tonalité1][-clef1]{-partie1}+instrument2...
// Combined parts: instrument-...-X_Y
function validateSpec(filename) {
  const base = filename.replace('.pdf', '');
  
  // Multi-instrument: split by +
  if (base.includes('+')) {
    const parts = base.split('+');
    for (const part of parts) {
      const subparts = part.split('-');
      if (subparts.length < 1) return false;
      
      const instrument = subparts[0];
      if (!instrument || !/^[a-z_]+$/.test(instrument)) return false;
      
      // Check each segment doesn't have duplicates
      for (let i = 0; i < subparts.length - 1; i++) {
        if (subparts[i] === subparts[i+1]) return false;
      }
    }
    return true;
  }
  
  // Check if it's combined parts (single instrument with 1_2)
  // Pattern: instrument-...-X_Y where X and Y are numbers
  const combinedPartsMatch = base.match(/^([a-z_]+).*-(\d+)_(\d+)(-.+)?$/);
  if (combinedPartsMatch) {
    const instrument = combinedPartsMatch[1];
    if (!instrument || !/^[a-z_]+$/.test(instrument)) return false;
    return true;
  }
  
  // Single instrument: must start with valid instrument name
  const firstPart = base.split('-')[0];
  if (!firstPart || !/^[a-z_]+$/.test(firstPart)) return false;
  
  // Check for duplicates
  const comps = base.split('-');
  for (let i = 0; i < comps.length - 1; i++) {
    if (comps[i] === comps[i+1]) return false;
  }
  
  return true;
}

// Validate all generated names
const invalidNames = [];
for (const c of changes) {
  if (!validateSpec(c.finalName)) {
    invalidNames.push(c);
  }
}

// Afficher résultats
console.log(`Total fichiers : ${changes.length}\n`);

if (invalidNames.length > 0) {
  console.log(`⚠️ ${invalidNames.length} noms ne collent pas à la spec:\n`);
  for (const inv of invalidNames.slice(0, 20)) {
    console.log(`  ${inv.oldName} → ${inv.finalName}`);
  }
  if (invalidNames.length > 20) {
    console.log(`  ... et ${invalidNames.length - 20} autres`);
  }
  console.log('');
}

// Afficher exemples
console.log('=== EXEMPLES DE CONVERSIONS ===\n');
const uniquePatterns = [];
const seen = new Set();

for (const c of changes) {
  const pattern = `${c.oldName} → ${c.finalName}`;
  if (!seen.has(pattern) && uniquePatterns.length < 30) {
    seen.add(pattern);
    uniquePatterns.push(c);
  }
}

for (const ex of uniquePatterns) {
  console.log(`${ex.oldName}`);
  console.log(`  → ${ex.finalName}\n`);
}

// Générer les fichiers CSV
const conversionComplete = [];
const duplicates = [];
const nameToFiles = new Map();

for (const c of changes) {
  const key = `${c.scoreId}/${c.instrId}/${c.numDir}/${c.finalName}`;
  if (!nameToFiles.has(key)) {
    nameToFiles.set(key, []);
  }
  nameToFiles.get(key).push(c);
}

for (const [key, files] of nameToFiles) {
  const [scoreId, instrId, numDir, finalName] = key.split('/');
  const oldPaths = files.map(f => f.oldName).join('; ');
  
  if (files.length === 1) {
    conversionComplete.push(`${files[0].oldPath};${finalName}`);
  } else {
    for (const f of files) {
      duplicates.push(`${f.oldPath};${finalName}`);
    }
  }
}

fs.writeFileSync(path.join(__dirname, 'conversion_complete.csv'), conversionComplete.join('\n'));
fs.writeFileSync(path.join(__dirname, 'duplicates.csv'), duplicates.join('\n'));

console.log(`\nFichiers générés:`);
console.log(`- conversion_complete.csv: ${conversionComplete.length} fichiers`);
console.log(`- duplicates.csv: ${duplicates.length} fichiers (doublons)`);

// Exécuter si confirmé
if (confirmed) {
  console.log('\nExécution...\n');
  let success = 0;
  let errors = 0;
  let removedDirs = 0;
  
  for (const change of changes) {
    try {
      fs.renameSync(change.oldPath, change.newPath);
      success++;
    } catch (err) {
      console.error(`❌ ${change.oldName} : ${err.message}`);
      errors++;
    }
  }
  
  // Supprimer répertoires vides
  for (const scoreId of scores) {
    const scorePath = path.join(scoresPath, scoreId);
    const instrDirs = fs.readdirSync(scorePath).filter(d => {
      const p = path.join(scorePath, d);
      return fs.statSync(p).isDirectory() && d !== 'score.ini';
    });
    
    for (const instrId of instrDirs) {
      const instrPath = path.join(scorePath, instrId);
      const numDirs = fs.readdirSync(instrPath).filter(e => /^\d+$/.test(e));
      
      for (const numDir of numDirs) {
        const numPath = path.join(instrPath, numDir);
        try {
          const remaining = fs.readdirSync(numPath);
          if (remaining.length === 0) {
            fs.rmdirSync(numPath);
            removedDirs++;
          }
        } catch (e) {}
      }
    }
  }
  
  console.log(`\n✅ Terminé !`);
  console.log(`Fichiers convertis : ${success}`);
  console.log(`Erreurs : ${errors}`);
  console.log(`Répertoires supprimés : ${removedDirs}`);
} else {
  console.log('\nPour confirmer la conversion :');
  console.log(`node scripts/convert_final_v2.js ${scoresPath} confirm`);
}
