/**
 * Spec 039 — Quem faz o quê (UX mockup Roger 2026-06-29)
 * Cards KPI · tabela · painel lateral · modal de edição
 */
(function () {
  'use strict';

  const LEVELS = [
    { id: 'full', label: 'Total', cls: 'qfx-level--full' },
    { id: 'approve', label: 'Aprovação', cls: 'qfx-level--approve' },
    { id: 'read', label: 'Leitura', cls: 'qfx-level--read' },
    { id: 'link', label: 'Link', cls: 'qfx-level--link' },
    { id: 'api', label: 'API', cls: 'qfx-level--api' },
    { id: 'none', label: 'Negado', cls: 'qfx-level--none' },
  ];

  const CHIP_COLORS = {
    SU: '#2563eb', CO: '#7c3aed', TEC: '#0284c7', NOC: '#64748b',
    SAD: '#9333ea', SSU: '#6d28d9', FIN: '#0891b2', MKT: '#ea580c',
    SEO: '#16a34a', DEV: '#0d9488', DVO: '#1d4ed8', SOC: '#dc2626',
    CMS: '#b45309', AIO: '#6b21a8', PTR: '#a16207',
  };

  const CHIP_BG = {
    SU: '#dbeafe', CO: '#ede9fe', TEC: '#e0f2fe', NOC: '#f1f5f9',
    SAD: '#f3e8ff', SSU: '#ede9fe', FIN: '#cffafe', MKT: '#ffedd5',
    SEO: '#dcfce7', DEV: '#ccfbf1', DVO: '#dbeafe', SOC: '#fee2e2',
    CMS: '#fef3c7', AIO: '#f3e8ff', PTR: '#fef3c7',
  };

  const KPI_ICONS = {
    action: '<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>',
    who: '<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="8" r="3"/><circle cx="17" cy="9" r="2.5"/><path d="M3 19c0-2.5 2.7-4.5 6-4.5s6 2 6 4.5"/><path d="M17 19c0-1.8 1.5-3.2 3.5-3.5"/></svg>',
    why: '<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="4"/><circle cx="12" cy="12" r="1" fill="currentColor"/></svg>',
  };

  let catalogState = null;
  let filterGroup = 'all';
  let searchQuery = '';
  let drawerActionId = null;
  let drawerDraft = null;
  let editModalOpen = false;
  let editDraft = null;
  let editModalHost = null;
  let saving = false;
  let saveError = null;
  let searchDebounceTimer = null;

  const EDIT_PORTAL_ID = 'qfx-edit-modal-portal';
  const BODY_LOCK_CLASS = 'qfx-scroll-lock';

  function esc(s) {
    return String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
  }

  async function loadCatalog() {
    const r = await fetchWithTimeout('/api/v1/rbac/actions', { headers: authHeaders() });
    if (!r.ok) throw new Error(`${r.status}`);
    catalogState = await r.json();
    return catalogState;
  }

  async function patchLevel(actionId, roleId, level, reset) {
    saving = true;
    saveError = null;
    const r = await fetchWithTimeout('/api/v1/rbac/actions/override', {
      method: 'PATCH',
      headers: authHeaders({ 'Content-Type': 'application/json' }),
      body: JSON.stringify({ action_id: actionId, role_id: roleId, level, reset: !!reset }),
    });
    saving = false;
    if (!r.ok) throw new Error((await r.text()).slice(0, 200) || String(r.status));
    return r.json();
  }

  function roleCode(roleId) {
    return catalogState?.role_codes?.[roleId] || roleId?.slice(0, 3).toUpperCase();
  }

  function roleLabel(roleId) {
    return catalogState?.roles?.[roleId]?.label || roleId;
  }

  function levelMeta(level) {
    return LEVELS.find((l) => l.id === level) || { id: level, label: level, cls: 'qfx-level--read' };
  }

  function whyForAction(action) {
    if (action.why) return action.why;
    const map = catalogState?.executive_map || [];
    const hit = map.find((r) => r.action_ids?.includes(action.id));
    return hit?.why || 'Operação registada no catálogo Spec 039';
  }

  function rolesWithAccess(action) {
    const roles = catalogState?.roles || {};
    return Object.keys(roles).filter((rid) => {
      const lv = action.effective?.[rid] || 'none';
      return lv !== 'none';
    });
  }

  function filteredActions() {
    let list = catalogState?.actions || [];
    if (filterGroup !== 'all') list = list.filter((a) => a.group === filterGroup);
    const q = searchQuery.trim().toLowerCase();
    if (q) {
      list = list.filter((a) =>
        a.id.toLowerCase().includes(q)
        || (a.label || '').toLowerCase().includes(q)
        || whyForAction(a).toLowerCase().includes(q)
      );
    }
    return list;
  }

  function kpiStats(actions) {
    const roles = catalogState?.roles || {};
    const roleIds = Object.keys(roles);
    let activeProfiles = 0;
    roleIds.forEach((rid) => {
      if (actions.some((a) => (a.effective?.[rid] || 'none') !== 'none')) activeProfiles += 1;
    });
    const purposes = new Set(actions.map((a) => whyForAction(a)));
    return {
      actions: actions.length,
      profiles: activeProfiles || roleIds.length,
      purposes: purposes.size,
    };
  }

  function descForAction(action) {
    return `Permite ${(action.label || '').toLowerCase()} no ecossistema Ligbox OPS.`;
  }

  function finalidadeSub(action) {
    const g = (catalogState?.groups || []).find((x) => x.id === action.group);
    return g?.label || 'Operação registada no catálogo';
  }

  function splitTagHtml(roleId, on, editable, toggle, compact) {
    const code = roleCode(roleId);
    const bg = CHIP_BG[code] || '#f1f5f9';
    const fg = CHIP_COLORS[code] || '#64748b';
    const cls = `qfx-split-tag${on ? ' on' : ''}${!on ? ' off' : ''}${compact ? ' qfx-split-tag--compact' : ''}`;
    const attrs = toggle && editable
      ? ` type="button" data-qfx-toggle-role="${esc(roleId)}"`
      : '';
    const tag = toggle && editable ? 'button' : 'span';
    return `<${tag} class="${cls}"${attrs} style="--tag-bg:${bg};--tag-fg:${fg}">
      <span class="qfx-split-tag__code">${esc(code)}</span>
      <span class="qfx-split-tag__name">${esc(roleLabel(roleId))}</span>
    </${tag}>`;
  }

  function chipHtml(roleId) {
    return splitTagHtml(roleId, true, false, false, true);
  }

  function renderKpiCards(stats) {
    const cards = [
      { key: 'action', label: 'AÇÃO', sub: 'O que pode ser feito no sistema', val: stats.actions, foot: 'ações cadastradas', mod: 'action' },
      { key: 'who', label: 'QUEM REALIZA', sub: 'Perfis/Grupos que executam', val: stats.profiles, foot: 'perfis e grupos', mod: 'who' },
      { key: 'why', label: 'POR QUÊ', sub: 'Finalidade da ação', val: stats.purposes, foot: 'finalidades mapeadas', mod: 'why' },
    ];
    return `<div class="qfx-kpi-grid">${cards.map((c) => `
      <article class="qfx-kpi qfx-kpi--${c.mod}">
        <div class="qfx-kpi-body">
          <h3 class="qfx-kpi-label">${c.label}</h3>
          <p class="qfx-kpi-subtitle">${c.sub}</p>
          <strong class="qfx-kpi-val">${c.val}</strong>
          <span class="qfx-kpi-foot">${c.foot}</span>
        </div>
        <div class="qfx-kpi-icon-ring" aria-hidden="true">${KPI_ICONS[c.mod]}</div>
      </article>`).join('')}</div>`;
  }

  function renderToolbar(groups) {
    const groupOpts = ['<option value="all">Todos os grupos</option>']
      .concat(groups.map((g) =>
        `<option value="${esc(g.id)}"${filterGroup === g.id ? ' selected' : ''}>${esc(g.label)}</option>`
      )).join('');
    return `
      <div class="qfx-toolbar">
        <label class="qfx-field">
          <span>Grupo</span>
          <select class="qfx-select" data-qfx-group>${groupOpts}</select>
        </label>
        <label class="qfx-field qfx-field--search">
          <span class="sr-only">Buscar</span>
          <input type="search" class="qfx-search" data-qfx-search placeholder="Buscar acção, perfil ou nível…" value="${esc(searchQuery)}"/>
        </label>
        ${filterGroup !== 'all' || searchQuery ? '<button type="button" class="qfx-btn-ghost" data-qfx-clear-filters>Limpar filtros</button>' : ''}
      </div>`;
  }

  function renderActionsTable(actions, selectedRole, editable) {
    const rows = actions.map((a) => {
      const eff = a.effective?.[selectedRole] || 'none';
      const lm = levelMeta(eff);
      const who = rolesWithAccess(a);
      const isOpen = drawerActionId === a.id;
      const levelOpts = LEVELS.map((l) =>
        `<option value="${l.id}"${eff === l.id ? ' selected' : ''}>${esc(l.label)}</option>`
      ).join('');

      return `
        <tr class="qfx-row${isOpen ? ' qfx-row--open' : ''}${a.overridden_roles?.includes(selectedRole) ? ' qfx-row--custom' : ''}" data-qfx-action="${esc(a.id)}">
          <td class="qfx-col-action">
            <strong class="qfx-action-title">${esc(a.label)}</strong>
            <code class="qfx-action-id">${esc(a.id)}</code>
          </td>
          <td class="qfx-col-who">${who.map(chipHtml).join('') || '<span class="qfx-muted">—</span>'}</td>
          <td class="qfx-col-why">${esc(whyForAction(a))}</td>
          <td class="qfx-col-level">
            <select class="qfx-level-select ${lm.cls}" data-qfx-level data-action-id="${esc(a.id)}" data-role-id="${esc(selectedRole)}" ${!editable || saving ? 'disabled' : ''}>
              ${levelOpts}
            </select>
          </td>
          <td class="qfx-col-ops">
            <button type="button" class="qfx-icon-btn" data-qfx-open="${esc(a.id)}" title="Ver detalhe">👁</button>
            <button type="button" class="qfx-icon-btn" data-qfx-open="${esc(a.id)}" title="Editar">⋯</button>
          </td>
        </tr>`;
    }).join('');

    return `
      <section class="qfx-table-block">
        <header class="qfx-table-head">
          <h4>Ações e permissões</h4>
          <span class="qfx-table-count">${actions.length} resultado${actions.length === 1 ? '' : 's'}</span>
        </header>
        <div class="qfx-table-wrap">
          <table class="qfx-table">
            <thead>
              <tr>
                <th>AÇÃO</th>
                <th>QUEM REALIZA</th>
                <th>POR QUÊ</th>
                <th>NÍVEL · ${esc(roleCode(selectedRole))}</th>
                <th>AÇÕES</th>
              </tr>
            </thead>
            <tbody>${rows || '<tr><td colspan="5" class="qfx-empty">Nenhuma acção encontrada.</td></tr>'}</tbody>
          </table>
        </div>
      </section>`;
  }

  function renderSidePanel(selectedRole, editable) {
    if (!drawerActionId || !drawerDraft) return '';
    const a = drawerDraft;
    const groups = catalogState?.groups || [];
    const roleIds = Object.keys(catalogState?.roles || {}).filter((r) => !['api_service', 'agent_system'].includes(r));
    const effLevel = a.effective?.[selectedRole] || 'none';
    const lm = levelMeta(effLevel);

    const roleBadges = roleIds.map((rid) => {
      const lv = a.effective?.[rid] || 'none';
      return splitTagHtml(rid, lv !== 'none', false, false);
    }).join('');

    const groupLabel = groups.find((g) => g.id === a.group)?.label || a.group || '—';

    return `
      <aside class="qfx-side-panel" aria-label="Detalhe da acção">
        <article class="qfx-side-card">
          <header class="qfx-modal-head">
            <div class="qfx-modal-head-text">
              <h3>${esc(a.label)}</h3>
              <span class="qfx-tag-pill">${esc(a.id)}</span>
            </div>
            <button type="button" class="qfx-modal-close" data-qfx-close-panel aria-label="Fechar painel">×</button>
          </header>
          <div class="qfx-modal-body">
            <section class="qfx-modal-block">
              <h4>Descrição</h4>
              <p>${esc(descForAction(a))}</p>
            </section>
            <section class="qfx-modal-block">
              <h4>Finalidade</h4>
              <p class="qfx-finalidade-main">${esc(whyForAction(a))}</p>
              <p class="qfx-finalidade-sub">${esc(finalidadeSub(a))}</p>
            </section>
            <section class="qfx-modal-block">
              <h4>Quem realiza</h4>
              <div class="qfx-split-tag-grid">${roleBadges || '<span class="qfx-muted">Nenhum perfil</span>'}</div>
            </section>
            <section class="qfx-modal-block">
              <h4>Nível padrão · ${esc(roleLabel(selectedRole))}</h4>
              <span class="qfx-level-badge ${lm.cls}">${esc(lm.label)}</span>
            </section>
            <section class="qfx-modal-block">
              <h4>Grupos</h4>
              <p class="qfx-readonly-field">${esc(groupLabel)}</p>
            </section>
          </div>
          <footer class="qfx-modal-foot">
            ${editable ? `
              <button type="button" class="qfx-btn-edit qfx-btn-edit--full" data-qfx-open-edit ${saving ? 'disabled' : ''}>
                <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>
                Editar permissão
              </button>` : '<span class="qfx-muted qfx-readonly-hint">Modo consulta</span>'}
          </footer>
        </article>
      </aside>`;
  }

  function renderEditModal(selectedRole, editable) {
    if (!editModalOpen || !editDraft) return '';
    const a = editDraft;
    const groups = catalogState?.groups || [];
    const roleIds = Object.keys(catalogState?.roles || {}).filter((r) => !['api_service', 'agent_system'].includes(r));
    const effLevel = a.effective?.[selectedRole] || 'none';
    const lm = levelMeta(effLevel);

    const roleBadges = roleIds.map((rid) => {
      const lv = a.effective?.[rid] || 'none';
      return splitTagHtml(rid, lv !== 'none', true, true);
    }).join('');

    const levelOpts = LEVELS.map((l) =>
      `<option value="${l.id}"${effLevel === l.id ? ' selected' : ''}>${esc(l.label)}</option>`
    ).join('');

    const groupOpts = groups.map((g) =>
      `<option value="${esc(g.id)}"${a.group === g.id ? ' selected' : ''}>${esc(g.label)}</option>`
    ).join('');

    return `
      <div class="qfx-edit-modal-root" role="dialog" aria-modal="true" aria-label="Editar permissão">
        <div class="qfx-backdrop" data-qfx-backdrop></div>
        <article class="qfx-modal-card qfx-modal-card--edit" data-qfx-edit-card>
          <header class="qfx-modal-head">
            <div class="qfx-modal-head-text">
              <h3>Editar permissão</h3>
              <span class="qfx-tag-pill">${esc(a.id)}</span>
              <p class="qfx-edit-subtitle">${esc(a.label)}</p>
            </div>
            <button type="button" class="qfx-modal-close" data-qfx-close-edit aria-label="Fechar">×</button>
          </header>
          <div class="qfx-modal-body">
            <section class="qfx-modal-block">
              <h4>Quem realiza</h4>
              <p class="qfx-hint">Clique nos perfis para activar (Total) ou desactivar (Negado).</p>
              <div class="qfx-split-tag-grid">${roleBadges}</div>
            </section>
            <section class="qfx-modal-block">
              <h4>Nível · ${esc(roleLabel(selectedRole))}</h4>
              <div class="qfx-level-field">
                <select class="qfx-level-select qfx-level-select--modal ${lm.cls}" data-qfx-edit-level data-role-id="${esc(selectedRole)}" ${!editable ? 'disabled' : ''}>
                  ${levelOpts}
                </select>
              </div>
            </section>
            <section class="qfx-modal-block">
              <h4>Grupos</h4>
              <select class="qfx-select qfx-select--modal" disabled title="Grupo fixo por acção">
                ${groupOpts}
              </select>
            </section>
            ${saveError ? `<p class="qfx-error">${esc(saveError)}</p>` : ''}
          </div>
          <footer class="qfx-modal-foot">
            <button type="button" class="qfx-btn-cancel" data-qfx-close-edit>Cancelar</button>
            <button type="button" class="qfx-btn-edit" data-qfx-save ${!editable || saving ? 'disabled' : ''}>
              <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg>
              Guardar alterações
            </button>
          </footer>
        </article>
      </div>`;
  }

  function syncBodyScrollLock() {
    document.body.classList.toggle(BODY_LOCK_CLASS, !!editModalOpen);
  }

  function repaintEditModalOnly(selectedRole, editable, onFullRepaint) {
    mountEditModal(renderEditModal(selectedRole, editable));
    bindEditModalEvents(onFullRepaint, selectedRole, editable);
    syncBodyScrollLock();
  }

  function updateListArea(host, selectedRole, editable, groups) {
    const area = host.querySelector('[data-qfx-list-area]');
    if (!area) return false;
    const actions = filteredActions();
    const stats = kpiStats(catalogState?.actions || []);
    area.innerHTML = `
      ${renderToolbar(groups)}
      ${renderKpiCards(stats)}
      ${saveError && !editModalOpen ? `<p class="qfx-error">${esc(saveError)}</p>` : ''}
      ${renderActionsTable(actions, selectedRole, editable)}`;
    return true;
  }

  function updateSideArea(host, selectedRole, editable) {
    const slot = host.querySelector('[data-qfx-side-slot]');
    const layout = host.querySelector('.qfx-layout');
    if (!slot || !layout) return;
    layout.classList.toggle('qfx-layout--panel', !!drawerActionId);
    slot.innerHTML = renderSidePanel(selectedRole, editable);
  }
  function ensureEditPortal() {
    let portal = document.getElementById(EDIT_PORTAL_ID);
    if (!portal) {
      portal = document.createElement('div');
      portal.id = EDIT_PORTAL_ID;
      portal.className = 'qfx-edit-portal';
      document.body.appendChild(portal);
    }
    return portal;
  }

  function clearEditPortal() {
    const portal = document.getElementById(EDIT_PORTAL_ID);
    if (portal) portal.innerHTML = '';
    editModalHost = null;
    document.body.classList.remove(BODY_LOCK_CLASS);
  }

  function mountEditModal(html) {
    if (!html) {
      clearEditPortal();
      return null;
    }
    const portal = ensureEditPortal();
    portal.innerHTML = html;
    editModalHost = portal;
    return portal;
  }
  function openDrawer(actionId) {
    const a = (catalogState?.actions || []).find((x) => x.id === actionId);
    if (!a) return;
    drawerActionId = actionId;
    drawerDraft = JSON.parse(JSON.stringify(a));
    editModalOpen = false;
    editDraft = null;
    clearEditPortal();
    saveError = null;
  }

  function closeDrawer() {
    drawerActionId = null;
    drawerDraft = null;
    editModalOpen = false;
    editDraft = null;
    clearEditPortal();
    saveError = null;
  }

  function openEditModal() {
    if (!drawerDraft) return;
    editDraft = JSON.parse(JSON.stringify(drawerDraft));
    if (!editDraft.effective) editDraft.effective = {};
    editDraft._dirty = false;
    editModalOpen = true;
    saveError = null;
  }

  function closeEditModal() {
    editModalOpen = false;
    editDraft = null;
    clearEditPortal();
    saveError = null;
  }

  async function saveEditModal() {
    if (!editDraft || !drawerActionId) return;
    const orig = (catalogState.actions || []).find((a) => a.id === drawerActionId);
    if (!orig) return;
    saving = true;
    saveError = null;
    try {
      const roleIds = Object.keys(catalogState?.roles || {});
      for (const rid of roleIds) {
        const newLv = editDraft.effective?.[rid] || 'none';
        const oldLv = orig.effective?.[rid] || 'none';
        const def = orig.defaults?.[rid] || 'none';
        if (newLv !== oldLv) {
          await patchLevel(drawerActionId, rid, newLv, newLv === def);
        }
      }
      await loadCatalog();
      const fresh = (catalogState.actions || []).find((a) => a.id === drawerActionId);
      if (fresh) drawerDraft = JSON.parse(JSON.stringify(fresh));
      closeEditModal();
    } catch (err) {
      saveError = err.message;
    } finally {
      saving = false;
    }
  }

  function bindEditModalEvents(onFullRepaint, selectedRole, editable) {
    const root = editModalHost || document.getElementById(EDIT_PORTAL_ID);
    if (!root) return;
    const onEditRefresh = () => repaintEditModalOnly(selectedRole, editable, onFullRepaint);

    root.querySelector('[data-qfx-backdrop]')?.addEventListener('click', (e) => {
      if (e.target !== e.currentTarget) return;
      closeEditModal();
      syncBodyScrollLock();
      onFullRepaint({ side: true, list: true });
    });

    root.querySelectorAll('[data-qfx-close-edit]').forEach((el) => {
      el.addEventListener('click', (e) => {
        e.stopPropagation();
        closeEditModal();
        syncBodyScrollLock();
        onFullRepaint({ side: true });
      });
    });

    root.querySelector('[data-qfx-edit-card]')?.addEventListener('mousedown', (e) => {
      e.stopPropagation();
    });

    root.querySelectorAll('[data-qfx-toggle-role]').forEach((btn) => {
      btn.addEventListener('click', (e) => {
        e.preventDefault();
        e.stopPropagation();
        if (!catalogState?.editable || !editDraft) return;
        if (!editDraft.effective) editDraft.effective = {};
        const rid = btn.dataset.qfxToggleRole;
        const cur = editDraft.effective[rid] || 'none';
        editDraft.effective[rid] = cur === 'none' ? 'full' : 'none';
        editDraft._dirty = true;
        onEditRefresh();
      });
    });

    root.querySelector('[data-qfx-edit-level]')?.addEventListener('change', (e) => {
      e.stopPropagation();
      if (!editDraft) return;
      if (!editDraft.effective) editDraft.effective = {};
      const rid = e.target.dataset.roleId;
      editDraft.effective[rid] = e.target.value;
      editDraft._dirty = true;
      onEditRefresh();
    });

    root.querySelector('[data-qfx-save]')?.addEventListener('click', async (e) => {
      e.preventDefault();
      e.stopPropagation();
      await saveEditModal();
      syncBodyScrollLock();
      onFullRepaint({ side: true, list: true });
    });
  }

  function bindEvents(host, selectedRole, editable, groups, onRefresh) {
    host.querySelector('[data-qfx-group]')?.addEventListener('change', (e) => {
      filterGroup = e.target.value;
      onRefresh({ list: true });
    });

    const searchInput = host.querySelector('[data-qfx-search]');
    searchInput?.addEventListener('input', (e) => {
      searchQuery = e.target.value;
      clearTimeout(searchDebounceTimer);
      searchDebounceTimer = setTimeout(() => onRefresh({ list: true }), 280);
    });

    host.querySelector('[data-qfx-clear-filters]')?.addEventListener('click', () => {
      filterGroup = 'all';
      searchQuery = '';
      if (searchInput) searchInput.value = '';
      onRefresh({ list: true });
    });

    host.querySelectorAll('[data-qfx-level]').forEach((sel) => {
      sel.addEventListener('change', async () => {
        if (!catalogState?.editable) return;
        const actionId = sel.dataset.actionId;
        const roleId = sel.dataset.roleId;
        const level = sel.value;
        const action = (catalogState.actions || []).find((a) => a.id === actionId);
        const def = action?.defaults?.[roleId] || 'none';
        try {
          await patchLevel(actionId, roleId, level, level === def);
          await loadCatalog();
          saveError = null;
          onRefresh({ list: true, side: true });
        } catch (err) {
          saveError = err.message;
          onRefresh({ list: true });
        }
      });
    });

    host.querySelectorAll('.qfx-row').forEach((row) => {
      row.addEventListener('click', (e) => {
        if (editModalOpen) return;
        if (e.target.closest('select, button')) return;
        openDrawer(row.dataset.qfxAction);
        onRefresh({ list: true, side: true });
      });
    });

    host.querySelectorAll('[data-qfx-open]').forEach((btn) => {
      btn.addEventListener('click', (e) => {
        e.stopPropagation();
        openDrawer(btn.dataset.qfxOpen);
        onRefresh({ list: true, side: true });
      });
    });

    host.querySelectorAll('[data-qfx-close-panel]').forEach((el) => {
      el.addEventListener('click', () => {
        closeDrawer();
        onRefresh({ list: true, side: true });
      });
    });

    host.querySelector('[data-qfx-open-edit]')?.addEventListener('click', (e) => {
      e.stopPropagation();
      openEditModal();
      syncBodyScrollLock();
      repaintEditModalOnly(selectedRole, editable, () => onRefresh({ side: true, list: true }));
    });
  }

  function syncDraftsFromCatalog() {
    if (drawerActionId) {
      const fresh = (catalogState.actions || []).find((a) => a.id === drawerActionId);
      if (fresh && drawerDraft && !editModalOpen) {
        drawerDraft.label = fresh.label;
        drawerDraft.group = fresh.group;
        drawerDraft.defaults = fresh.defaults;
        drawerDraft.effective = { ...fresh.effective };
      }
    }
    if (editModalOpen && editDraft && drawerActionId) {
      const fresh = (catalogState.actions || []).find((a) => a.id === drawerActionId);
      if (fresh && !editDraft._dirty) {
        editDraft.label = fresh.label;
        editDraft.group = fresh.group;
        editDraft.defaults = fresh.defaults;
        editDraft.effective = { ...fresh.effective };
      }
    }
  }

  function refreshView(host, selectedRole, editable, groups, scope = { all: true }) {
    syncDraftsFromCatalog();

    if (scope.all) {
      updateListArea(host, selectedRole, editable, groups);
      updateSideArea(host, selectedRole, editable);
      if (editModalOpen) {
        repaintEditModalOnly(selectedRole, editable, () => refreshView(host, selectedRole, editable, groups, { all: true }));
      } else {
        clearEditPortal();
      }
    } else {
      if (scope.list) updateListArea(host, selectedRole, editable, groups);
      if (scope.side) updateSideArea(host, selectedRole, editable);
      if (scope.edit && editModalOpen) {
        repaintEditModalOnly(selectedRole, editable, () => refreshView(host, selectedRole, editable, groups, { all: true }));
      }
    }

    bindEvents(host, selectedRole, editable, groups, (partial) => {
      refreshView(host, selectedRole, editable, groups, partial);
    });
    if (editModalOpen) {
      bindEditModalEvents(
        () => refreshView(host, selectedRole, editable, groups, { all: true }),
        selectedRole,
        editable
      );
    }
  }

  async function paint(host, opts = {}) {
    if (!host) return;
    const selectedRole = opts.selectedRole || 'super_admin';
    try {
      if (!catalogState) {
        host.innerHTML = '<p class="loading">Carregando Quem faz o quê…</p>';
        await loadCatalog();
      }
    } catch (e) {
      host.innerHTML = `<p class="loading">Catálogo indisponível: ${esc(e.message)}</p>`;
      clearEditPortal();
      return;
    }

    const editable = !!catalogState?.editable;
    const groups = catalogState?.groups || [];
    syncDraftsFromCatalog();

    host.innerHTML = `
      <div class="qfx-layout${drawerActionId ? ' qfx-layout--panel' : ''}">
        <div class="qfx-main">
          <header class="qfx-page-head">
            <div>
              <h2 class="qfx-title">Mapa executivo — Quem faz o quê</h2>
              <p class="qfx-subtitle">Controle visual por acção, perfil e finalidade · Spec 039</p>
            </div>
            ${editable ? '<span class="qfx-badge-edit">Edição activa</span>' : '<span class="qfx-badge-read">Consulta</span>'}
          </header>
          <div data-qfx-list-area></div>
        </div>
        <div data-qfx-side-slot></div>
      </div>`;

    refreshView(host, selectedRole, editable, groups, { all: true });
  }

  function resetFilters() {
    filterGroup = 'all';
    searchQuery = '';
    clearTimeout(searchDebounceTimer);
    drawerActionId = null;
    drawerDraft = null;
    editModalOpen = false;
    editDraft = null;
    clearEditPortal();
    catalogState = null;
  }

  window.DeskExecutiveMap = { paint, resetFilters, loadCatalog };
})();
