/**
 * SectionToolbar — Generic, reusable filter + sort toolbar component.
 *
 * Toolbar v2 — stacked rows layout:
 *   Row 1 (filters): search input + visible filter selects + optional
 *                    "+ Filtres (n)" toggle + "Réinitialiser" button.
 *   Row 2 (advanced, optional): collapsible panel holding filters flagged
 *                    `advanced: true`, toggled by the "+ Filtres" button.
 *   Row 3 (sort):    "Tri :" label + sort chips, always on their own line.
 *
 * Inserts a unified toolbar bar between `.module__header` and `.table-container`
 * in any section of the SPA. Provides filter dropdowns, sort chips, and an
 * optional search input, all wired to a single `onUpdate` callback.
 *
 * @example
 *   SectionToolbar.init('controls', {
 *     filters: [
 *       { key: 'category', label: 'Domaine', options: [...] },
 *       { key: 'owner', label: 'Responsable', options: [...], advanced: true }
 *     ],
 *     sort: {
 *       options: [
 *         { key: 'reference', label: 'Ref.', default: true, dir: 'asc' },
 *         { key: 'title', label: 'Titre' }
 *       ]
 *     },
 *     search: true,
 *     onUpdate: () => renderTable()
 *   });
 */
const SectionToolbar = (() => {
  'use strict';

  // ---------------------------------------------------------------------------
  // Private state
  // ---------------------------------------------------------------------------

  /** @type {Map<string, {sort: {key:string, dir:string}|null, filters: Object, search: string, config: Object, el: HTMLElement, debounceTimer: number|null, advancedOpen: boolean}>} */
  const _state = new Map();

  // ---------------------------------------------------------------------------
  // Helpers
  // ---------------------------------------------------------------------------

  /**
   * Create a DOM element with optional attributes and children.
   * @param {string} tag
   * @param {Object} [attrs]
   * @param {Array<Node|string>} [children]
   * @returns {HTMLElement}
   */
  function el(tag, attrs, children) {
    const node = document.createElement(tag);
    if (attrs) {
      Object.keys(attrs).forEach(function (k) {
        if (k === 'className') {
          node.className = attrs[k];
        } else if (k === 'dataset') {
          Object.keys(attrs[k]).forEach(function (dk) {
            node.dataset[dk] = attrs[k][dk];
          });
        } else if (k === 'disabled' && attrs[k]) {
          node.disabled = true;
        } else {
          node.setAttribute(k, attrs[k]);
        }
      });
    }
    if (children) {
      children.forEach(function (c) {
        if (typeof c === 'string') {
          node.appendChild(document.createTextNode(c));
        } else if (c) {
          node.appendChild(c);
        }
      });
    }
    return node;
  }

  // ---------------------------------------------------------------------------
  // Sort chip rendering
  // ---------------------------------------------------------------------------

  /**
   * Build the direction icon element for a sort chip.
   * @param {'asc'|'desc'} dir
   * @returns {HTMLElement}
   */
  function _sortIcon(dir) {
    var icon = document.createElement('i');
    icon.className = dir === 'asc' ? 'fas fa-arrow-up' : 'fas fa-arrow-down';
    icon.setAttribute('aria-hidden', 'true');
    return icon;
  }

  /**
   * Refresh visual state of all sort chips for a given module.
   * @param {string} moduleId
   */
  function _refreshSortChips(moduleId) {
    var state = _state.get(moduleId);
    if (!state || !state.sort) return;

    var wrapper = state.el;
    var chips = wrapper.querySelectorAll('[data-toolbar-sort]');

    chips.forEach(function (chip) {
      var key = chip.getAttribute('data-toolbar-sort');
      var isActive = key === state.sort.key;

      // Reset classes
      chip.classList.remove('sort-chip--active', 'sort-chip--asc', 'sort-chip--desc');

      // Remove existing icon (if any)
      var existingIcon = chip.querySelector('i');
      if (existingIcon) existingIcon.remove();

      if (isActive) {
        chip.classList.add('sort-chip--active');
        chip.classList.add(state.sort.dir === 'asc' ? 'sort-chip--asc' : 'sort-chip--desc');
        chip.appendChild(_sortIcon(state.sort.dir));
        chip.setAttribute('aria-label', chip.querySelector('span').textContent + ' — ' + t('common.sortBy', 'tri') + ' ' + (state.sort.dir === 'asc' ? t('sort.ascending', 'croissant') : t('sort.descending', 'decroissant')));
      } else {
        chip.setAttribute('aria-label', chip.querySelector('span').textContent);
      }
    });
  }

  // ---------------------------------------------------------------------------
  // Reset button / advanced badge refresh
  // ---------------------------------------------------------------------------

  /**
   * Refresh the visibility of the "Réinitialiser" button (shown only when at
   * least one filter is active or the search query is non-empty) and the
   * active-count badge on the "+ Filtres" toggle.
   * @param {string} moduleId
   */
  function _refreshMeta(moduleId) {
    var state = _state.get(moduleId);
    if (!state) return;

    var anyActive = !!state.search;
    var advActive = 0;
    (state.config.filters || []).forEach(function (f) {
      if (state.filters[f.key]) {
        anyActive = true;
        if (f.advanced) advActive++;
      }
    });

    var resetBtn = state.el.querySelector('.section-toolbar__reset');
    if (resetBtn) resetBtn.hidden = !anyActive;

    var countEl = state.el.querySelector('.section-toolbar__adv-count');
    if (countEl) {
      countEl.hidden = advActive === 0;
      countEl.textContent = advActive ? '(' + advActive + ')' : '';
    }
  }

  /**
   * Toggle the collapsible advanced-filters panel.
   * @param {string} moduleId
   */
  function _toggleAdvanced(moduleId) {
    var state = _state.get(moduleId);
    if (!state) return;

    state.advancedOpen = !state.advancedOpen;

    var panel = state.el.querySelector('.section-toolbar__advanced');
    if (panel) panel.hidden = !state.advancedOpen;

    var btn = state.el.querySelector('.section-toolbar__adv-toggle');
    if (btn) {
      btn.setAttribute('aria-expanded', state.advancedOpen ? 'true' : 'false');
      var chevron = btn.querySelector('.section-toolbar__adv-chevron');
      if (chevron) {
        chevron.className = 'fas ' + (state.advancedOpen ? 'fa-chevron-up' : 'fa-chevron-down') + ' section-toolbar__adv-chevron';
      }
    }
  }

  /**
   * Reset search + all filters to empty and sort to its default, then notify.
   * @param {string} moduleId
   */
  function _resetAll(moduleId) {
    var state = _state.get(moduleId);
    if (!state) return;

    // Search
    if (state.debounceTimer) {
      clearTimeout(state.debounceTimer);
      state.debounceTimer = null;
    }
    state.search = '';
    var searchEl = state.el.querySelector('.section-toolbar__search');
    if (searchEl) searchEl.value = '';

    // Filters (fire per-filter onChange for cascading filters when the value changed)
    (state.config.filters || []).forEach(function (f) {
      var changed = !!state.filters[f.key];
      state.filters[f.key] = '';
      var sel = state.el.querySelector('[data-toolbar-filter="' + f.key + '"]');
      if (sel) sel.value = '';
      if (changed && typeof f.onChange === 'function') {
        f.onChange('');
      }
    });

    // Sort back to its default option
    var initial = _deriveInitialState(state.config);
    if (initial.sort) {
      state.sort = initial.sort;
      _refreshSortChips(moduleId);
    }

    _refreshMeta(moduleId);
    _notify(moduleId);
  }

  // ---------------------------------------------------------------------------
  // Event handlers
  // ---------------------------------------------------------------------------

  /**
   * Handle click on a sort chip.
   * @param {string} moduleId
   * @param {string} sortKey
   * @param {'asc'|'desc'} defaultDir — default direction for this chip
   */
  function _onSortChipClick(moduleId, sortKey, defaultDir) {
    var state = _state.get(moduleId);
    if (!state) return;

    if (state.sort.key === sortKey) {
      // Toggle direction on already-active chip
      state.sort.dir = state.sort.dir === 'asc' ? 'desc' : 'asc';
    } else {
      // Activate a different chip with its default direction
      state.sort.key = sortKey;
      state.sort.dir = defaultDir || 'asc';
    }

    _refreshSortChips(moduleId);
    _notify(moduleId);
  }

  /**
   * Handle change on a filter select.
   * @param {string} moduleId
   * @param {string} filterKey
   * @param {string} value
   * @param {Function|undefined} perFilterCb — optional per-filter onChange
   */
  function _onFilterChange(moduleId, filterKey, value, perFilterCb) {
    var state = _state.get(moduleId);
    if (!state) return;

    state.filters[filterKey] = value;

    // Per-filter callback (e.g., cascading filters)
    if (typeof perFilterCb === 'function') {
      perFilterCb(value);
    }

    _refreshMeta(moduleId);
    _notify(moduleId);
  }

  /**
   * Handle input on the search field (debounced).
   * @param {string} moduleId
   * @param {string} value
   */
  function _onSearchInput(moduleId, value) {
    var state = _state.get(moduleId);
    if (!state) return;

    state.search = value;
    _refreshMeta(moduleId);

    // Debounce: 300 ms
    if (state.debounceTimer) clearTimeout(state.debounceTimer);
    state.debounceTimer = setTimeout(function () {
      state.debounceTimer = null;
      _notify(moduleId);
    }, 300);
  }

  /**
   * Notify the consumer that state has changed.
   * @param {string} moduleId
   */
  function _notify(moduleId) {
    var state = _state.get(moduleId);
    if (!state || typeof state.config.onUpdate !== 'function') return;
    state.config.onUpdate();
  }

  // ---------------------------------------------------------------------------
  // DOM construction
  // ---------------------------------------------------------------------------

  /**
   * Build one filter <select> from its definition.
   * @param {string} moduleId
   * @param {Object} filterDef
   * @returns {HTMLElement}
   */
  function _buildFilterSelect(moduleId, filterDef) {
    var selectId = filterDef.id || (moduleId + 'Filter_' + filterDef.key);
    var selectAttrs = {
      className: 'form-control form-control--sm',
      id: selectId,
      dataset: { toolbarFilter: filterDef.key }
    };
    if (filterDef.disabled) {
      selectAttrs.disabled = true;
    }

    var selectEl = el('select', selectAttrs);
    // A11y : nom accessible (les filtres n'ont pas de <label> visible).
    if (filterDef.label) selectEl.setAttribute('aria-label', filterDef.label);

    // "All" option — shows the (short) filter label by default; a custom
    // `allLabel` still wins when a section provides one.
    var allLabel = filterDef.allLabel || filterDef.label || t('common.all', 'Tous');
    var allOption = el('option', { value: '' }, [allLabel]);
    if (filterDef.i18n) {
      allOption.setAttribute('data-i18n', filterDef.i18n + '.all');
    }
    selectEl.appendChild(allOption);

    // Individual options
    filterDef.options.forEach(function (opt) {
      var optEl = el('option', { value: opt.value }, [opt.label]);
      selectEl.appendChild(optEl);
    });

    // Set default value
    if (filterDef.defaultValue) {
      selectEl.value = filterDef.defaultValue;
    }

    // Change listener
    var perCb = filterDef.onChange;
    selectEl.addEventListener('change', function () {
      _onFilterChange(moduleId, filterDef.key, this.value, perCb);
    });

    return selectEl;
  }

  /**
   * Build the complete toolbar DOM element (stacked rows layout).
   * @param {string} moduleId
   * @param {Object} config
   * @returns {HTMLElement}
   */
  function _buildToolbar(moduleId, config) {
    var toolbar = el('div', {
      className: 'section-toolbar',
      id: 'toolbar-' + moduleId
    });

    var allFilters = config.filters || [];
    var visibleFilters = allFilters.filter(function (f) { return !f.advanced; });
    var advancedFilters = allFilters.filter(function (f) { return f.advanced; });

    // --- Row 1: filters (search + visible selects + toggle + reset) ---
    if (allFilters.length || config.search) {
      var filtersRow = el('div', { className: 'section-toolbar__row section-toolbar__filters' });

      // Search input (comes first in the filter row)
      if (config.search) {
        var searchInput = el('input', {
          type: 'search',
          className: 'form-control form-control--sm section-toolbar__search',
          placeholder: (window.CSI_i18n && window.CSI_i18n.t ? window.CSI_i18n.t('common.search') : 'Search') + '...',
          'data-i18n-placeholder': 'common.search'
        });
        searchInput.addEventListener('input', function () {
          _onSearchInput(moduleId, this.value);
        });
        filtersRow.appendChild(searchInput);
      }

      // Visible filter selects
      visibleFilters.forEach(function (filterDef) {
        filtersRow.appendChild(_buildFilterSelect(moduleId, filterDef));
      });

      // "+ Filtres (n)" toggle for the advanced panel
      if (advancedFilters.length) {
        var advToggle = el('button', {
          type: 'button',
          className: 'btn btn--sm section-toolbar__adv-toggle',
          'aria-expanded': 'false',
          'aria-controls': 'toolbar-' + moduleId + '-advanced'
        }, ['+ ']);
        advToggle.appendChild(el('span', { 'data-i18n': 'sectionToolbar.moreFilters' }, [t('sectionToolbar.moreFilters', 'Filtres')]));
        var advCount = el('span', { className: 'section-toolbar__adv-count' });
        advCount.hidden = true;
        advToggle.appendChild(advCount);
        advToggle.appendChild(el('i', {
          className: 'fas fa-chevron-down section-toolbar__adv-chevron',
          'aria-hidden': 'true'
        }));
        advToggle.addEventListener('click', function () {
          _toggleAdvanced(moduleId);
        });
        filtersRow.appendChild(advToggle);
      }

      // Reset button (hidden until a filter or the search is active)
      var resetBtn = el('button', {
        type: 'button',
        className: 'btn btn--sm section-toolbar__reset'
      });
      resetBtn.appendChild(el('i', { className: 'fas fa-rotate-left', 'aria-hidden': 'true' }));
      resetBtn.appendChild(el('span', { 'data-i18n': 'sectionToolbar.reset' }, [t('sectionToolbar.reset', 'Réinitialiser')]));
      resetBtn.hidden = true;
      resetBtn.addEventListener('click', function () {
        _resetAll(moduleId);
      });
      filtersRow.appendChild(resetBtn);

      toolbar.appendChild(filtersRow);

      // --- Row 2 (optional): collapsible advanced filters panel ---
      if (advancedFilters.length) {
        var advRow = el('div', {
          className: 'section-toolbar__row section-toolbar__advanced',
          id: 'toolbar-' + moduleId + '-advanced'
        });
        advRow.hidden = true;
        advancedFilters.forEach(function (filterDef) {
          advRow.appendChild(_buildFilterSelect(moduleId, filterDef));
        });
        toolbar.appendChild(advRow);
      }
    }

    // --- Sort row (always its own line, below the filters) ---
    if (config.sort && config.sort.options && config.sort.options.length) {
      var sortRow = el('div', { className: 'section-toolbar__row section-toolbar__sort' });

      var sortLabel = el('span', {
        className: 'section-toolbar__sort-label',
        'data-i18n': 'common.sortBy'
      }, ['Tri :']);
      sortRow.appendChild(sortLabel);

      var chipsWrapper = el('div', { className: 'section-toolbar__sort-chips' });

      config.sort.options.forEach(function (sortDef) {
        var btnAttrs = {
          className: 'sort-chip',
          dataset: { toolbarSort: sortDef.key },
          type: 'button',
          'aria-label': sortDef.label
        };
        var labelSpan = el('span', null, [sortDef.label]);
        if (sortDef.i18n) {
          labelSpan.setAttribute('data-i18n', sortDef.i18n);
        }

        var btn = el('button', btnAttrs, [labelSpan]);

        var defaultDir = sortDef.dir || 'asc';
        btn.addEventListener('click', function () {
          _onSortChipClick(moduleId, sortDef.key, defaultDir);
        });

        chipsWrapper.appendChild(btn);
      });

      sortRow.appendChild(chipsWrapper);
      toolbar.appendChild(sortRow);
    }

    return toolbar;
  }

  // ---------------------------------------------------------------------------
  // Insertion logic
  // ---------------------------------------------------------------------------

  /**
   * Insert the toolbar element into the DOM.
   * @param {string} moduleId
   * @param {HTMLElement} toolbarEl
   * @param {Object} config
   */
  function _insertToolbar(moduleId, toolbarEl, config) {
    // If a containerId override is provided, use that
    if (config.containerId) {
      var container = document.getElementById(config.containerId);
      if (container) {
        container.appendChild(toolbarEl);
        return;
      }
      // Fall through to auto-detect if containerId not found
    }

    // Auto-detect insertion point within the module
    var moduleEl = document.getElementById(moduleId);
    if (!moduleEl) return;

    var header = moduleEl.querySelector('.module__header');
    var tableContainer = moduleEl.querySelector('.table-container');

    if (header && header.nextSibling) {
      moduleEl.insertBefore(toolbarEl, header.nextSibling);
    } else if (tableContainer) {
      moduleEl.insertBefore(toolbarEl, tableContainer);
    } else {
      // Fallback: append to the module element
      moduleEl.appendChild(toolbarEl);
    }
  }

  // ---------------------------------------------------------------------------
  // Initial state
  // ---------------------------------------------------------------------------

  /**
   * Derive initial state from the config.
   * @param {Object} config
   * @returns {{sort: {key:string, dir:string}|null, filters: Object, search: string}}
   */
  function _deriveInitialState(config) {
    var sort = null;
    if (config.sort && config.sort.options && config.sort.options.length) {
      // Find the option marked as default, or fall back to the first one
      var defaultOpt = config.sort.options.find(function (o) { return o.default; });
      if (!defaultOpt) defaultOpt = config.sort.options[0];
      sort = {
        key: defaultOpt.key,
        dir: defaultOpt.dir || 'asc'
      };
    }

    var filters = {};
    if (config.filters) {
      config.filters.forEach(function (f) {
        filters[f.key] = f.defaultValue || '';
      });
    }

    return { sort: sort, filters: filters, search: '' };
  }

  // ---------------------------------------------------------------------------
  // Public API
  // ---------------------------------------------------------------------------

  /**
   * Initialise (or re-initialise) a toolbar for a given module.
   *
   * If a toolbar already exists for this moduleId it is destroyed first,
   * ensuring that calling `init()` multiple times is safe.
   *
   * @param {string} moduleId — ID of the parent `.module` div
   * @param {Object} config   — toolbar configuration (see JSDoc at top of file)
   */
  function init(moduleId, config) {
    if (!moduleId || !config) return;

    // Destroy previous instance if it exists (safe re-init)
    if (_state.has(moduleId)) {
      destroy(moduleId);
    }

    // Build DOM
    var toolbarEl = _buildToolbar(moduleId, config);

    // Derive initial state
    var initial = _deriveInitialState(config);

    // Store state
    _state.set(moduleId, {
      sort: initial.sort,
      filters: initial.filters,
      search: initial.search,
      config: config,
      el: toolbarEl,
      debounceTimer: null,
      advancedOpen: false
    });

    // Insert into DOM
    _insertToolbar(moduleId, toolbarEl, config);

    // Apply initial visual state for sort chips
    if (initial.sort) {
      _refreshSortChips(moduleId);
    }

    // Apply initial reset-button / advanced-badge state (defaultValue may be set)
    _refreshMeta(moduleId);
  }

  /**
   * Get the current sort state for a module.
   * @param {string} moduleId
   * @returns {{key: string, dir: 'asc'|'desc'}|null}
   */
  function getSort(moduleId) {
    var state = _state.get(moduleId);
    if (!state || !state.sort) return null;
    // Return a copy to avoid external mutation
    return { key: state.sort.key, dir: state.sort.dir };
  }

  /**
   * Get the current filter values for a module.
   * @param {string} moduleId
   * @returns {Object} — map of filterKey -> selectedValue
   */
  function getFilters(moduleId) {
    var state = _state.get(moduleId);
    if (!state) return {};
    // Return a shallow copy
    var copy = {};
    Object.keys(state.filters).forEach(function (k) {
      copy[k] = state.filters[k];
    });
    return copy;
  }

  /**
   * Get the current search query for a module.
   * @param {string} moduleId
   * @returns {string}
   */
  function getSearch(moduleId) {
    var state = _state.get(moduleId);
    return state ? state.search : '';
  }

  /**
   * Destroy a toolbar instance: remove DOM, clear state and timers.
   * @param {string} moduleId
   */
  function destroy(moduleId) {
    var state = _state.get(moduleId);
    if (!state) return;

    // Clear debounce timer
    if (state.debounceTimer) {
      clearTimeout(state.debounceTimer);
    }

    // Remove DOM element
    if (state.el && state.el.parentNode) {
      state.el.parentNode.removeChild(state.el);
    }

    _state.delete(moduleId);
  }

  /**
   * Replace the options of a specific filter select.
   *
   * Useful for cascading / dynamic filters whose option list changes at
   * runtime (e.g., a "value" filter that depends on a "type" filter).
   * Works for both visible and advanced filters (the lookup spans the
   * whole toolbar element, including the collapsed advanced panel).
   *
   * The first "All" option is preserved; all other options are replaced.
   *
   * @param {string} moduleId
   * @param {string} filterKey
   * @param {Array<{value: string, label: string}>} options — new options
   */
  function updateFilterOptions(moduleId, filterKey, options) {
    var state = _state.get(moduleId);
    if (!state) return;

    var selectEl = state.el.querySelector('[data-toolbar-filter="' + filterKey + '"]');
    if (!selectEl) return;

    // Remember current value so we can try to restore it
    var currentValue = selectEl.value;

    // Keep the first option (the "All" / "Tous" option) and remove the rest
    while (selectEl.options.length > 1) {
      selectEl.removeChild(selectEl.lastChild);
    }

    // Append new options
    if (options && options.length) {
      options.forEach(function (opt) {
        var optEl = el('option', { value: opt.value }, [opt.label]);
        selectEl.appendChild(optEl);
      });
    }

    // Try to restore previous selection; fall back to "All"
    selectEl.value = currentValue;
    if (selectEl.selectedIndex === -1) {
      selectEl.value = '';
    }

    // Update internal state to reflect the actual selection
    state.filters[filterKey] = selectEl.value;

    // The effective value may have changed (restored selection lost)
    _refreshMeta(moduleId);
  }

  // ---------------------------------------------------------------------------
  // Expose public API
  // ---------------------------------------------------------------------------

  return {
    init: init,
    getSort: getSort,
    getFilters: getFilters,
    getSearch: getSearch,
    destroy: destroy,
    updateFilterOptions: updateFilterOptions
  };
})();

// Make available globally for the SPA
window.SectionToolbar = SectionToolbar;
