/**
 * Cyber-Assistant - Module de migration et compatibilité legacy
 *
 * Détecte la version des fichiers importés, analyse les champs,
 * génère un rapport de migration et applique les transformations nécessaires.
 *
 * Ce module est optionnel : si absent, l'import standard fonctionne sans changement.
 *
 * SÉCURITÉ : Toutes les données utilisateur sont échappées via escSafe()
 * (qui délègue à window.escapeHtml) avant injection dans le DOM.
 * Ce pattern est identique à showConfirmModal() dans bundle.js.
 */
(function() {
  'use strict';

  // i18n helper — delegates to the global CSI_i18n.t() when available
  function _t(key, fallback) {
    return window.CSI_i18n && window.CSI_i18n.t ? window.CSI_i18n.t(key) : (fallback || key);
  }

  // ============================================================
  // Constantes
  // ============================================================

  /**
   * Map de migration des valeurs de priorité (anciennes -> actuelles)
   */
  var PRIORITY_MIGRATION = {
    'normale': 'medium',
    'haute': 'high',
    'basse': 'low',
    'critique': 'critical'
  };

  /**
   * Priorités valides dans la version actuelle
   */
  var VALID_PRIORITIES = ['critical', 'high', 'medium', 'low'];

  /**
   * Registre des versions connues avec leurs caractéristiques
   */
  var VERSION_REGISTRY = {
    '1.0': {
      label: 'Cyber-Assistant v1.0',
      exportVersionField: '1.1',
      expectedFields: [
        'controls', 'actions', 'risks', 'nonconformities', 'audits',
        'kpis', 'reviews', 'stakeholders', 'documents', 'nis2Controls',
        'soa', 'targetMaturity', 'nextActionId', 'title', 'exportDate', 'version'
      ],
      addedInLater: [
        'frameworkMapper', 'kpiHistory', 'settings', 'cyfunLevel',
        'cyfunLang', '_cyfunLevelChosen', 'actionsHistory', 'auditLog',
        'criticalAssets', 'criticalAssetsMeta', 'projectRisks',
        'soclePillars', 'riskAnalyses', 'impactMatrices', 'riskThresholds',
        'nis2Plan', 'threats', 'documentReview'
      ]
    },
    '1.2': {
      label: 'Cyber-Assistant v1.2',
      exportVersionField: '1.2',
      expectedFields: [
        'controls', 'actions', 'criticalAssets', 'criticalAssetsMeta',
        'projectRisks', 'risks', 'threats', 'nonconformities', 'audits',
        'kpis', 'reviews', 'stakeholders', 'documents', 'documentReview',
        'nis2Controls', 'nis2Plan', 'soclePillars', 'targetMaturity', 'soa',
        'riskAnalyses', 'impactMatrices', 'riskThresholds', 'frameworkMapper',
        'kpiHistory', 'settings', 'nextActionId', 'title', 'cyfunLevel',
        'cyfunLang', '_cyfunLevelChosen', 'actionsHistory', 'auditLog',
        'exportDate', 'version'
      ],
      addedInLater: []
    },
    '2.0': {
      label: 'Cyber-Assistant v2.0 (actuelle)',
      exportVersionField: '2.0',
      expectedFields: [
        'controls', 'actions', 'criticalAssets', 'criticalAssetsMeta',
        'projectRisks', 'risks', 'threats', 'nonconformities', 'audits',
        'kpis', 'reviews', 'stakeholders', 'documents', 'documentReview',
        'nis2Controls', 'nis2Plan', 'soclePillars', 'targetMaturity', 'soa',
        'riskAnalyses', 'impactMatrices', 'riskThresholds', 'frameworkMapper',
        'kpiHistory', 'settings', 'nextActionId', 'title', 'cyfunLevel',
        'cyfunLang', '_cyfunLevelChosen', 'actionsHistory', 'auditLog',
        // FIX-LEGACY-CATALOG-RECYF (audit 2026-06-10) : champs ReCyF + verrou CyFun
        // presents dans tout export courant (bundle.js exportData).
        'recyfControls', 'recyfLevel', 'nis2Referential', '_cyfunLevelLocked',
        '_nis2ReferentialLocked',
        'exportDate', 'version'
      ],
      addedInLater: []
    }
  };

  /**
   * Catalogue complet des champs attendus en v2.0 avec descriptions et defaults
   */
  var FIELD_CATALOG = {
    controls:          { type: 'array',  label: 'Contrôles ISO 27002',         default_: [] },
    actions:           { type: 'array',  label: 'Plan d\'actions',             default_: [] },
    risks:             { type: 'array',  label: 'Risques SMSI',               default_: [] },
    threats:           { type: 'array',  label: 'Menaces',                     default_: [] },
    nonconformities:   { type: 'array',  label: 'Non-conformités',            default_: [] },
    audits:            { type: 'array',  label: 'Audits',                      default_: [] },
    kpis:              { type: 'array',  label: 'KPI',                         default_: [] },
    reviews:           { type: 'array',  label: 'Revues de direction',         default_: [] },
    stakeholders:      { type: 'array',  label: 'Parties prenantes',           default_: [] },
    documents:         { type: 'array',  label: 'Documents',                   default_: [] },
    documentReview:    { type: 'array',  label: 'Revue normative',             default_: [] },
    criticalAssets:    { type: 'array',  label: 'Actifs critiques',           default_: [] },
    criticalAssetsMeta:{ type: 'object', label: 'Métadonnées actifs',          default_: {} },
    projectRisks:      { type: 'array',  label: 'Risques projets',            default_: [] },
    riskAnalyses:      { type: 'array',  label: 'Analyses de risques',        default_: [] },
    impactMatrices:    { type: 'object', label: 'Matrices d\'impacts',         default_: null },
    riskThresholds:    { type: 'object', label: 'Seuils de risque',            default_: null },
    nis2Controls:      { type: 'array',  label: 'Mesures NIS2',               default_: [] },
    nis2Plan:          { type: 'object', label: 'Programme NIS2',              default_: null },
    soclePillars:      { type: 'array',  label: 'Socle de sécurité',          default_: [] },
    soa:               { type: 'array',  label: 'Déclaration d\'applicabilité',default_: [] },
    targetMaturity:    { type: 'object', label: 'Maturité cible',              default_: { iso27002: 3, nis2: {}, nis2Target: 0 } },
    frameworkMapper:   { type: 'object', label: 'Mapping transversal',         default_: (function() { var fws = window._csiFrameworks || {}; var id = fws['iso27002'] ? 'iso27002' : 'open-security-controls'; return { enabledFrameworks: [id, 'nis2'], customMappings: [], userNotes: {} }; })() },
    kpiHistory:        { type: 'object', label: 'Historique KPI',              default_: {} },
    settings:          { type: 'object', label: 'Paramètres',                  default_: {} },
    actionsHistory:    { type: 'object', label: 'Historique actions',           default_: {} },
    auditLog:          { type: 'array',  label: 'Journal d\'audit',            default_: [] },
    nextActionId:      { type: 'number', label: 'Prochain ID action',          default_: 0 },
    title:             { type: 'string', label: 'Titre',                       default_: 'Cyber-Assistant' },
    // GOV-NIS2 S5 (audit 2026-08-20) : noMigrateInject — un fichier partiel sans niveau
    // CyFun rétrogradait silencieusement l'espace cible en 'basic' à l'import.
    cyfunLevel:        { type: 'string', label: 'Niveau CyFun',               default_: 'basic', noMigrateInject: true },
    cyfunLang:         { type: 'string', label: 'Langue CyFun',               default_: 'fr' },
    _cyfunLevelChosen: { type: 'boolean',label: 'CyFun choisi',               default_: false, noMigrateInject: true },
    // FIX-LEGACY-CATALOG-RECYF (audit 2026-06-10) : champs ReCyF (NIS2 France) et
    // verrou CyFun reellement importes par applyImportedData (bundle.js) mais
    // absents du catalogue — le rapport pre-import les declarait a tort
    // « Sera ignoré » a chaque reimport d'un export courant.
    // FIX-LEGACY-NOINJECT (revue audit 2026-06-11) : noMigrateInject empêche migrateData
    // d'injecter un défaut pour ces champs (le rapport les reconnaît toujours, mais
    // applyImportedData les applique sur `!== undefined` — bundle.js:7724/7762/7763 — donc
    // les forcer EFFACERAIT le verrou de niveau CyFun et réinitialiserait le référentiel/
    // degré NIS2 de l'espace cible à l'import d'un export qui ne les porte pas).
    recyfControls:     { type: 'array',  label: 'Mesures ReCyF (NIS2 France)', default_: [], noMigrateInject: true },
    recyfLevel:        { type: 'string', label: 'Niveau ReCyF',               default_: 'importante', noMigrateInject: true },
    nis2Referential:   { type: 'string', label: 'Référentiel NIS2 actif',     default_: 'cyfun2025', noMigrateInject: true },
    _cyfunLevelLocked: { type: 'boolean',label: 'Verrou niveau CyFun',        default_: false, noMigrateInject: true },
    _nis2ReferentialLocked: { type: 'boolean', label: 'Verrou référentiel NIS2', default_: false, noMigrateInject: true }
  };

  /**
   * Champs métadonnées (exportDate, version, _integrity) — non comptés comme data
   * FIX-LEGACY-CATALOG-RECYF (audit 2026-06-10) : _cyfunEvalArchive est importé
   * quand présent (bundle.js applyImportedData) mais absent des exports courants ;
   * on le classe ici pour qu'il ne soit ni signalé « Sera ignoré » quand présent,
   * ni listé « manquant » à chaque import.
   */
  var META_FIELDS = ['exportDate', 'version', '_integrity', '_cyfunEvalArchive'];

  // ============================================================
  // Détection de version
  // ============================================================

  /**
   * Détecte la version d'un fichier importé
   * @param {object} data - Les données JSON parsées
   * @returns {{ version: string, confidence: string, label: string, details: string[] }}
   */
  function detectVersion(data) {
    if (!data || typeof data !== 'object') {
      return { version: 'unknown', confidence: 'low', label: 'Format inconnu', details: ['Données invalides'] };
    }

    var details = [];
    var version = 'unknown';
    var confidence = 'low';

    // 1. Champ version explicite
    if (data.version === '2.0') {
      version = '2.0';
      confidence = 'exact';
      details.push('Champ version = "2.0"');
    } else if (data.version === '1.2') {
      version = '1.2';
      confidence = 'exact';
      details.push('Champ version = "1.2"');
    } else if (data.version === '1.1') {
      version = '1.0';
      confidence = 'exact';
      details.push('Champ version = "1.1" (v1.0)');
    } else if (data.version) {
      details.push('Champ version = "' + data.version + '" (non reconnu)');
    }

    // 2. Détection structurelle (si version pas encore identifiée avec certitude)
    if (confidence !== 'exact') {
      var hasV12Fields = !!(data.frameworkMapper || data.cyfunLevel || data.auditLog || data.kpiHistory);
      var hasThreats = Array.isArray(data.threats) && data.threats.length > 0;
      var hasCriticalAssets = Array.isArray(data.criticalAssets) && data.criticalAssets.length > 0;

      if (hasV12Fields) {
        version = '1.2';
        confidence = 'high';
        details.push('Champs v1.2 détectés (frameworkMapper, cyfunLevel, auditLog ou kpiHistory)');
      } else if (!hasV12Fields && Array.isArray(data.controls) && data.controls.length > 0) {
        version = '1.0';
        confidence = 'high';
        details.push('Champs v1.2 absents, structure compatible v1.0');
      }
    }

    // 3. Détection par valeurs de priorité legacy
    if (Array.isArray(data.actions)) {
      var legacyPriorities = data.actions.filter(function(a) {
        return a && a.priority && PRIORITY_MIGRATION[a.priority];
      });
      if (legacyPriorities.length > 0) {
        if (version === 'unknown') version = '1.0';
        if (confidence === 'low') confidence = 'medium';
        details.push(legacyPriorities.length + ' action(s) avec priorité legacy détectée(s)');
      }
    }

    // 4. Détection par format NIS2
    if (Array.isArray(data.nis2Controls) && data.nis2Controls.length > 0) {
      var firstCtrl = data.nis2Controls[0];
      if (firstCtrl.fonction && firstCtrl.categorie && !firstCtrl.ref) {
        if (version === 'unknown') version = '1.0';
        details.push('Format NIS2 legacy (23 contrôles) détecté');
      } else if (firstCtrl.ref) {
        details.push('Format NIS2 CyFun 2025 détecté');
      }
    }

    // Résultat final
    var regEntry = VERSION_REGISTRY[version];
    var label = regEntry ? regEntry.label : 'Version inconnue (' + (data.version || 'sans version') + ')';

    return { version: version, confidence: confidence, label: label, details: details };
  }

  // ============================================================
  // Analyse dry-run
  // ============================================================

  /**
   * Analyse un fichier importé sans l'appliquer (dry-run)
   * @param {object} data - Les données JSON parsées
   * @returns {object} ImportReport
   */
  function analyzeImport(data) {
    var versionInfo = detectVersion(data);
    var report = {
      version: versionInfo,
      fieldMapping: { matched: [], missing: [], extra: [], migrated: [] },
      dataSummary: {},
      migrations: [],
      warnings: [],
      crossRefIssues: [],
      sizeEstimate: { raw: 0, fitsLocalStorage: true },
      canImport: true,
      blockingReasons: []
    };

    if (!data || typeof data !== 'object') {
      report.canImport = false;
      report.blockingReasons.push('Les données ne sont pas un objet JSON valide');
      return report;
    }

    // -- Taille estimée --
    try {
      var jsonStr = JSON.stringify(data);
      report.sizeEstimate.raw = jsonStr.length * 2; // UTF-16
      report.sizeEstimate.fitsLocalStorage = report.sizeEstimate.raw < 4.5 * 1024 * 1024;
      if (!report.sizeEstimate.fitsLocalStorage) {
        report.warnings.push({
          severity: 'high',
          message: 'Les données importées (' + formatBytes(report.sizeEstimate.raw) + ') risquent de dépasser le quota localStorage (5 MB)',
          field: '_global'
        });
      }
    } catch (e) {
      report.warnings.push({ severity: 'medium', message: 'Impossible d\'estimer la taille des données', field: '_global' });
    }

    // -- Vérification contrôles (champ requis) --
    if (!data.controls || !Array.isArray(data.controls)) {
      report.canImport = false;
      report.blockingReasons.push('Le champ "controls" est manquant ou invalide (requis)');
      return report;
    }

    // -- Field mapping --
    var dataKeys = Object.keys(data);
    var catalogKeys = Object.keys(FIELD_CATALOG);

    // Champs correspondants
    catalogKeys.forEach(function(key) {
      if (data[key] !== undefined) {
        var count = '';
        if (Array.isArray(data[key])) count = data[key].length + ' élément(s)';
        else if (data[key] && typeof data[key] === 'object') count = Object.keys(data[key]).length + ' propriété(s)';
        else count = String(data[key]).substring(0, 50);
        report.fieldMapping.matched.push({ field: key, label: FIELD_CATALOG[key].label, detail: count, status: 'ok' });
      }
    });

    // Champs manquants (dans le catalogue mais pas dans les données)
    catalogKeys.forEach(function(key) {
      if (data[key] === undefined) {
        report.fieldMapping.missing.push({
          field: key,
          label: FIELD_CATALOG[key].label,
          defaultValue: describeDefault(FIELD_CATALOG[key].default_),
          impact: 'Valeur par défaut utilisée'
        });
      }
    });

    // Champs extra (dans les données mais pas dans le catalogue)
    dataKeys.forEach(function(key) {
      if (!FIELD_CATALOG[key] && META_FIELDS.indexOf(key) === -1) {
        report.fieldMapping.extra.push({
          field: key,
          description: typeof data[key],
          impact: 'Sera ignoré'
        });
      }
    });

    // -- Migrations nécessaires --

    // Migration des priorités
    if (Array.isArray(data.actions)) {
      var priorityMigrations = {};
      data.actions.forEach(function(a) {
        if (a && a.priority && PRIORITY_MIGRATION[a.priority]) {
          var from = a.priority;
          var to = PRIORITY_MIGRATION[from];
          if (!priorityMigrations[from]) priorityMigrations[from] = { from: from, to: to, count: 0 };
          priorityMigrations[from].count++;
        }
      });
      Object.keys(priorityMigrations).forEach(function(from) {
        var m = priorityMigrations[from];
        report.migrations.push({
          type: 'priority_enum',
          field: 'actions[].priority',
          count: m.count,
          description: m.count + ' action(s) : priorité "' + m.from + '" \u2192 "' + m.to + '"'
        });
        report.fieldMapping.migrated.push({
          field: 'actions[].priority',
          from: m.from,
          to: m.to,
          description: m.count + ' valeur(s) migrée(s)'
        });
      });
    }

    // Migration SOA
    if (!Array.isArray(data.soa) && Array.isArray(data.controls)) {
      var hasLegacySoa = data.controls.some(function(c) { return c && c.selected !== undefined; });
      if (hasLegacySoa) {
        report.migrations.push({
          type: 'soa_format',
          field: 'soa',
          count: data.controls.length,
          description: 'SOA reconstruit depuis les contrôles (format legacy à 3 champs)'
        });
      }
    }

    // Migration NIS2 Plan
    if (!data.nis2Plan) {
      report.migrations.push({
        type: 'nis2_plan_init',
        field: 'nis2Plan',
        count: 1,
        description: 'Programme NIS2 initialisé avec valeurs par défaut'
      });
    }

    // -- Data summary --
    var summaryFields = [
      { key: 'controls', label: 'Contrôles ISO 27002' },
      { key: 'actions', label: 'Actions' },
      { key: 'risks', label: 'Risques SMSI' },
      { key: 'threats', label: 'Menaces' },
      { key: 'nonconformities', label: 'Non-conformités' },
      { key: 'audits', label: 'Audits' },
      { key: 'kpis', label: 'KPI' },
      { key: 'reviews', label: 'Revues de direction' },
      { key: 'stakeholders', label: 'Parties prenantes' },
      { key: 'documents', label: 'Documents' },
      { key: 'criticalAssets', label: 'Actifs critiques' },
      { key: 'projectRisks', label: 'Risques projets' },
      { key: 'nis2Controls', label: 'Mesures NIS2' },
      { key: 'soa', label: 'Entrées SOA' },
      { key: 'riskAnalyses', label: 'Analyses de risques' }
    ];
    summaryFields.forEach(function(sf) {
      var arr = data[sf.key];
      report.dataSummary[sf.key] = { label: sf.label, count: Array.isArray(arr) ? arr.length : 0 };
    });

    // -- Cross-reference check --
    if (typeof window.validateBeforeSave === 'function') {
      try {
        var validation = window.validateBeforeSave(data);
        if (validation.warnings && validation.warnings.length > 0) {
          validation.warnings.forEach(function(w) {
            report.crossRefIssues.push({ source: 'validation', message: w });
          });
        }
      } catch (e) {
        // Non bloquant
      }
    }

    // -- Integrity hash check --
    if (data._integrity) {
      try {
        var dataForHash = shallowCloneWithout(data, ['_integrity']);
        var computedHash = simpleHash(JSON.stringify(dataForHash));
        if (computedHash !== data._integrity) {
          report.warnings.push({
            severity: 'high',
            message: 'Le hash d\'intégrité ne correspond pas \u2014 le fichier a été modifié ou corrompu',
            field: '_integrity'
          });
        }
      } catch (e) {
        // Non bloquant
      }
    }

    return report;
  }

  // ============================================================
  // Migration des données
  // ============================================================

  /**
   * Applique toutes les migrations sur une copie profonde des données
   * @param {object} data - Les données JSON parsées (non mutées)
   * @returns {{ migratedData: object, log: string[] }}
   */
  function migrateData(data) {
    var log = [];

    // Deep clone
    var migrated;
    try {
      migrated = typeof structuredClone === 'function'
        ? structuredClone(data)
        : JSON.parse(JSON.stringify(data));
    } catch (e) {
      migrated = JSON.parse(JSON.stringify(data));
    }

    // 1. Migration des priorités dans les actions
    if (Array.isArray(migrated.actions)) {
      migrated.actions.forEach(function(action, i) {
        if (action && action.priority && PRIORITY_MIGRATION[action.priority]) {
          var oldVal = action.priority;
          action.priority = PRIORITY_MIGRATION[oldVal];
          log.push('actions[' + i + '].priority: "' + oldVal + '" \u2192 "' + action.priority + '"');
        }
      });
    }

    // 2. Migration des priorités dans les project risks (si applicable)
    if (Array.isArray(migrated.projectRisks)) {
      migrated.projectRisks.forEach(function(pr, i) {
        if (pr && pr.priority && PRIORITY_MIGRATION[pr.priority]) {
          var oldVal = pr.priority;
          pr.priority = PRIORITY_MIGRATION[oldVal];
          log.push('projectRisks[' + i + '].priority: "' + oldVal + '" \u2192 "' + pr.priority + '"');
        }
      });
    }

    // 3. Initialiser les champs manquants avec les valeurs par défaut
    Object.keys(FIELD_CATALOG).forEach(function(key) {
      if (migrated[key] === undefined) {
        var cat = FIELD_CATALOG[key];
        // FIX-LEGACY-NOINJECT (revue audit 2026-06-11) : ne pas matérialiser les champs
        // dont l'absence DOIT préserver l'état cible (verrou CyFun, référentiel/degré NIS2).
        if (cat.noMigrateInject) return;
        if (cat.type === 'array') {
          migrated[key] = [];
          log.push(key + ': initialisé avec []');
        } else if (cat.type === 'object' && cat.default_ !== null) {
          migrated[key] = JSON.parse(JSON.stringify(cat.default_));
          log.push(key + ': initialisé avec valeur par défaut');
        } else if (cat.type === 'string') {
          migrated[key] = cat.default_;
          log.push(key + ': initialisé avec "' + cat.default_ + '"');
        } else if (cat.type === 'number') {
          migrated[key] = cat.default_;
          log.push(key + ': initialisé avec ' + cat.default_);
        } else if (cat.type === 'boolean') {
          migrated[key] = cat.default_;
          log.push(key + ': initialisé avec ' + cat.default_);
        }
      }
    });

    return { migratedData: migrated, log: log };
  }

  // ============================================================
  // Rapport de migration (DOM-safe rendering)
  // ============================================================

  /**
   * Construit le contenu du rapport dans un conteneur DOM
   * Utilise createElement/textContent pour éviter innerHTML avec données utilisateur
   * @param {object} report - L'ImportReport de analyzeImport()
   * @param {HTMLElement} container - Élément DOM parent
   */
  function renderMigrationReport(report, container) {
    // Helper: crée un élément avec textContent
    function el(tag, text, className) {
      var e = document.createElement(tag);
      if (text) e.textContent = text;
      if (className) e.className = className;
      return e;
    }

    // -- Section Version --
    var secVersion = el('div', null, 'legacy-import__section');
    secVersion.appendChild(el('h4', _t('legacyImport.detectedVersion', 'Detected version'), 'legacy-import__heading'));
    var pVer = el('p');
    pVer.appendChild(el('strong', report.version.label));
    pVer.appendChild(document.createTextNode(' '));
    var confClass = report.version.confidence === 'exact' ? 'badge--success'
      : report.version.confidence === 'high' ? 'badge--info'
      : report.version.confidence === 'medium' ? 'badge--warning' : 'badge--danger';
    pVer.appendChild(el('span', report.version.confidence, 'badge ' + confClass));
    secVersion.appendChild(pVer);
    if (report.version.details.length > 0) {
      var ulDet = el('ul', null, 'legacy-import__details');
      report.version.details.forEach(function(d) { ulDet.appendChild(el('li', d)); });
      secVersion.appendChild(ulDet);
    }
    container.appendChild(secVersion);

    // -- Section Résumé données --
    var secSummary = el('div', null, 'legacy-import__section');
    secSummary.appendChild(el('h4', _t('legacyImport.dataSummary', 'Data summary'), 'legacy-import__heading'));
    var tbl = el('table', null, 'legacy-import__table');
    var thead = el('thead');
    var trHead = el('tr');
    trHead.appendChild(el('th', _t('legacyImport.section', 'Section')));
    trHead.appendChild(el('th', _t('legacyImport.elements', 'Elements')));
    thead.appendChild(trHead);
    tbl.appendChild(thead);
    var tbody = el('tbody');
    Object.keys(report.dataSummary).forEach(function(key) {
      var s = report.dataSummary[key];
      if (s.count > 0) {
        var tr = el('tr');
        tr.appendChild(el('td', s.label));
        tr.appendChild(el('td', String(s.count)));
        tbody.appendChild(tr);
      }
    });
    tbl.appendChild(tbody);
    secSummary.appendChild(tbl);
    container.appendChild(secSummary);

    // -- Section Correspondance champs --
    if (report.fieldMapping.missing.length > 0 || report.fieldMapping.extra.length > 0) {
      var secFields = el('div', null, 'legacy-import__section');
      secFields.appendChild(el('h4', _t('legacyImport.fieldMapping', 'Field mapping'), 'legacy-import__heading'));

      if (report.fieldMapping.missing.length > 0) {
        secFields.appendChild(el('p', _t('legacyImport.missingFields', 'Missing fields (default values):'), 'legacy-import__subtitle'));
        var tblMiss = el('table', null, 'legacy-import__table');
        var theadM = el('thead');
        var trM = el('tr');
        trM.appendChild(el('th', _t('legacyImport.field', 'Field')));
        trM.appendChild(el('th', _t('legacyImport.defaultValue', 'Default value')));
        theadM.appendChild(trM);
        tblMiss.appendChild(theadM);
        var tbodyM = el('tbody');
        report.fieldMapping.missing.forEach(function(m) {
          var tr = el('tr');
          tr.appendChild(el('td', m.label));
          var tdCode = el('td');
          tdCode.appendChild(el('code', m.defaultValue));
          tr.appendChild(tdCode);
          tbodyM.appendChild(tr);
        });
        tblMiss.appendChild(tbodyM);
        secFields.appendChild(tblMiss);
      }

      if (report.fieldMapping.extra.length > 0) {
        secFields.appendChild(el('p', _t('legacyImport.unknownFields', 'Unknown fields (ignored):'), 'legacy-import__subtitle'));
        var ulExtra = el('ul');
        report.fieldMapping.extra.forEach(function(e) {
          var li = el('li');
          li.appendChild(el('code', e.field));
          li.appendChild(document.createTextNode(' (' + e.description + ')'));
          ulExtra.appendChild(li);
        });
        secFields.appendChild(ulExtra);
      }
      container.appendChild(secFields);
    }

    // -- Section Migrations --
    if (report.migrations.length > 0) {
      var secMig = el('div', null, 'legacy-import__section');
      secMig.appendChild(el('h4', _t('legacyImport.autoMigrations', 'Automatic migrations'), 'legacy-import__heading'));
      var ulMig = el('ul');
      report.migrations.forEach(function(m) { ulMig.appendChild(el('li', m.description)); });
      secMig.appendChild(ulMig);
      container.appendChild(secMig);
    }

    // -- Section Avertissements --
    if (report.warnings.length > 0) {
      var secWarn = el('div', null, 'legacy-import__section legacy-import__section--warning');
      secWarn.appendChild(el('h4', _t('legacyImport.warnings', 'Warnings'), 'legacy-import__heading'));
      var ulWarn = el('ul');
      report.warnings.forEach(function(w) {
        var icon = w.severity === 'high' ? '\u26A0 ' : '\u2139 ';
        ulWarn.appendChild(el('li', icon + w.message));
      });
      secWarn.appendChild(ulWarn);
      container.appendChild(secWarn);
    }

    // -- Section Cross-refs --
    if (report.crossRefIssues.length > 0) {
      var secXref = el('div', null, 'legacy-import__section legacy-import__section--warning');
      secXref.appendChild(el('h4', _t('legacyImport.crossReferences', 'Cross references'), 'legacy-import__heading'));
      var ulXref = el('ul');
      var maxIssues = Math.min(report.crossRefIssues.length, 10);
      for (var i = 0; i < maxIssues; i++) {
        ulXref.appendChild(el('li', report.crossRefIssues[i].message));
      }
      if (report.crossRefIssues.length > 10) {
        ulXref.appendChild(el('li', _t('legacyImport.andMoreIssues', '... and') + ' ' + (report.crossRefIssues.length - 10) + ' ' + _t('legacyImport.moreItems', 'more')));
      }
      secXref.appendChild(ulXref);
      container.appendChild(secXref);
    }

    // -- Section Taille --
    var secSize = el('div', null, 'legacy-import__section');
    secSize.appendChild(el('h4', _t('legacyImport.sizeEstimate', 'Size estimate'), 'legacy-import__heading'));
    var pSize = el('p', formatBytes(report.sizeEstimate.raw) + ' ');
    pSize.appendChild(el('span',
      report.sizeEstimate.fitsLocalStorage ? 'OK' : _t('legacyImport.quotaRisk', 'Quota risk'),
      'badge ' + (report.sizeEstimate.fitsLocalStorage ? 'badge--success' : 'badge--danger')
    ));
    secSize.appendChild(pSize);
    container.appendChild(secSize);

    // -- Section Blocage --
    if (!report.canImport) {
      var secBlock = el('div', null, 'legacy-import__section legacy-import__section--danger');
      secBlock.appendChild(el('h4', _t('legacyImport.importBlocked', 'Import blocked'), 'legacy-import__heading'));
      var ulBlock = el('ul');
      report.blockingReasons.forEach(function(r) { ulBlock.appendChild(el('li', r)); });
      secBlock.appendChild(ulBlock);
      container.appendChild(secBlock);
    }
  }

  // ============================================================
  // Affichage modal
  // ============================================================

  /**
   * Affiche le rapport de migration dans un modal
   * @param {object} report - L'ImportReport
   * @param {object} rawData - Les données brutes importées
   * @param {function} onConfirm - Callback appelé avec les données migrées
   * @param {function} onCancel - Callback d'annulation
   */
  function showMigrationModal(report, rawData, onConfirm, onCancel) {
    var overlay = document.getElementById('modalOverlay');
    var modalTitle = document.getElementById('modalTitle');
    var modalBody = document.getElementById('modalBody');
    var saveBtn = document.getElementById('modalSave');
    var cancelBtn = document.getElementById('modalCancel');
    var closeBtn = document.getElementById('modalClose');
    var deleteBtn = document.getElementById('modalDelete');

    if (!overlay || !modalTitle || !modalBody || !saveBtn || !cancelBtn) {
      void 0;
      if (onCancel) onCancel();
      return;
    }

    // Cleanup
    saveBtn.onclick = null;
    cancelBtn.onclick = null;
    if (closeBtn) closeBtn.onclick = null;
    if (deleteBtn) deleteBtn.style.display = 'none';

    modalTitle.textContent = _t('legacyImport.modalTitle', 'Imported file analysis');

    // Render report safely via DOM methods (no innerHTML with user data)
    modalBody.textContent = '';
    var reportContainer = document.createElement('div');
    reportContainer.className = 'legacy-import__report';
    renderMigrationReport(report, reportContainer);
    modalBody.appendChild(reportContainer);

    // Boutons
    var confirmLabel = report.migrations.length > 0
      ? _t('legacyImport.importWithMigrations', 'Import with migrations')
      : _t('legacyImport.importBtn', 'Import');
    saveBtn.textContent = confirmLabel;
    saveBtn.className = 'btn btn--primary';
    saveBtn.style.display = '';
    saveBtn.disabled = !report.canImport;

    cancelBtn.textContent = _t('legacyImport.cancelBtn', 'Cancel');
    cancelBtn.style.display = '';
    cancelBtn.disabled = false;

    if (closeBtn) {
      closeBtn.style.display = '';
      closeBtn.disabled = false;
    }

    // Handlers
    var cleanup = function() {
      overlay.classList.remove('modal-overlay--visible');
    };

    saveBtn.onclick = function() {
      cleanup();
      if (onConfirm) {
        var result = migrateData(rawData);
        logMigrationActions(report, result.log);
        onConfirm(result.migratedData);
      }
    };

    cancelBtn.onclick = function() {
      cleanup();
      if (onCancel) onCancel();
    };

    if (closeBtn) {
      closeBtn.onclick = function() {
        cleanup();
        if (onCancel) onCancel();
      };
    }

    overlay.classList.add('modal-overlay--visible');
  }

  // ============================================================
  // Validation post-import
  // ============================================================

  /**
   * Valide l'intégrité des données après import
   * @param {object} appDataRef - Référence vers appData
   * @returns {{ valid: boolean, issues: string[], warnings: string[] }}
   */
  function validatePostImport(appDataRef) {
    var result = { valid: true, issues: [], warnings: [] };

    if (!appDataRef || typeof appDataRef !== 'object') {
      result.valid = false;
      result.issues.push('appData est invalide après import');
      return result;
    }

    // Utiliser le module de validation existant
    if (typeof window.validateBeforeSave === 'function') {
      try {
        var validation = window.validateBeforeSave(appDataRef);
        if (validation.errors) result.issues = result.issues.concat(validation.errors);
        if (validation.warnings) result.warnings = result.warnings.concat(validation.warnings);
        if (!validation.valid) result.valid = false;
      } catch (e) {
        result.warnings.push('Erreur pendant la validation: ' + e.message);
      }
    }

    // nextActionId >= max action ID
    if (Array.isArray(appDataRef.actions) && appDataRef.actions.length > 0) {
      var maxId = 0;
      appDataRef.actions.forEach(function(a) {
        if (a && a.id) {
          var numPart = parseInt(String(a.id).replace(/\D/g, ''), 10);
          if (!isNaN(numPart) && numPart > maxId) maxId = numPart;
        }
      });
      if (typeof appDataRef.nextActionId === 'number' && appDataRef.nextActionId <= maxId) {
        result.warnings.push('nextActionId (' + appDataRef.nextActionId + ') est inférieur ou égal au max ID action (' + maxId + ')');
      }
    }

    // Vérifier les IDs dupliqués dans les collections critiques
    var collections = ['controls', 'actions', 'risks', 'threats', 'nonconformities', 'audits'];
    collections.forEach(function(col) {
      if (Array.isArray(appDataRef[col])) {
        var ids = {};
        appDataRef[col].forEach(function(item) {
          if (item && item.id) {
            if (ids[item.id]) {
              result.warnings.push('ID dupliqué dans ' + col + ': ' + item.id);
            }
            ids[item.id] = true;
          }
        });
      }
    });

    return result;
  }

  // ============================================================
  // Audit logging
  // ============================================================

  /**
   * Enregistre les actions de migration dans l'audit log
   * @param {object} report - L'ImportReport
   * @param {string[]} migrationLog - Le log détaillé de migrateData()
   */
  function logMigrationActions(report, migrationLog) {
    if (typeof window.addAuditEntry !== 'function') return;

    try {
      var summary = 'Import v' + report.version.version + ' \u2192 v2.0';
      if (report.migrations.length > 0) {
        summary += ' (' + report.migrations.length + ' migration(s))';
      }
      window.addAuditEntry('import_legacy', 'migration', summary);

      var maxEntries = Math.min(migrationLog.length, 10);
      for (var i = 0; i < maxEntries; i++) {
        window.addAuditEntry('migration_detail', 'migration', migrationLog[i]);
      }
      if (migrationLog.length > 10) {
        window.addAuditEntry('migration_detail', 'migration', '... et ' + (migrationLog.length - 10) + ' autre(s) migration(s)');
      }
    } catch (e) {
      void 0;
    }
  }

  // ============================================================
  // Hash d'intégrité
  // ============================================================

  /**
   * Calcule un hash simple (DJB2) pour vérification d'intégrité
   * Non cryptographique — détection de corruption accidentelle uniquement
   * @param {string} str
   * @returns {string} Hash hexadécimal
   */
  function simpleHash(str) {
    var hash = 5381;
    for (var i = 0; i < str.length; i++) {
      hash = ((hash << 5) + hash + str.charCodeAt(i)) & 0xFFFFFFFF;
    }
    return (hash >>> 0).toString(16).padStart(8, '0');
  }

  /**
   * Génère le hash d'intégrité pour un objet d'export
   * @param {object} data - Les données d'export (sans _integrity)
   * @returns {string} Hash
   */
  function computeIntegrityHash(data) {
    var cleanData = shallowCloneWithout(data, ['_integrity']);
    return simpleHash(JSON.stringify(cleanData));
  }

  /**
   * Vérifie le hash d'intégrité d'un import
   * @param {object} data - Les données importées
   * @returns {{ valid: boolean, message: string }}
   */
  function verifyIntegrity(data) {
    if (!data._integrity) {
      return { valid: true, message: 'Pas de hash d\'intégrité (fichier ancien format)' };
    }
    var expected = data._integrity;
    var computed = computeIntegrityHash(data);
    if (computed === expected) {
      return { valid: true, message: 'Intégrité vérifiée' };
    }
    return { valid: false, message: 'Hash d\'intégrité invalide (fichier modifié ou corrompu)' };
  }

  // ============================================================
  // Utilitaires
  // ============================================================

  function formatBytes(bytes) {
    if (bytes < 1024) return bytes + ' B';
    if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
    return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
  }

  function describeDefault(val) {
    if (val === null || val === undefined) return 'null';
    if (Array.isArray(val)) return '[] (vide)';
    if (typeof val === 'object') {
      var keys = Object.keys(val);
      if (keys.length === 0) return '{} (vide)';
      return '{' + keys.join(', ') + '}';
    }
    return JSON.stringify(val);
  }

  function shallowCloneWithout(obj, excludeKeys) {
    var clone = {};
    Object.keys(obj).forEach(function(key) {
      if (excludeKeys.indexOf(key) === -1) {
        clone[key] = obj[key];
      }
    });
    return clone;
  }

  // ============================================================
  // API publique
  // ============================================================

  var LegacyImport = {
    detectVersion: detectVersion,
    analyzeImport: analyzeImport,
    migrateData: migrateData,
    renderMigrationReport: renderMigrationReport,
    showMigrationModal: showMigrationModal,
    validatePostImport: validatePostImport,
    logMigrationActions: logMigrationActions,
    computeIntegrityHash: computeIntegrityHash,
    verifyIntegrity: verifyIntegrity,
    simpleHash: simpleHash,
    PRIORITY_MIGRATION: PRIORITY_MIGRATION,
    VERSION_REGISTRY: VERSION_REGISTRY
  };

  // Exposer globalement
  window.CSI_LegacyImport = LegacyImport;

  // Enregistrer dans le registre CSI
  if (window.CSI && window.CSI.register) {
    window.CSI.register('legacy-import', {
      exports: { CSI_LegacyImport: LegacyImport }
    });
  }

  void 0;

})();
