(function () {
  'use strict';

  const esc = (s) => String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');

  const LEVEL_SHORT = {
    full: 'ADM',
    read: 'READ',
    link: 'LINK',
    api: 'API',
    system: 'SYS',
    none: '—',
  };

  const state = {
    data: null,
    tab: 'overview',
    selectedRole: 'super_admin',
    softwareFilter: 'all',
    saving: false,
    saveError: null,
    moduleDraft: null,
    bindingDraft: null,
  };

  async function crudApi(method, path, body) {
    const opts = {
      method,
      headers: authHeaders({ 'Content-Type': 'application/json' }),
    };
    if (body !== undefined) opts.body = JSON.stringify(body);
    const r = await fetchWithTimeout(`/api/v1${path}`, opts);
    if (!r.ok) throw new Error(`${r.status} ${(await r.text()).slice(0, 200)}`);
    if (method === 'DELETE' || r.status === 204) return {};
    const ct = r.headers.get('content-type') || '';
    if (ct.includes('json')) return r.json();
    return r.text();
  }

  async function reloadMatrix() {
    state.data = await api('/rbac/matrix');
    state.moduleDraft = null;
    state.bindingDraft = null;
  }

  function selectedRoleMeta() {
    return state.data?.catalog?.roles?.[state.selectedRole] || {};
  }

  function canCrud() {
    return !!state.data?.crud_enabled;
  }

  function isRoleLocked() {
    return !!selectedRoleMeta().locked;
  }

  async function handleNewRole() {
    const id = window.prompt('ID da função (slug, ex.: billing_analyst):');
    if (!id) return;
    const label = window.prompt('Nome exibido (pt-BR):', id.replace(/_/g, ' '));
    if (!label) return;
    state.saving = true;
    state.saveError = null;
    try {
      await crudApi('POST', '/rbac/roles', {
        id: id.trim().toLowerCase(),
        label: label.trim(),
        category: 'custom',
        description: '',
      });
      await reloadMatrix();
      state.selectedRole = id.trim().toLowerCase();
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  async function handleCloneRole() {
    const newId = window.prompt(`Copiar ${state.selectedRole} — novo ID (slug):`);
    if (!newId) return;
    const newLabel = window.prompt('Nome da cópia:', `${selectedRoleMeta().label || state.selectedRole} (cópia)`);
    if (!newLabel) return;
    state.saving = true;
    try {
      await crudApi('POST', `/rbac/roles/${encodeURIComponent(state.selectedRole)}/clone`, {
        new_id: newId.trim().toLowerCase(),
        new_label: newLabel.trim(),
      });
      await reloadMatrix();
      state.selectedRole = newId.trim().toLowerCase();
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  async function handleFreezeRole() {
    const meta = selectedRoleMeta();
    const next = meta.status === 'frozen' ? 'active' : 'frozen';
    const msg = next === 'frozen' ? 'Pausar esta função? Novas atribuições serão bloqueadas.' : 'Reactivar função?';
    if (!window.confirm(msg)) return;
    state.saving = true;
    try {
      await crudApi('PATCH', `/rbac/roles/${encodeURIComponent(state.selectedRole)}/status`, { status: next });
      await reloadMatrix();
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  async function handleDeleteRole() {
    if (!window.confirm(`Arquivar função ${state.selectedRole}? Falha se houver utilizadores activos.`)) return;
    state.saving = true;
    try {
      await crudApi('DELETE', `/rbac/roles/${encodeURIComponent(state.selectedRole)}`);
      await reloadMatrix();
      state.selectedRole = state.data.role_columns?.[0] || 'super_admin';
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  function handleReportDownload() {
    const token = localStorage.getItem('desk_token') || sessionStorage.getItem('desk_token');
    const url = `/api/v1/rbac/roles/${encodeURIComponent(state.selectedRole)}/report.csv`;
    fetch(url, { headers: authHeaders() })
      .then((r) => {
        if (!r.ok) throw new Error(String(r.status));
        return r.blob();
      })
      .then((blob) => {
        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = `matriz-${state.selectedRole}.csv`;
        a.click();
      })
      .catch((e) => { state.saveError = e.message; paint(document.getElementById('access-matrix-content')); });
  }

  async function saveModuleDraft() {
    if (!state.moduleDraft) return;
    state.saving = true;
    try {
      await crudApi('PUT', `/rbac/roles/${encodeURIComponent(state.selectedRole)}/modules`, {
        modules: state.moduleDraft,
      });
      await reloadMatrix();
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  async function saveBindingDraft() {
    if (!state.bindingDraft) return;
    state.saving = true;
    try {
      await crudApi('PUT', `/rbac/roles/${encodeURIComponent(state.selectedRole)}/bindings`, {
        bindings: state.bindingDraft,
      });
      await reloadMatrix();
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  function renderToolbar() {
    if (!canCrud()) return '';
    const meta = selectedRoleMeta();
    const frozen = meta.status === 'frozen';
    return `<div class="am-toolbar">
      <button type="button" class="am-tool-btn primary" data-am-action="new-role">+ Nova função</button>
      <button type="button" class="am-tool-btn" data-am-action="clone-role" ${isRoleLocked() ? 'disabled' : ''}>Copiar</button>
      <button type="button" class="am-tool-btn" data-am-action="freeze-role" ${isRoleLocked() ? 'disabled' : ''}>${frozen ? 'Reactivar' : 'Pausar'}</button>
      <button type="button" class="am-tool-btn" data-am-action="delete-role" ${isRoleLocked() ? 'disabled' : ''}>Arquivar</button>
      <button type="button" class="am-tool-btn" data-am-action="report-csv">Relatório CSV</button>
    </div>`;
  }

  async function patchApi(path, body) {
    const r = await fetchWithTimeout(`/api/v1${path}`, {
      method: 'PATCH',
      headers: authHeaders({ 'Content-Type': 'application/json' }),
      body: JSON.stringify(body),
    });
    if (!r.ok) throw new Error(`${r.status} ${(await r.text()).slice(0, 200)}`);
    return r.json();
  }

  function bindingMeta(agentId, roleId, relation) {
    const b = state.data?.agent_bindings_matrix?.agents?.[agentId]?.[roleId]?.[relation];
    return {
      enabled: !!b?.enabled,
      locked: !!b?.locked,
    };
  }

  function capMeta(capId, roleId) {
    const c = state.data?.agent_bindings_matrix?.caps?.[capId]?.[roleId];
    return { enabled: !!c?.enabled, locked: !!c?.locked };
  }

  async function toggleBinding(agentId, roleId, relation) {
    const meta = bindingMeta(agentId, roleId, relation);
    if (meta.locked) return;
    state.saving = true;
    state.saveError = null;
    try {
      await patchApi('/rbac/agent-bindings', {
        agent_id: agentId,
        role_id: roleId,
        relation,
        enabled: !meta.enabled,
      });
      state.data = await api('/rbac/matrix');
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  async function toggleCap(capId, roleId) {
    const meta = capMeta(capId, roleId);
    if (meta.locked) return;
    state.saving = true;
    state.saveError = null;
    try {
      await patchApi('/rbac/agent-governance', {
        cap_id: capId,
        role_id: roleId,
        enabled: !meta.enabled,
      });
      state.data = await api('/rbac/matrix');
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } catch (e) {
      state.saveError = e.message;
      const root = document.getElementById('access-matrix-content');
      if (root) paint(root);
    } finally {
      state.saving = false;
    }
  }

  async function api(path) {
    const r = await fetchWithTimeout(`/api/v1${path}`, {
      headers: authHeaders({ 'Content-Type': 'application/json' }),
    });
    if (!r.ok) throw new Error(`${r.status} ${(await r.text()).slice(0, 200)}`);
    return r.json();
  }

  function levelMeta(level) {
    return state.data.legend[level] || state.data.legend.none;
  }

  function badgeHtml(level, opts = {}) {
    const meta = levelMeta(level);
    const short = LEVEL_SHORT[meta.access] || '—';
    const cls = opts.compact ? ' am-badge--compact' : '';
    return `<span class="am-badge am-badge--${meta.access}${cls}" title="${esc(meta.label)}">${short}</span>`;
  }

  function serviceLabel(svc) {
    return state.data.service_labels?.[svc] || svc;
  }

  function rolesByCategory(catalog, categoryLabels) {
    const groups = {};
    Object.values(catalog.roles || {}).forEach((role) => {
      const cat = role.category || 'platform';
      if (!groups[cat]) groups[cat] = [];
      groups[cat].push(role);
    });
    const order = ['ops', 'commercial', 'business', 'platform', 'custom', 'system'];
    return order
      .filter((c) => groups[c]?.length)
      .map((c) => ({ id: c, label: categoryLabels[c] || c, roles: groups[c] }));
  }

  function roleDeskRows() {
    const roleId = state.selectedRole;
    return (state.data.desk_modules || []).map((row) => ({
      ...row,
      level: row.levels[roleId] || 'none',
    })).filter((r) => r.level !== 'none');
  }

  function renderRoleSidebar() {
    const groups = rolesByCategory(state.data.catalog, state.data.category_labels || {});
    return groups.map((g) => `
      <div class="am-role-group nav-zone">
        <span class="nav-zone__label">${esc(g.label)}</span>
        <div class="nav-zone__links">
        ${g.roles.map((r) => {
          const st = r.status === 'frozen' ? ' <span class="am-role-paused">pausado</span>' : '';
          const code = window.DeskAccessControlPanel?.ROLE_CODES?.[r.id] || '';
          return `
          <button type="button" class="am-role-btn nav-link${state.selectedRole === r.id ? ' active' : ''}${r.status === 'frozen' ? ' am-role-btn--frozen' : ''}"
            data-am-role="${esc(r.id)}">
            ${code ? `<span class="am-role-code" aria-hidden="true">${esc(code)}</span>` : ''}
            <span class="am-role-label">${esc(r.label)}</span>${st}
          </button>`;
        }).join('')}
        </div>
      </div>`).join('');
  }

  function renderScopeBar() {
    return `<div class="am-scope-bar">
      ${(state.data.scope_layers || []).map((l) => `
        <div class="am-scope-item">
          <span class="am-scope-label">${esc(l.label)}</span>
          <span class="am-scope-host">${esc(l.host)}</span>
          <span class="am-scope-desc">${esc(l.desc)}</span>
        </div>`).join('')}
    </div>`;
  }

  function renderMatrixTable(rows, rowLabel, idKey) {
    const { role_columns, legend } = state.data;
    const cols = role_columns || [];
    const head = cols.map((c) => {
      const label = state.data.catalog.roles[c]?.label || c;
      const hl = c === state.selectedRole ? ' am-col-active' : '';
      return `<th class="${hl.trim()}" title="${esc(c)}">${esc(label.split(' ')[0])}</th>`;
    }).join('');

    const body = rows.map((row) => {
      const cells = cols.map((role) => {
        const lv = row.levels[role] || 'none';
        const tdHl = role === state.selectedRole ? ' am-row-highlight' : '';
        return `<td class="${tdHl.trim()}">${badgeHtml(lv)}</td>`;
      }).join('');
      const sub = row.product
        ? `<span class="am-row-sub">${esc(row.product)}</span>`
        : `<code class="am-row-code">${esc(row[idKey] || '')}</code>`;
      return `<tr>
        <td class="am-sticky">
          <span class="am-row-title">${esc(row.label)}</span>
          ${sub}
        </td>${cells}</tr>`;
    }).join('');

    return `
      <div class="am-table-wrap">
        <table class="am-matrix-table">
          <thead><tr><th class="am-sticky">${esc(rowLabel)}</th>${head}</tr></thead>
          <tbody>${body}</tbody>
        </table>
      </div>`;
  }

  function renderDeskMatrix() {
    if (canCrud() && !isRoleLocked()) {
      if (!state.moduleDraft) {
        const draft = {};
        (state.data.desk_modules || []).forEach((row) => {
          draft[row.id] = row.levels[state.selectedRole] || 'none';
        });
        state.moduleDraft = draft;
      }
      const levels = ['full', 'read', 'api', 'link', 'none'];
      const rows = (state.data.desk_modules || []).map((row) => `
        <tr>
          <td>${esc(row.label)} <code class="am-row-code">${esc(row.id)}</code></td>
          <td>
            <select class="am-mod-select" data-am-mod="${esc(row.id)}">
              ${levels.map((lv) =>
                `<option value="${lv}" ${state.moduleDraft[row.id] === lv ? 'selected' : ''}>${lv}</option>`
              ).join('')}
            </select>
          </td>
        </tr>`).join('');
      return `
        <p class="am-section-lead">Editor de módulos Desk — função <strong>${esc(roleLabel(state.selectedRole))}</strong></p>
        <div class="am-table-wrap">
          <table class="am-edit-table">
            <thead><tr><th>Módulo</th><th>Acesso</th></tr></thead>
            <tbody>${rows}</tbody>
          </table>
        </div>
        <div class="am-editor-actions">
          <button type="button" class="am-tool-btn primary" data-am-action="save-modules">Salvar módulos</button>
        </div>
        <hr class="am-hr"/>
        <p class="am-section-lead">Grelha completa (todas as funções)</p>
        ${renderMatrixTable(state.data.desk_modules || [], 'Módulo Desk', 'id')}`;
    }
    return `
      <p class="am-section-lead">Grelha completa — módulos internos do Desk (VM122).</p>
      ${renderMatrixTable(state.data.desk_modules || [], 'Módulo Desk', 'id')}`;
  }

  function renderBindingCards(bindings, title) {
    if (!bindings.length) {
      return `<p class="am-empty">${title ? esc(title) + ' — ' : ''}sem registos para esta função.</p>`;
    }
    const byService = {};
    bindings.forEach((b) => {
      if (!byService[b.service]) byService[b.service] = [];
      byService[b.service].push(b);
    });
    return `
      ${title ? `<h4 class="am-block-title">${esc(title)}</h4>` : ''}
      <div class="am-api-grid">
        ${Object.entries(byService).flatMap(([svc, items]) =>
          items.map((b) => `
            <article class="am-api-card am-svc-${esc(b.service)}">
              <header class="am-api-card-head">
                <span class="am-api-service">${esc(serviceLabel(svc))}</span>
                ${badgeHtml(b.access || 'full', { compact: true })}
              </header>
              <div class="am-api-type">${esc(b.type || 'binding')}</div>
              <code class="am-api-value">${esc(b.value)}</code>
              <footer class="am-api-foot">${esc(svc)}</footer>
            </article>`)
        ).join('')}
      </div>`;
  }

  function renderSoftwareCards(rows, title) {
    if (!rows.length) {
      return `<p class="am-empty">${esc(title || 'Software')} — sem acesso directo.</p>`;
    }
    return `
      ${title ? `<h4 class="am-block-title">${esc(title)}</h4>` : ''}
      <div class="am-sw-grid">
        ${rows.map((r) => {
          const meta = levelMeta(r.level);
          return `<article class="am-sw-card">
            <header class="am-sw-card-head">
              <div>
                <strong>${esc(r.label)}</strong>
                <span class="am-sw-product">${esc(r.product)}</span>
              </div>
              ${badgeHtml(r.level, { compact: true })}
            </header>
            <p class="am-sw-group">${esc(r.group)}</p>
            ${r.host && r.host !== '—' ? `<code class="am-sw-host">${esc(r.host)}</code>` : ''}
          </article>`;
        }).join('')}
      </div>`;
  }

  function renderDeskChips() {
    const rows = roleDeskRows();
    if (!rows.length) {
      return '<p class="am-empty">Sem módulos Desk activos para esta função.</p>';
    }
    return `<div class="am-desk-chips">
      ${rows.map((r) => `
        <span class="am-desk-chip" title="${esc(r.id)}">
          ${esc(r.label)}
          ${badgeHtml(r.level, { compact: true })}
        </span>`).join('')}
    </div>`;
  }

  function renderRoleStats(role) {
    const sw = state.data.role_software_summaries?.[state.selectedRole] || [];
    const bindings = role?.bindings || [];
    const desk = roleDeskRows();
    return `<div class="am-stats">
      <div class="am-stat"><span class="am-stat-n">${desk.length}</span><span class="am-stat-l">Módulos Desk</span></div>
      <div class="am-stat"><span class="am-stat-n">${sw.length}</span><span class="am-stat-l">Software</span></div>
      <div class="am-stat"><span class="am-stat-n">${bindings.length}</span><span class="am-stat-l">APIs / Grupos</span></div>
      <div class="am-stat"><span class="am-stat-n">${(state.data.role_agents_summaries?.[state.selectedRole] || []).length}</span><span class="am-stat-l">Agentes</span></div>
    </div>`;
  }

  function renderRoleOverview() {
    const role = state.data.catalog.roles[state.selectedRole];
    const sw = state.data.role_software_summaries?.[state.selectedRole] || [];
    const bindings = role?.bindings || [];

    return `
      ${renderScopeBar()}
      ${renderRoleStats(role)}
      <section class="am-block">
        <h4 class="am-block-title">Desk VM122 — módulos activos</h4>
        ${renderDeskChips()}
      </section>
      <section class="am-block">
        ${renderSoftwareCards(sw, 'Software & infra em escopo')}
      </section>
      <section class="am-block">
        ${renderBindingCards(bindings, 'APIs, grupos Odoo e permissões')}
      </section>
      <section class="am-block">
        <h4 class="am-block-title">Agentics — capacidades da função</h4>
        ${renderRoleAgentCaps()}
        ${renderRoleAgentsSummary('')}
      </section>`;
  }

  function renderSoftwareMatrix() {
    const { software_groups } = state.data;
    const groups = (software_groups || []).filter((g) =>
      state.softwareFilter === 'all' || g.id === state.softwareFilter
    );

    const filters = [
      { id: 'all', label: 'Todas as camadas' },
      ...(software_groups || []).map((g) => ({ id: g.id, label: g.label.split('—')[0].trim() })),
    ];

    const filterBar = `
      <div class="am-filter-bar">
        ${filters.map((f) => `
          <button type="button" class="am-filter${state.softwareFilter === f.id ? ' active' : ''}"
            data-am-sw-filter="${esc(f.id)}">${esc(f.label)}</button>`).join('')}
      </div>`;

    const sections = groups.map((grp) => {
      const meta = [grp.host, grp.url].filter(Boolean).join(' · ');
      return `
        <section class="am-sw-section">
          <header class="am-sw-section-head">
            <div>
              <h4>${esc(grp.label)}</h4>
              ${meta ? `<span class="am-sw-meta">${esc(meta)}</span>` : ''}
            </div>
          </header>
          ${renderMatrixTable(grp.items || [], 'Recurso', 'id')}
        </section>`;
    }).join('');

    const sw = state.data.role_software_summaries?.[state.selectedRole] || [];
    return `
      <p class="am-section-lead">Matriz de software fora do Desk — VM112 (onboard/mail), VM123 (finance/hosting) e consolas de infra.</p>
      ${renderSoftwareCards(sw, 'Resumo da função seleccionada')}
      ${filterBar}${sections}`;
  }

  function renderBindings() {
    const role = state.data.catalog.roles[state.selectedRole];
    if (!role) return '<p class="am-empty">Função não encontrada</p>';
    let editor = '';
    if (canCrud() && !isRoleLocked()) {
      if (!state.bindingDraft) {
        state.bindingDraft = (role.bindings || []).map((b) => ({ ...b }));
      }
      const rows = state.bindingDraft.map((b, i) => `
        <tr>
          <td><input class="am-bind-in" data-am-bind="${i}" data-field="service" value="${esc(b.service || '')}"/></td>
          <td><input class="am-bind-in" data-am-bind="${i}" data-field="type" value="${esc(b.type || '')}"/></td>
          <td><input class="am-bind-in" data-am-bind="${i}" data-field="value" value="${esc(b.value || '')}"/></td>
          <td><input class="am-bind-in" data-am-bind="${i}" data-field="access" value="${esc(b.access || 'full')}"/></td>
          <td><button type="button" class="am-tool-btn sm" data-am-rm-bind="${i}">×</button></td>
        </tr>`).join('');
      editor = `
        <section class="am-block">
          <h4 class="am-block-title">Editor bindings</h4>
          <div class="am-table-wrap">
            <table class="am-edit-table">
              <thead><tr><th>Serviço</th><th>Tipo</th><th>Valor</th><th>Acesso</th><th></th></tr></thead>
              <tbody>${rows}</tbody>
            </table>
          </div>
          <div class="am-editor-actions">
            <button type="button" class="am-tool-btn" data-am-action="add-binding">+ Binding</button>
            <button type="button" class="am-tool-btn primary" data-am-action="save-bindings">Salvar bindings</button>
          </div>
        </section>`;
    }
    const groups = (state.data.external_groups || []).map((g) =>
      `<li><code>${esc(g.id)}</code> — ${esc(g.label)} <span class="am-sw-meta">${esc(g.service)}</span></li>`
    ).join('');
    return `
      ${editor}
      <p class="am-section-lead">Bindings estilo Odoo — grupos, roles e permissões provisionados por função.</p>
      ${renderBindingCards(role.bindings || [], '')}
      ${groups ? `<section class="am-block"><h4 class="am-block-title">Grupos externos registados</h4><ul class="am-group-list">${groups}</ul></section>` : ''}`;
  }

  function renderAudit() {
    const entries = state.data.role_audit || [];
    if (!entries.length) return '<p class="am-empty">Sem alterações registadas ainda.</p>';
    return `<div class="am-audit-list">
      ${entries.map((e) => `
        <article class="am-audit-row">
          <header><strong>${esc(e.action)}</strong> · ${esc(e.entity_type)} <code>${esc(e.entity_id)}</code></header>
          <p class="am-sw-meta">${esc(e.actor)} · ${esc(e.created_at)}</p>
        </article>`).join('')}
    </div>`;
  }

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

  function renderRelationChip(rel) {
    const labels = state.data.agent_relation_labels || {
      approve: 'Aprova',
      focus: 'Operador',
      ui: 'UI',
    };
    return `<span class="am-rel am-rel--${esc(rel)}">${esc(labels[rel] || rel)}</span>`;
  }

  function renderRoleChips(roleIds) {
    return (roleIds || []).map((r) =>
      `<span class="am-role-chip${r === state.selectedRole ? ' am-role-chip--sel' : ''}" title="${esc(r)}">${esc(roleLabel(r))}</span>`
    ).join('');
  }

  function renderAgentGovernanceLegend() {
    const labels = state.data.agent_relation_labels || {};
    return `<div class="am-agent-legend">
      ${Object.entries(labels).map(([k, v]) =>
        `<span class="am-agent-legend-item">${renderRelationChip(k)} ${esc(v)}</span>`
      ).join('')}
    </div>`;
  }

  function renderRoleAgentCaps() {
    const capsDef = [
      ['use_ui', 'UI Agentics'],
      ['trigger_runs', 'Disparar cenários'],
      ['approve_runbooks', 'Aprovar runbooks'],
      ['configure_models', 'Configurar LLM'],
    ];
    const roleId = state.selectedRole;
    if (!state.data.editable) {
      const caps = state.data.role_agentic_caps?.[roleId] || [];
      if (!caps.length) {
        return '<p class="am-empty">Esta função não acede ao módulo Agentics.</p>';
      }
      return `<div class="am-cap-chips">
        ${caps.map((c) => `<span class="am-cap-chip">${esc(c.label)}</span>`).join('')}
      </div>`;
    }
    return `<div class="am-cap-toggles">
      ${capsDef.map(([id, label]) => {
        const meta = capMeta(id, roleId);
        return `<button type="button"
          class="am-cap-toggle${meta.enabled ? ' on' : ''}${meta.locked ? ' locked' : ''}"
          ${meta.locked ? 'disabled' : ''}
          data-am-cap="${esc(id)}|${esc(roleId)}"
          title="${esc(label)}">${esc(label)}</button>`;
      }).join('')}
    </div>`;
  }

  function renderRoleAgentsSummary(title) {
    const rows = state.data.role_agents_summaries?.[state.selectedRole] || [];
    if (!rows.length) {
      return `<p class="am-empty">${esc(title || 'Agentes')} — sem interacção directa.</p>`;
    }
    return `
      ${title ? `<h4 class="am-block-title">${esc(title)}</h4>` : ''}
      <div class="am-agent-mini-grid">
        ${rows.map((a) => `
          <article class="am-agent-mini${a.is_approver ? ' am-agent-mini--approve' : ''}">
            <header>
              <span class="am-agent-id">${esc(a.id)}</span>
              <strong>${esc(a.name)}</strong>
            </header>
            <p class="am-agent-role">${esc(a.role)}</p>
            <div class="am-rel-row">${(a.relations || []).map(renderRelationChip).join('')}</div>
          </article>`).join('')}
      </div>`;
  }

  function renderAgentCard(a) {
    const rels = a.role_relations?.[state.selectedRole] || [];
    const hit = rels.length > 0;
    return `
      <article class="am-agent-card${hit ? ' am-agent-card--hit' : ''}">
        <header class="am-agent-top">
          <span class="am-agent-id">${esc(a.id)}</span>
          <div>
            <strong>${esc(a.name)}</strong>
            <span class="am-agent-codename">${esc(a.codename)}</span>
          </div>
        </header>
        <p class="am-agent-role">${esc(a.role)}</p>
        ${hit ? `<div class="am-rel-row am-rel-row--you">${rels.map(renderRelationChip).join('')} <span class="am-you">esta função</span></div>` : ''}
        <div class="am-agent-section">
          <span class="am-agent-lbl">Aprova</span>
          ${renderRoleChips(a.approvers)}
        </div>
        <div class="am-agent-section">
          <span class="am-agent-lbl">Operadores</span>
          ${renderRoleChips(a.operators)}
        </div>
        ${(a.reads || []).length ? `
          <div class="am-agent-section">
            <span class="am-agent-lbl">Lê</span>
            <span class="am-agent-meta">${esc(a.reads.slice(0, 3).join(' · '))}</span>
          </div>` : ''}
        <footer class="am-agent-approval">
          <span>Regra Spec 027</span>
          <code>${esc(a.approval_text)}</code>
        </footer>
      </article>`;
  }

  function renderRelationToggle(agentId, roleId, relation) {
    const meta = bindingMeta(agentId, roleId, relation);
    const short = { ui: 'UI', focus: 'OP', approve: 'AP' }[relation] || relation;
    return `<button type="button"
      class="am-toggle${meta.enabled ? ' on' : ''}${meta.locked ? ' locked' : ''}"
      ${meta.locked ? 'disabled' : ''}
      data-am-toggle="${esc(agentId)}|${esc(roleId)}|${relation}"
      title="${esc(relation)}${meta.locked ? ' (obrigatório)' : ''}">${short}</button>`;
  }

  function renderAgentMatrixCell(agentId, role, rels) {
    const tdHl = role === state.selectedRole ? ' am-row-highlight' : '';
    if (!state.data.editable) {
      const inner = rels.length
        ? rels.map(renderRelationChip).join('')
        : '<span class="am-rel am-rel--none">—</span>';
      return `<td class="${tdHl.trim()}">${inner}</td>`;
    }
    return `<td class="am-cell-toggles${tdHl.trim()}">
      ${renderRelationToggle(agentId, role, 'ui')}
      ${renderRelationToggle(agentId, role, 'focus')}
      ${renderRelationToggle(agentId, role, 'approve')}
    </td>`;
  }

  function renderAgentsMatrix() {
    const agents = state.data.agents || [];
    const cols = state.data.role_columns || [];
    const head = cols.map((c) => {
      const hl = c === state.selectedRole ? ' am-col-active' : '';
      return `<th class="${hl.trim()}" title="${esc(c)}">${esc(roleLabel(c).split(' ')[0])}</th>`;
    }).join('');

    const body = agents.map((a) => {
      const cells = cols.map((role) => {
        const rels = a.role_relations?.[role] || [];
        return renderAgentMatrixCell(a.id, role, rels);
      }).join('');
      return `<tr>
        <td class="am-sticky">
          <span class="am-row-title">${esc(a.id)} · ${esc(a.name)}</span>
          <span class="am-row-sub">${esc(a.role)}</span>
        </td>${cells}</tr>`;
    }).join('');

    return `
      <h4 class="am-block-title">Matriz agente × função</h4>
      ${renderAgentGovernanceLegend()}
      <div class="am-table-wrap">
        <table class="am-matrix-table">
          <thead><tr><th class="am-sticky">Agente</th>${head}</tr></thead>
          <tbody>${body}</tbody>
        </table>
      </div>`;
  }

  function renderAgents() {
    const caps = state.data.role_agentic_caps?.[state.selectedRole] || [];
    return `
      <p class="am-section-lead">
        Agentes A0–A7 usam conta <code>agent_system</code>.
        ${state.data.editable
          ? 'Clique <strong>UI / OP / AP</strong> para ligar ou desligar atribuições (audit activo).'
          : 'Três relações: UI · Operador · Aprova.'}
      </p>
      ${state.saveError ? `<p class="am-save-error">${esc(state.saveError)}</p>` : ''}
      ${(state.data.editable || caps.length) ? `<section class="am-block"><h4 class="am-block-title">Capacidades — ${esc(roleLabel(state.selectedRole))}</h4>${renderRoleAgentCaps()}</section>` : ''}
      ${renderAgentsMatrix()}
      <section class="am-block">
        <h4 class="am-block-title">Detalhe por agente</h4>
        <div class="am-agents-grid">
          ${(state.data.agents || []).map(renderAgentCard).join('')}
        </div>
      </section>`;
  }

  function renderLegend() {
    const legend = state.data.legend || {};
    return Object.entries(legend).map(([key, l]) =>
      `<span class="am-legend-item">${badgeHtml(key, { compact: true })} ${esc(l.label)}</span>`
    ).join('');
  }

  function activeTabHint() {
    const tab = (state.data.tabs || []).find((t) => t.id === state.tab);
    return tab?.hint || '';
  }

  function renderMainPanel() {
    if (state.tab === 'access-control') {
      return `
        <div class="am-panel am-panel--access-control">
          <div id="am-access-control-host" class="am-access-control-host"></div>
        </div>`;
    }
    if (state.tab === 'quem-faz-o-que') {
      return `
        <div class="am-panel am-panel--executive">
          <div id="am-executive-map-host" class="am-executive-map-host"></div>
        </div>`;
    }
    const role = state.data.catalog.roles[state.selectedRole];
    let body = '';
    if (state.tab === 'overview') body = renderRoleOverview();
    else if (state.tab === 'vm122') body = renderDeskMatrix();
    else if (state.tab === 'software') body = renderSoftwareMatrix();
    else if (state.tab === 'bindings') body = renderBindings();
    else if (state.tab === 'agents') body = renderAgents();
    else if (state.tab === 'audit') body = renderAudit();

    return `
      <div class="am-panel">
        <header class="am-panel-head">
          <div class="am-panel-title">
            <span class="am-panel-kicker">Spec 027 · RBAC</span>
            <h3>${esc(role?.label || state.selectedRole)}</h3>
            <p class="am-panel-desc">${esc(role?.description || '')}</p>
            ${activeTabHint() ? `<p class="am-tab-hint">${esc(activeTabHint())}</p>` : ''}
          </div>
          <div class="am-legend">${renderLegend()}</div>
        </header>
        <div class="am-panel-body">${body}</div>
      </div>`;
  }

  function renderTabs() {
    return (state.data.tabs || []).map((t) => {
      const sep = t.separated ? '<span class="am-tab-sep" aria-hidden="true"></span>' : '';
      const extra = t.id === 'access-control' ? ' am-tab--access-control' : (t.id === 'quem-faz-o-que' ? ' am-tab--quem-faz-o-que' : '');
      return `${sep}<button type="button" class="am-tab${extra}${state.tab === t.id ? ' active' : ''}" data-am-tab="${esc(t.id)}">${esc(t.label)}</button>`;
    }).join('');
  }

  function bindEvents(root) {
    root.querySelectorAll('[data-am-role]').forEach((btn) => {
      btn.addEventListener('click', () => {
        state.selectedRole = btn.dataset.amRole;
        state.moduleDraft = null;
        state.bindingDraft = null;
        window.DeskExecutiveMap?.resetFilters?.();
        paint(root);
      });
    });
    root.querySelectorAll('[data-am-tab]').forEach((btn) => {
      btn.addEventListener('click', () => {
        state.tab = btn.dataset.amTab;
        if (typeof window.getDeskState === 'function') {
          const ds = window.getDeskState();
          if (ds) ds.matrixTab = state.tab;
        }
        paint(root);
      });
    });
    root.querySelectorAll('[data-am-sw-filter]').forEach((btn) => {
      btn.addEventListener('click', () => {
        state.softwareFilter = btn.dataset.amSwFilter;
        paint(root);
      });
    });
    root.querySelectorAll('[data-am-toggle]').forEach((btn) => {
      btn.addEventListener('click', () => {
        if (state.saving || !state.data?.editable) return;
        const [agentId, roleId, relation] = btn.dataset.amToggle.split('|');
        toggleBinding(agentId, roleId, relation);
      });
    });
    root.querySelectorAll('[data-am-cap]').forEach((btn) => {
      btn.addEventListener('click', () => {
        if (state.saving || !state.data?.editable) return;
        const [capId, roleId] = btn.dataset.amCap.split('|');
        toggleCap(capId, roleId);
      });
    });
    root.querySelectorAll('[data-am-action]').forEach((btn) => {
      btn.addEventListener('click', () => {
        const a = btn.dataset.amAction;
        if (a === 'new-role') handleNewRole();
        if (a === 'clone-role') handleCloneRole();
        if (a === 'freeze-role') handleFreezeRole();
        if (a === 'delete-role') handleDeleteRole();
        if (a === 'report-csv') handleReportDownload();
        if (a === 'save-modules') saveModuleDraft();
        if (a === 'save-bindings') saveBindingDraft();
        if (a === 'add-binding') {
          state.bindingDraft = state.bindingDraft || [];
          state.bindingDraft.push({ service: 'desk', type: 'permission', value: 'read_tickets', access: 'read' });
          paint(root);
        }
      });
    });
    root.querySelectorAll('[data-am-mod]').forEach((sel) => {
      sel.addEventListener('change', () => {
        if (!state.moduleDraft) return;
        state.moduleDraft[sel.dataset.amMod] = sel.value;
      });
    });
    root.querySelectorAll('.am-bind-in').forEach((inp) => {
      inp.addEventListener('change', () => {
        const i = Number(inp.dataset.amBind);
        const field = inp.dataset.field;
        if (!state.bindingDraft?.[i]) return;
        state.bindingDraft[i][field] = inp.value;
      });
    });
    root.querySelectorAll('[data-am-rm-bind]').forEach((btn) => {
      btn.addEventListener('click', () => {
        const i = Number(btn.dataset.amRmBind);
        state.bindingDraft.splice(i, 1);
        paint(root);
      });
    });
  }

  function paint(root) {
    root.innerHTML = `
      <div class="am-wrap">
        <div class="am-header am-header--toolbar">
          <span class="am-preview-tag">${state.data?.crud_enabled ? 'CRUD · audit ON' : state.data?.editable ? 'Edição · audit ON' : 'Consulta'}${state.saving ? ' · …' : ''}</span>
        </div>
        ${renderToolbar()}
        ${state.saveError ? `<p class="am-save-error">${esc(state.saveError)}</p>` : ''}
        <nav class="am-subnav">${renderTabs()}</nav>
        <div class="am-layout">
          <aside class="am-role-list">${renderRoleSidebar()}</aside>
          ${renderMainPanel()}
        </div>
      </div>`;
    bindEvents(root);
    window.DeskTopnav?.remountMatrixRoles?.();
    if (state.tab === 'access-control') {
      const host = root.querySelector('#am-access-control-host');
      const roleMeta = state.data?.catalog?.roles?.[state.selectedRole];
      window.DeskAccessControlPanel?.paint?.(host, {
        selectedRole: state.selectedRole,
        roleMeta,
        catalog: state.data?.catalog,
        editable: !!state.data?.editable,
      });
    }
    if (state.tab === 'quem-faz-o-que') {
      const host = root.querySelector('#am-executive-map-host');
      window.DeskExecutiveMap?.paint?.(host, { selectedRole: state.selectedRole });
    }
  }

  async function renderAccessMatrix() {
    const root = document.getElementById('access-matrix-content');
    if (!root) return;
    if (typeof ensureValidSession === 'function' && !(await ensureValidSession())) return;
    root.innerHTML = '<p class="loading">Carregando Matriz de Acesso…</p>';
    try {
      state.data = await api('/rbac/matrix');
      if (!state.selectedRole && state.data.role_columns?.length) {
        state.selectedRole = state.data.role_columns[0];
      }
      const tabIds = (state.data.tabs || []).map((t) => t.id);
      if (typeof window.getDeskState === 'function') {
        const ds = window.getDeskState();
        if (ds?.matrixTab && tabIds.includes(ds.matrixTab)) {
          state.tab = ds.matrixTab;
        }
      }
      if (!tabIds.includes(state.tab) && tabIds.length) state.tab = tabIds[0];
      paint(root);
    } catch (e) {
      root.innerHTML = `<p class="loading">Matriz indisponível: ${esc(e.message)}</p>`;
    }
  }

  window.renderAccessMatrix = renderAccessMatrix;
  window.DeskAccessMatrix = {
    renderAccessMatrix,
    getState: () => state,
  };
})();
