/**
 * Cyber-Assistant - Framework Mapping UI
 * Interface utilisateur pour le mapping transversal entre referentiels.
 * Charge apres bundle.js.
 */
(function() {
  'use strict';

  // ============================================================
  // State
  // ============================================================
  var _currentSource = 'iso27002';
  var _currentTarget = 'nis2';
  var _currentRelFilter = '';
  var _currentSearch = '';
  var _currentView = 'list'; // 'list' or 'heatmap'

  // ============================================================
  // Main entry point
  // ============================================================
  function loadFrameworkMappingView() {
    var container = document.getElementById('framework-mapping');
    if (!container) return;

    var FM = window.CSI_FrameworkMapper;
    if (!FM || !FM.isInitialized()) {
      container.innerHTML = '<div class="empty-state"><p>' + t('mapping.notAvailable', 'Le module de mapping n\'est pas disponible.') + '</p></div>';
      return;
    }

    var enabled = FM.listEnabledFrameworks();
    // FIX-MAPPER-SELECTOR-MGMT (audit 2026-06-10) : exclure des sélecteurs source/cible les
    // référentiels de type système de management (iso27001, open-security-management) et
    // ceux sans contrôles évaluables : ils n'exposent que des 'clauses' (ni controls ni
    // requirements) et aucune table de mapping, d'où des stats « 0/undefined » et une liste
    // toujours vide quand on les sélectionne.
    enabled = enabled.filter(function(f) {
      var fw = FM.getFramework(f.id);
      if (!fw || (fw.meta && fw.meta.type === 'management-system')) return false;
      return (fw.controls || fw.requirements || []).length > 0;
    });
    if (enabled.length < 2) {
      container.innerHTML = '<div class="empty-state"><p>' + t('mapping.needTwo', 'Activez au moins deux referentiels dans les parametres.') + '</p></div>';
      return;
    }

    // Ensure current source/target are valid
    var ids = enabled.map(function(f) { return f.id; });
    if (ids.indexOf(_currentSource) < 0) _currentSource = ids[0];
    if (ids.indexOf(_currentTarget) < 0 || _currentTarget === _currentSource) {
      _currentTarget = ids[1] || ids[0];
    }

    _renderView(container, FM, enabled);
  }

  // ============================================================
  // Full view render
  // ============================================================
  function _renderView(container, FM, enabled) {
    var html = '';

    // Header with selectors
    html += '<div class="mapping-header">';
    html += '  <div class="mapping-selectors">';
    html += '    <select id="mappingSourceFw" class="form-control mapping-select">';
    enabled.forEach(function(fw) {
      html += '      <option value="' + fw.id + '"' + (fw.id === _currentSource ? ' selected' : '') + '>' + _escHtml(fw.shortName) + '</option>';
    });
    html += '    </select>';
    html += '    <span class="mapping-arrow"><i class="fas fa-arrows-left-right"></i></span>';
    html += '    <select id="mappingTargetFw" class="form-control mapping-select">';
    enabled.forEach(function(fw) {
      html += '      <option value="' + fw.id + '"' + (fw.id === _currentTarget ? ' selected' : '') + '>' + _escHtml(fw.shortName) + '</option>';
    });
    html += '    </select>';
    html += '  </div>';
    html += '  <div class="mapping-filters">';
    html += '    <select id="mappingRelFilter" class="form-control mapping-filter-select">';
    html += '      <option value="">' + t('mapping.allTypes', 'Tous types') + '</option>';
    (window.MAPPING_RELATIONSHIP_TYPES || []).forEach(function(rt) {
      html += '      <option value="' + rt.value + '"' + (rt.value === _currentRelFilter ? ' selected' : '') + '>' + _escHtml(rt.label) + '</option>';
    });
    html += '    </select>';
    html += '    <input type="search" id="mappingSearch" class="form-control mapping-search" placeholder="' + t('mapping.search', 'Rechercher...') + '" value="' + _escHtml(_currentSearch) + '">';
    html += '    <div class="mapping-view-toggle">';
    html += '      <button class="btn btn--sm mapping-view-btn' + (_currentView === 'list' ? ' mapping-view-btn--active' : '') + '" data-view="list" title="' + t('mapping.viewList', 'Vue liste') + '"><i class="fas fa-list"></i></button>';
    html += '      <button class="btn btn--sm mapping-view-btn' + (_currentView === 'heatmap' ? ' mapping-view-btn--active' : '') + '" data-view="heatmap" title="' + t('mapping.viewHeatmap', 'Vue matrice') + '"><i class="fas fa-table-cells"></i></button>';
    html += '    </div>';
    html += '  </div>';
    html += '</div>';

    // Stats bar
    var stats = FM.getCoverageStats(_currentSource, _currentTarget);
    html += _renderStatsBar(stats, FM);

    // Content area
    html += '<div id="mappingContent" class="mapping-content">';
    if (_currentView === 'list') {
      html += _renderListView(FM, stats);
    } else {
      html += _renderHeatmapView(FM);
    }
    html += '</div>';

    container.innerHTML = html;
    _bindEvents(container, FM, enabled);
  }

  // ============================================================
  // Stats bar
  // ============================================================
  function _renderStatsBar(stats, FM) {
    if (!stats) return '<div class="mapping-stats"><p>' + t('mapping.noMappings', 'Aucune correspondance trouvee.') + '</p></div>';

    var sourceFw = FM.getFramework(_currentSource);
    var targetFw = FM.getFramework(_currentTarget);
    var sName = sourceFw ? sourceFw.meta.shortName : _currentSource;
    var tName = targetFw ? targetFw.meta.shortName : _currentTarget;

    var html = '<div class="mapping-stats">';
    html += '  <div class="mapping-stat">';
    html += '    <span class="mapping-stat__value">' + stats.totalMappings + '</span>';
    html += '    <span class="mapping-stat__label">' + t('mapping.stats.totalMappings', 'Relations') + '</span>';
    html += '  </div>';
    html += '  <div class="mapping-stat">';
    html += '    <span class="mapping-stat__value">' + stats.avgCoverage + '%</span>';
    html += '    <span class="mapping-stat__label">' + t('mapping.stats.avgCoverage', 'Couverture moy.') + '</span>';
    html += '  </div>';
    html += '  <div class="mapping-stat">';
    html += '    <span class="mapping-stat__value">' + stats.mappedSources + '/' + stats.totalSource + '</span>';
    html += '    <span class="mapping-stat__label">' + _escHtml(sName) + ' ' + t('mapping.stats.mapped', 'mappes') + '</span>';
    html += '  </div>';
    html += '  <div class="mapping-stat">';
    html += '    <span class="mapping-stat__value">' + stats.mappedTargets + '/' + stats.totalTarget + '</span>';
    html += '    <span class="mapping-stat__label">' + _escHtml(tName) + ' ' + t('mapping.stats.covered', 'couverts') + '</span>';
    html += '  </div>';
    html += '</div>';
    return html;
  }

  // ============================================================
  // List view
  // ============================================================
  function _renderListView(FM, stats) {
    var controls = FM.getControls(_currentSource);
    var matrix = FM.getMatrix(_currentSource, _currentTarget);

    // Group mappings by source ref
    var bySource = {};
    matrix.forEach(function(m) {
      if (!bySource[m.sourceRef]) bySource[m.sourceRef] = [];
      bySource[m.sourceRef].push(m);
    });

    // Apply filters
    var search = _currentSearch.toLowerCase();

    var html = '<div class="mapping-list">';
    html += '<table class="table mapping-table">';
    html += '<thead><tr>';
    html += '  <th class="mapping-col-ref">' + t('mapping.col.ref', 'Ref.') + '</th>';
    html += '  <th class="mapping-col-title">' + t('mapping.col.title', 'Titre') + '</th>';
    html += '  <th class="mapping-col-status">' + t('mapping.col.status', 'Statut') + '</th>';
    html += '  <th class="mapping-col-mappings">' + t('mapping.col.mappings', 'Correspondances') + '</th>';
    html += '</tr></thead>';
    html += '<tbody>';

    var visibleCount = 0;
    controls.forEach(function(ctrl) {
      // FIX-MAPPER-LIST-CATEGORY (audit 2026-06-10) : côté CyFun, les mappings sont définis
      // au niveau des CATÉGORIES ('GV.PO') alors que les exigences ont des refs détaillées
      // ('GV.PO-01.1') — repli sur la catégorie de l'exigence pour que le sens CyFun→ISO
      // affiche les correspondances au lieu de « Aucune correspondance » sur les 218 lignes.
      var groupRef = bySource[ctrl.ref] ? ctrl.ref : (ctrl.categoryId || ctrl.category);
      var mappings = bySource[groupRef] || [];

      // Filter by relationship type
      if (_currentRelFilter) {
        mappings = mappings.filter(function(m) { return m.relationship === _currentRelFilter; });
      }

      // Search filter
      var matchesSearch = true;
      if (search) {
        var targetInfo = mappings.map(function(m) {
          var ti = FM.getControlInfo(_currentTarget, m.targetRef);
          return ti ? (ti.ref + ' ' + ti.title) : m.targetRef;
        }).join(' ').toLowerCase();

        matchesSearch = (ctrl.ref + ' ' + ctrl.title).toLowerCase().indexOf(search) >= 0 ||
                        targetInfo.indexOf(search) >= 0;
      }

      if (!matchesSearch) return;

      // Hide if filter active and no mappings match
      if (_currentRelFilter && mappings.length === 0) return;

      visibleCount++;
      var status = FM.getControlStatus(_currentSource, ctrl.ref);
      var statusClass = 'status--' + (status.status || 'non_implemente');

      html += '<tr class="mapping-row">';
      html += '  <td class="mapping-col-ref"><strong>' + _escHtml(ctrl.ref) + '</strong></td>';
      html += '  <td class="mapping-col-title">' + _escHtml(ctrl.title) + '</td>';
      html += '  <td class="mapping-col-status"><span class="mapping-status ' + statusClass + '">' + _statusLabel(status.status) + '</span></td>';
      html += '  <td class="mapping-col-mappings">';

      if (mappings.length === 0) {
        html += '<span class="mapping-empty">' + t('mapping.noLink', 'Aucune correspondance') + '</span>';
      } else {
        html += '<div class="mapping-badges">';
        mappings.forEach(function(m) {
          var relType = _getRelType(m.relationship);
          var targetCtrl = FM.getControlInfo(_currentTarget, m.targetRef);
          var targetStatus = FM.getControlStatus(_currentTarget, m.targetRef);
          var targetStatusClass = 'status--' + (targetStatus.status || 'non_implemente');

          html += '<div class="mapping-badge" style="border-left: 3px solid ' + relType.color + '" title="' + _escHtml(m.notes || '') + '">';
          html += '  <span class="mapping-badge__ref">' + _escHtml(m.targetRef) + '</span>';
          html += '  <span class="mapping-badge__rel"><i class="fas ' + relType.icon + '"></i> ' + _escHtml(relType.label) + '</span>';
          html += '  <span class="mapping-badge__cov">' + (m.coverage || 0) + '%</span>';
          html += '  <span class="mapping-badge__status ' + targetStatusClass + '">' + _statusDot(targetStatus.status) + '</span>';
          html += '</div>';
        });
        html += '</div>';

        // Check for cross-framework conflicts
        // FIX-MAPPER-LIST-CATEGORY (audit 2026-06-10) : utiliser la même clé de regroupement
        // (catégorie si la ref détaillée n'a pas de mapping direct) pour la détection de conflits.
        var conflicts = FM.getInferredStatusWithConflicts ? FM.getInferredStatusWithConflicts(_currentSource, groupRef, _currentTarget) : null;
        if (conflicts && conflicts.conflicts && conflicts.conflicts.length > 0) {
          html += _renderConflictBadge(conflicts.conflicts);
        }
      }

      html += '  </td>';
      html += '</tr>';
    });

    if (visibleCount === 0) {
      html += '<tr><td colspan="4" class="mapping-empty-row">' + t('mapping.noResults', 'Aucun resultat pour ces criteres.') + '</td></tr>';
    }

    html += '</tbody></table>';
    html += '</div>';
    return html;
  }

  // ============================================================
  // Heatmap view
  // ============================================================
  function _renderHeatmapView(FM) {
    var sourceFw = FM.getFramework(_currentSource);
    var targetFw = FM.getFramework(_currentTarget);
    if (!sourceFw || !targetFw) return '';

    var matrix = FM.getMatrix(_currentSource, _currentTarget);

    // Group by source category x target category
    var heatData = {};
    // Certains référentiels (ex. ReCyF) n'exposent pas meta.categories : la vue
    // matrice par catégories n'a alors pas de sens. On affiche un message explicite
    // plutôt que de laisser un TypeError casser tout le rendu du mapping (la vue
    // liste reste disponible pour ces référentiels).
    var sourceCategories = (sourceFw.meta && sourceFw.meta.categories) || [];
    var targetCategories = (targetFw.meta && targetFw.meta.categories) || [];
    if (!sourceCategories.length || !targetCategories.length) {
      return '<div class="empty-state"><p>' +
        _escHtml(t('mapping.heatmap.unavailable', 'Vue matrice indisponible pour ce référentiel. Utilisez la vue liste.')) +
        '</p></div>';
    }

    // Init grid
    sourceCategories.forEach(function(sc) {
      heatData[sc.id] = {};
      targetCategories.forEach(function(tc) {
        heatData[sc.id][tc.id] = { count: 0, totalCoverage: 0 };
      });
    });

    // Fill grid
    // FIX-MAPPER-HEATMAP-CATEGORY (audit 2026-06-10) : côté CyFun, les refs de mapping sont
    // des IDs de CATÉGORIES ('GV.PO'), jamais des refs d'exigences ('GV.PO-01.1') —
    // getControlInfo renvoyait null pour chaque cible et la matrice restait entièrement
    // vide (0/132 relations placées). On résout désormais la catégorie de chaque ref :
    // ID de catégorie tel quel, sinon catégorie du contrôle dans la définition.
    matrix.forEach(function(m) {
      var scat = _refToCategoryId(FM, _currentSource, m.sourceRef, sourceCategories);
      var tcat = _refToCategoryId(FM, _currentTarget, m.targetRef, targetCategories);
      if (!scat || !tcat) return;

      if (heatData[scat] && heatData[scat][tcat]) {
        heatData[scat][tcat].count++;
        heatData[scat][tcat].totalCoverage += (m.coverage || 0);
      }
    });

    var html = '<div class="mapping-heatmap-container">';
    html += '<table class="table mapping-heatmap">';
    html += '<caption class="sr-only">' + t('mapping.heatmap.caption', 'Matrice de couverture entre référentiels source et cible') + '</caption>';
    html += '<thead><tr>';
    html += '  <th class="mapping-heatmap__corner"></th>';
    targetCategories.forEach(function(tc) {
      html += '  <th class="mapping-heatmap__colhead" scope="col">' + _escHtml(tc.label) + '</th>';
    });
    html += '</tr></thead>';
    html += '<tbody>';

    sourceCategories.forEach(function(sc) {
      html += '<tr>';
      html += '  <th class="mapping-heatmap__rowhead" scope="row">' + _escHtml(sc.label) + '</th>';
      targetCategories.forEach(function(tc) {
        var cell = heatData[sc.id][tc.id];
        var avgCov = cell.count > 0 ? Math.round(cell.totalCoverage / cell.count) : 0;
        var intensity = cell.count > 0 ? Math.min(cell.count / 10, 1) : 0;
        var bgColor = cell.count === 0 ? 'transparent' : _coverageColor(avgCov, intensity);

        html += '  <td class="mapping-heatmap__cell" style="background-color: ' + bgColor + '" title="' + cell.count + ' ' + t('mapping.heatmap.relations', 'relations') + ', ' + t('mapping.heatmap.avgCoverage', 'couverture moy.') + ' ' + avgCov + '%"' + ' aria-label="' + _escHtml(sc.label) + ' / ' + _escHtml(tc.label) + ': ' + cell.count + ' ' + t('mapping.heatmap.relations', 'relations') + ', ' + avgCov + '%">';
        if (cell.count > 0) {
          html += '<span class="mapping-heatmap__count">' + cell.count + '</span>';
          html += '<span class="mapping-heatmap__cov">' + avgCov + '%</span>';
        }
        html += '  </td>';
      });
      html += '</tr>';
    });

    html += '</tbody></table>';
    html += '</div>';
    return html;
  }

  // ============================================================
  // Event bindings
  // ============================================================
  function _bindEvents(container, FM, enabled) {
    var sourceSelect = container.querySelector('#mappingSourceFw');
    var targetSelect = container.querySelector('#mappingTargetFw');
    var relFilter = container.querySelector('#mappingRelFilter');
    var searchInput = container.querySelector('#mappingSearch');
    var viewBtns = container.querySelectorAll('.mapping-view-btn');

    if (sourceSelect) {
      sourceSelect.addEventListener('change', function() {
        _currentSource = this.value;
        if (_currentSource === _currentTarget) {
          // Auto-switch target
          var other = enabled.filter(function(f) { return f.id !== _currentSource; });
          if (other.length > 0) _currentTarget = other[0].id;
        }
        _renderView(container, FM, enabled);
      });
    }

    if (targetSelect) {
      targetSelect.addEventListener('change', function() {
        _currentTarget = this.value;
        if (_currentTarget === _currentSource) {
          var other = enabled.filter(function(f) { return f.id !== _currentTarget; });
          if (other.length > 0) _currentSource = other[0].id;
        }
        _renderView(container, FM, enabled);
      });
    }

    if (relFilter) {
      relFilter.addEventListener('change', function() {
        _currentRelFilter = this.value;
        _renderView(container, FM, enabled);
      });
    }

    if (searchInput) {
      var debounce = null;
      searchInput.addEventListener('input', function() {
        var val = this.value;
        clearTimeout(debounce);
        debounce = setTimeout(function() {
          _currentSearch = val;
          var content = container.querySelector('#mappingContent');
          if (content) {
            if (_currentView === 'list') {
              var stats = FM.getCoverageStats(_currentSource, _currentTarget);
              content.innerHTML = _renderListView(FM, stats);
            }
          }
        }, 300);
      });
    }

    viewBtns.forEach(function(btn) {
      btn.addEventListener('click', function() {
        _currentView = this.getAttribute('data-view');
        _renderView(container, FM, enabled);
      });
    });
  }

  // ============================================================
  // Helpers
  // ============================================================
  var _escHtml = window.escapeHtml;

  // FIX-MAPPER-HEATMAP-CATEGORY (audit 2026-06-10) : résout l'ID de catégorie d'une ref de
  // mapping, qu'elle désigne déjà une catégorie (cibles CyFun 'GV.PO') ou un contrôle/exigence.
  function _refToCategoryId(FM, frameworkId, ref, categories) {
    for (var i = 0; i < categories.length; i++) {
      if (categories[i].id === ref) return ref;
    }
    var ctrl = FM.getControlInfo(frameworkId, ref);
    return ctrl ? (ctrl.categoryId || ctrl.category) : null;
  }

  function _getRelType(relationship) {
    var types = window.MAPPING_RELATIONSHIP_TYPES || [];
    for (var i = 0; i < types.length; i++) {
      if (types[i].value === relationship) return types[i];
    }
    return { value: relationship, label: relationship, icon: 'fa-link', color: '#6b7280' };
  }

  function _statusLabel(status) {
    var labels = {
      'non_implemente': t('status.notImplemented', 'Non impl.'),
      'en_cours': t('status.inProgress', 'En cours'),
      'implemente': t('status.implemented', 'Impl.'),
      'verifie': t('status.verified', 'Verifie'),
      'unknown': '-'
    };
    return labels[status] || status || '-';
  }

  function _statusDot(status) {
    var colors = {
      'non_implemente': '#ef4444',
      'en_cours': '#f59e0b',
      'implemente': '#10b981',
      'verifie': '#3b82f6',
      'unknown': '#9ca3af'
    };
    var color = colors[status] || '#9ca3af';
    var statusLabel = _statusLabel(status);
    return '<span class="mapping-status-dot" style="background:' + color + '" aria-hidden="true"></span>' +
           '<span class="sr-only">' + statusLabel + '</span>';
  }

  function _renderConflictBadge(conflicts) {
    if (!conflicts || conflicts.length === 0) return '';
    var html = '<div class="mapping-conflict" title="' + _escHtml(conflicts[0].message) + '">';
    html += '  <i class="fas fa-exclamation-triangle" style="color: #f59e0b;"></i>';
    html += '  <span class="sr-only">' + t('mapping.conflict', 'Conflit de statut détecté') + '</span>';
    html += '</div>';
    return html;
  }

  function _coverageColor(coverage, intensity) {
    if (coverage >= 70) return 'rgba(16, 185, 129, ' + (0.15 + intensity * 0.45) + ')';
    if (coverage >= 40) return 'rgba(245, 158, 11, ' + (0.15 + intensity * 0.45) + ')';
    return 'rgba(239, 68, 68, ' + (0.15 + intensity * 0.45) + ')';
  }

  // ============================================================
  // Expose
  // ============================================================
  window.loadFrameworkMappingView = loadFrameworkMappingView;

  void 0;
})();
