/* coltweak — per-table column customization: show/hide + drag-reorder, persisted per table in localStorage
 * (remembered across sessions until the user Resets). No dependency; leaves the table's own sorting / paging /
 * filtering untouched (it only hides + reorders existing cells).
 *
 * Enroll a table:
 *   <table data-coltweak="unique-key"> … </table>
 *   - each customizable header cell:  <th data-col="slug">Label</th>   (label = th text, or data-collabel="…")
 *   - pinned cells (checkbox / row actions): add data-colfixed  → excluded from the menu, never moved/hidden
 * Body & foot cells need NO attributes — they align to the header by position. Any row whose cell count does
 * not match the header (e.g. a "no rows" colspan row) is left completely untouched.
 * A "Columns" button is auto-inserted just above the table. Reset restores the original layout.
 */
(function () {
  function ready(fn) { if (document.readyState !== 'loading') fn(); else document.addEventListener('DOMContentLoaded', fn); }
  function esc(s) { return (s == null ? '' : String(s)).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); }

  injectCss();
  ready(function () { [].forEach.call(document.querySelectorAll('table[data-coltweak]'), function (t) { try { initTable(t); } catch (e) { /* never break the page over a table widget */ } }); });

  function initTable(table) {
    var key = table.getAttribute('data-coltweak'); if (!key) return;
    var headRow = table.tHead && table.tHead.rows.length ? table.tHead.rows[0] : null; if (!headRow) return;
    var ths = [].slice.call(headRow.cells);

    // Customizable columns = header cells with data-col and NOT data-colfixed.
    var cols = [];
    ths.forEach(function (th, i) {
      var slug = th.getAttribute('data-col');
      if (slug && th.getAttribute('data-colfixed') === null) {
        cols.push({ slug: slug, idx: i, label: (th.getAttribute('data-collabel') || th.textContent || slug).trim() || slug });
      }
    });
    if (cols.length < 2) return;                                   // nothing meaningful to customize
    var origSlugs = cols.map(function (c) { return c.slug; });

    // Collect the rows we manage (header row 0 + body + foot) and TAG each customizable cell with its slug, so
    // show/hide + reorder work by slug and stay correct no matter how many times they run.
    var managed = allRows(table);
    managed.forEach(function (row) {
      if (row.cells.length !== ths.length) return;                 // skip colspan / mismatched rows
      cols.forEach(function (c) { row.cells[c.idx]._ctSlug = c.slug; });
    });

    var state = load(key, origSlugs);
    apply();

    /* ---- Columns button + panel (inserted above the table) ---- */
    var wrap = document.createElement('div'); wrap.className = 'coltweak-bar';
    var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'btn btn-sm btn-outline-secondary';
    btn.innerHTML = '<i class="ti ti-columns-3 me-1"></i>Columns';
    var panel = document.createElement('div'); panel.className = 'coltweak-panel'; panel.style.display = 'none';
    wrap.appendChild(btn); wrap.appendChild(panel);
    var anchor = table.closest('.table-responsive') || table;
    if (anchor.parentNode) anchor.parentNode.insertBefore(wrap, anchor);

    btn.addEventListener('click', function (e) { e.stopPropagation(); var open = panel.style.display === 'none'; panel.style.display = open ? '' : 'none'; if (open) renderPanel(); });
    document.addEventListener('click', function (e) { if (!wrap.contains(e.target)) panel.style.display = 'none'; });

    function colBySlug(s) { for (var i = 0; i < cols.length; i++) if (cols[i].slug === s) return cols[i]; return null; }

    function renderPanel() {
      var items = state.order.map(function (slug) {
        var c = colBySlug(slug); if (!c) return '';
        return '<label class="coltweak-item" draggable="true" data-slug="' + esc(slug) + '">' +
          '<i class="ti ti-grip-vertical coltweak-grip"></i>' +
          '<input type="checkbox" class="form-check-input coltweak-vis" ' + (state.hidden[slug] ? '' : 'checked') + '> ' +
          '<span>' + esc(c.label) + '</span></label>';
      }).join('');
      panel.innerHTML = '<div class="coltweak-head"><span>Show / drag to reorder</span><a href="#" class="coltweak-reset">Reset</a></div>' + items;
      [].forEach.call(panel.querySelectorAll('.coltweak-vis'), function (cb) {
        cb.addEventListener('change', function () {
          var slug = cb.closest('.coltweak-item').getAttribute('data-slug');
          if (cb.checked) delete state.hidden[slug]; else state.hidden[slug] = true;
          save(); apply();
        });
      });
      panel.querySelector('.coltweak-reset').addEventListener('click', function (e) { e.preventDefault(); reset(); });
      wireDnd();
    }

    /* ---- drag-reorder inside the panel ---- */
    var dragSlug = null;
    function wireDnd() {
      [].forEach.call(panel.querySelectorAll('.coltweak-item'), function (it) {
        it.addEventListener('dragstart', function () { dragSlug = it.getAttribute('data-slug'); it.classList.add('coltweak-drag'); });
        it.addEventListener('dragend', function () { it.classList.remove('coltweak-drag'); dragSlug = null; });
        it.addEventListener('dragover', function (e) { e.preventDefault(); });
        it.addEventListener('drop', function (e) {
          e.preventDefault(); var target = it.getAttribute('data-slug'); if (!dragSlug || dragSlug === target) return;
          var o = state.order.slice(); o.splice(o.indexOf(dragSlug), 1); o.splice(o.indexOf(target), 0, dragSlug);
          state.order = o; save(); apply(); renderPanel();
        });
      });
    }

    /* ---- apply order + visibility to the DOM (idempotent, by slug) ---- */
    function apply() {
      // Keep the FULL remembered order (a table with conditional columns renders different subsets per view — e.g.
      // CPT/ICD only on completed views; we must not drop those slugs while on a view that lacks them, or their saved
      // position would be lost). Ensure every column present NOW is tracked (append new), then render the subset.
      origSlugs.forEach(function (s) { if (state.order.indexOf(s) < 0) state.order.push(s); });
      var seq = state.order.filter(function (s) { return origSlugs.indexOf(s) >= 0; });   // render sequence = remembered order limited to columns present in this view
      allRows(table).forEach(function (row) {
        if (row.cells.length !== ths.length) return;                                       // skip colspan / mismatched rows
        var cells = [].slice.call(row.cells);
        var bySlug = {}, movablePos = [];
        cells.forEach(function (c, i) { if (c._ctSlug) { bySlug[c._ctSlug] = c; movablePos.push(i); } });
        var out = cells.slice();
        movablePos.forEach(function (pos, k) { if (bySlug[seq[k]]) out[pos] = bySlug[seq[k]]; });   // place movable cells in seq order; fixed cells keep their slot
        out.forEach(function (c) { row.appendChild(c); });                                 // re-append in the new order
      });
      applyVisibility();
    }
    function applyVisibility() {
      var curThs = [].slice.call(table.tHead.rows[0].cells);
      curThs.forEach(function (th) { if (th._ctSlug) th.style.display = state.hidden[th._ctSlug] ? 'none' : ''; });
      allRows(table).forEach(function (row) {
        if (row === table.tHead.rows[0]) return;
        if (row.cells.length !== curThs.length) return;
        [].forEach.call(row.cells, function (td) { if (td._ctSlug) td.style.display = state.hidden[td._ctSlug] ? 'none' : ''; });
      });
    }

    function reset() { state = { order: origSlugs.slice(), hidden: {} }; try { localStorage.removeItem('coltweak:' + key); } catch (e) {} apply(); renderPanel(); }
    function save() { try { localStorage.setItem('coltweak:' + key, JSON.stringify({ order: state.order, hidden: Object.keys(state.hidden) })); } catch (e) {} }
  }

  function allRows(table) {
    var rows = [];
    if (table.tHead && table.tHead.rows.length) rows.push(table.tHead.rows[0]);
    [].forEach.call(table.tBodies, function (tb) { [].push.apply(rows, tb.rows); });
    if (table.tFoot) [].push.apply(rows, table.tFoot.rows);
    return rows;
  }
  function load(key, origSlugs) {
    var d = { order: origSlugs.slice(), hidden: {} };
    try {
      var raw = localStorage.getItem('coltweak:' + key);
      if (raw) { var o = JSON.parse(raw); if (o && Array.isArray(o.order)) d.order = o.order; if (o && Array.isArray(o.hidden)) o.hidden.forEach(function (s) { d.hidden[s] = true; }); }
    } catch (e) {}
    return d;
  }
  function injectCss() {
    if (document.getElementById('coltweak-css')) return;
    var s = document.createElement('style'); s.id = 'coltweak-css';
    s.textContent = '.coltweak-bar{display:inline-block;position:relative;margin:0 0 .4rem}'
      + '.coltweak-panel{position:absolute;z-index:1090;top:100%;left:0;min-width:230px;background:var(--m-bg-elev,#fff);border:1px solid var(--m-border-strong,#b7c0cd);border-radius:8px;box-shadow:0 12px 32px rgba(0,0,0,.18);padding:.35rem;max-height:60vh;overflow:auto}'
      + '.coltweak-head{display:flex;justify-content:space-between;align-items:center;font-weight:600;font-size:.75rem;padding:.2rem .4rem .45rem;color:var(--m-text-soft,#6c7a89)}'
      + '.coltweak-reset{font-size:.75rem}'
      + '.coltweak-item{display:flex;align-items:center;gap:.45rem;padding:.28rem .4rem;border-radius:6px;cursor:grab;font-size:.85rem;color:var(--m-text,#1f2733);user-select:none}'
      + '.coltweak-item:hover{background:rgba(79,70,229,.08)}.coltweak-grip{opacity:.45}.coltweak-drag{opacity:.4}';
    document.head.appendChild(s);
  }
})();
