/* global React */
// =======================================================================
// BlazeConnector Admin v3 — Administración pages
// Tenants (+ detail) · API Keys · Templates · Auditoría
// =======================================================================
const { useState: _as, useEffect: _ae, useMemo: _am, useRef: _ar } = React;

/* =========================================================================
   TENANTS — list
   ========================================================================= */
function PageTenants({ onOpenTenant, role }) {
  const bc = window.useBC();
  const TS = bc.tenants;
  const live = bc.mode === 'live';
  const [search, setSearch] = _as('');
  const [planF, setPlanF] = _as('all');
  const [statusF, setStatusF] = _as('all');
  const [creating, setCreating] = _as(false);

  const rows = _am(() => {
    const ql = search.toLowerCase();
    return TS.filter(t => {
      if (planF !== 'all' && t.plan !== planF) return false;
      if (statusF !== 'all' && t.status !== statusF) return false;
      if (ql && !(t.name + ' ' + t.id + ' ' + t.uuid + ' ' + (t.region || '')).toLowerCase().includes(ql)) return false;
      return true;
    });
  }, [search, planF, statusF, TS]);

  const loading = bc.tenantsLoading && TS.length === 0;
  const failed = bc.status === 'error' && TS.length === 0;

  return (
    <div className="page">
      <div className="page-header">
        <div>
          <h1 className="page-title">Tenants {live && <span className="sb success sb-square" style={{ fontSize: 10, verticalAlign: 'middle' }}>live</span>}</h1>
          <p className="page-sub">{rows.length} de {TS.length} ISPs · {TS.filter(t => t.status === 'active').length} activos · {TS.filter(t => t.status === 'suspended').length} suspendidos</p>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost btn-sm"><window.Icon name="download" size={12} /> Export CSV</button>
          {window.hasPerm(role, 'tenant:edit') && <button className="btn btn-primary btn-sm" onClick={() => setCreating(true)}><window.Icon name="plus" size={12} /> Crear tenant</button>}
        </div>
      </div>

      <div className="grid-4" style={{ marginBottom: 12 }}>
        <window.KpiCard label="Total tenants" value={TS.length} color="violet" foot={live ? 'datos en vivo' : 'demo'} />
        <window.KpiCard label="Mensajes 24h" value={live ? '—' : TS.reduce((a, t) => a + (t.msgs24h || 0), 0)} unit="" color="orange" delta={live ? null : 4.2} series={live ? null : window.DASHBOARD_SERIES.msgsPerMin.slice(0, 30)} foot={live ? 'no expuesto por admin API' : null} />
        <window.KpiCard label="Suscriptores totales" value={live ? '—' : TS.reduce((a, t) => a + (t.subs || 0), 0)} color="blue" foot={live ? 'no expuesto por admin API' : 'end-users servidos'} />
        <window.KpiCard label="Activos" value={TS.filter(t => t.status === 'active').length} color="green" foot={'de ' + TS.length + ' tenants'} />
      </div>

      <div className="card">
        <window.FilterBar>
          <window.FilterSearch value={search} onChange={setSearch} placeholder="Buscar por nombre, UUID…" />
          {['all','Enterprise','Pro','Starter'].map(p => (
            <button key={p} className={'fb-chip ' + (planF === p ? 'is-active' : '')} onClick={() => setPlanF(p)}>{p === 'all' ? 'Todos los planes' : p}</button>
          ))}
          <span className="fb-spacer"></span>
          {['all','active','suspended','inactive'].map(s => (
            <button key={s} className={'fb-chip ' + (statusF === s ? 'is-active' : '')} onClick={() => setStatusF(s)}>{s === 'all' ? 'Todos' : s}</button>
          ))}
        </window.FilterBar>
        {loading ? <window.EmptyState icon="loader" title="Cargando tenants…" sub={bc.baseUrl} />
         : failed ? <window.EmptyState icon="circle-x" title="No se pudieron cargar los tenants" sub={bc.error} action={<button className="btn btn-secondary btn-sm" onClick={() => window.BC.loadTenants()}>Reintentar</button>} />
         : <window.DataTable density="cozy" onRowClick={(r) => onOpenTenant(r.id)} columns={[
          { label: 'Tenant', render: (t) => (
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <window.TenantAvatar tenant={t} size={28} />
              <div>
                <div style={{ fontWeight: 700, color: 'var(--app-fg1)' }}>{t.name}</div>
                <div className="mono" style={{ fontSize: 10.5, color: 'var(--app-fg3)' }}>{t.uuid}</div>
              </div>
            </div>
          ) },
          { label: 'Plan', width: 110, render: (t) => <span className={'sb sb-square ' + (t.plan === 'Enterprise' ? 'violet' : t.plan === 'Pro' ? 'info' : 'neutral')}>{t.plan}</span> },
          { label: 'Estado', width: 100, render: (t) => <window.StatusBadge status={t.status} /> },
          { label: 'Canales', width: 120, render: (t) => t.channels.length ? <div style={{ display: 'flex', gap: 4 }}>{t.channels.map(c => <window.ChannelChip key={c} name={c} size="sm" />)}</div> : <span style={{ color: 'var(--app-fg-faint)' }}>—</span> },
          { label: 'Billing', width: 110, render: (t) => <span className="mono" style={{ fontSize: 11.5, color: 'var(--app-fg2)' }}>{t.billing || '—'}</span> },
          { label: 'Módulos', width: 70, numeric: true, render: (t) => <span className="mono" style={{ color: 'var(--app-fg2)' }}>{t.modules.length}</span> },
          { label: 'Pagos', width: 90, render: (t) => <span className="mono" style={{ fontSize: 11, color: 'var(--app-fg3)' }}>{t.pagos.length ? t.pagos.join(', ') : '—'}</span> },
          { label: 'Actualizado', width: 130, render: (t) => <span style={{ color: 'var(--app-fg3)', fontSize: 11.5 }}>hace {window.timeAgo(t.lastActive)}</span> },
          { label: '', width: 40, render: () => <button className="btn btn-ghost btn-sm btn-icon-only" onClick={(e) => e.stopPropagation()}><window.Icon name="more-horizontal" size={13} /></button> }
        ]} rows={rows} />}
      </div>

      {creating && <CreateTenantModal live={live} onClose={() => setCreating(false)} onCreated={(uuid) => { setCreating(false); if (uuid) onOpenTenant(uuid); }} />}
    </div>
  );
}

/* Crear tenant — POST /admin/tenants real en modo live */
function CreateTenantModal({ live, onClose, onCreated }) {
  const [name, setName] = _as('');
  const [plan, setPlan] = _as('Pro');
  const [busy, setBusy] = _as(false);
  const [err, setErr] = _as(null);
  const PLAN_API = { Starter: 'free', Pro: 'pro', Enterprise: 'enterprise' };

  async function submit() {
    if (!name.trim()) { setErr('El nombre es obligatorio'); return; }
    if (!live) { onCreated(null); return; }
    setBusy(true); setErr(null);
    try {
      const t = await window.BC.createTenant({ name: name.trim(), plan: PLAN_API[plan] });
      onCreated(t && t.uuid);
    } catch (e) { setErr(e.message); setBusy(false); }
  }

  return (
    <window.Modal title="Crear nuevo tenant" sub={live ? 'POST /admin/tenants (real)' : 'ISP onboardeado al connector'} onClose={onClose} actions={
      <>
        <button className="btn btn-ghost" onClick={onClose} disabled={busy}>Cancelar</button>
        <button className="btn btn-primary" onClick={submit} disabled={busy || !name.trim()}>{busy ? 'Creando…' : 'Crear tenant'}</button>
      </>
    }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)' }}>Nombre comercial</span>
          <input value={name} onChange={(e) => setName(e.target.value)} autoFocus placeholder="ej. NorteFibra" style={{ background: 'var(--app-input-bg)', border: '1px solid var(--app-border)', borderRadius: 6, padding: '8px 10px', color: 'var(--app-fg1)', fontFamily: 'inherit', fontSize: 13 }} />
        </label>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)', marginBottom: 6 }}>Plan</div>
          <div style={{ display: 'flex', gap: 6 }}>
            {['Starter', 'Pro', 'Enterprise'].map(p => <button key={p} className={'fb-chip' + (p === plan ? ' is-active' : '')} onClick={() => setPlan(p)}>{p}</button>)}
          </div>
        </div>
        {err && <div style={{ fontSize: 12, color: 'var(--st-danger-fg)', padding: '6px 8px', background: 'var(--st-danger-bg)', borderRadius: 6 }}>{err}</div>}
        <div style={{ fontSize: 11, color: 'var(--app-fg3)' }}>Los módulos y proveedores se configuran luego desde el detalle del tenant.</div>
      </div>
    </window.Modal>
  );
}

/* =========================================================================
   TENANT DETAIL
   ========================================================================= */
function PageTenantDetail({ tenantId, onBack, role }) {
  const bc = window.useBC();
  const live = bc.mode === 'live';
  const tenant = bc.tenants.find(t => t.id === tenantId);
  const [tab, setTab] = _as('overview');
  if (!tenant) return <div className="page"><window.EmptyState title="Tenant no encontrado" sub={tenantId} /></div>;

  const liveKeys = (bc.keysByTenant[tenant.uuid] || {}).data;
  const liveTpls = (bc.templatesByTenant[tenant.uuid] || {}).data;

  return (
    <div className="page">
      <div className="page-header">
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <button className="btn btn-ghost btn-sm btn-icon-only" onClick={onBack}><window.Icon name="arrow-left" size={14} /></button>
          <window.TenantAvatar tenant={tenant} size={36} />
          <div>
            <h1 className="page-title" style={{ display: 'flex', alignItems: 'center', gap: 10 }}>{tenant.name} <window.StatusBadge status={tenant.status} /></h1>
            <p className="page-sub"><span className="mono">{tenant.id}</span> · <span className="mono">{tenant.uuid}</span> · {tenant.region}</p>
          </div>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost btn-sm"><window.Icon name="external-link" size={12} /> Ver mensajes</button>
          {window.hasPerm(role, 'tenant:edit') && (tenant.status === 'active'
            ? <button className="btn btn-ghost btn-sm" style={{ color: 'var(--st-warn-fg)' }}><window.Icon name="pause" size={12} /> Suspender</button>
            : <button className="btn btn-secondary btn-sm"><window.Icon name="play" size={12} /> Reactivar</button>)}
          {window.hasPerm(role, 'tenant:edit') && <button className="btn btn-primary btn-sm"><window.Icon name="pencil" size={12} /> Editar</button>}
        </div>
      </div>

      <div className="page-tabs">
        {[
          { id: 'overview',  label: 'Overview' },
          { id: 'providers', label: 'Providers',  count: tenant.pagos.length + (tenant.billing ? 1 : 0) },
          { id: 'modules',   label: 'Módulos',    count: tenant.modules.length },
          { id: 'apikeys',   label: 'API Keys',   count: live ? (liveKeys ? liveKeys.length : null) : window.API_KEYS.filter(k => k.tenant === tenant.id).length },
          { id: 'templates', label: 'Templates',  count: live ? (liveTpls ? liveTpls.length : null) : window.TEMPLATES.length },
          { id: 'audit',     label: 'Audit',      count: live ? null : window.AUDIT_EVENTS.filter(a => a.tenant === tenant.id).length }
        ].map(t => (
          <button key={t.id} className={'page-tab ' + (tab === t.id ? 'is-active' : '')} onClick={() => setTab(t.id)}>
            {t.label}{t.count != null && <span className="page-tab-count">{t.count}</span>}
          </button>
        ))}
      </div>

      {tab === 'overview' && <TenantOverview tenant={tenant} />}
      {tab === 'providers' && <TenantProviders tenant={tenant} />}
      {tab === 'modules' && <TenantModules tenant={tenant} />}
      {tab === 'apikeys' && <TenantApiKeys tenant={tenant} role={role} />}
      {tab === 'templates' && <TenantTemplates tenant={tenant} />}
      {tab === 'audit' && <TenantAudit tenant={tenant} />}
    </div>
  );
}

function TenantOverview({ tenant }) {
  const series = window.DASHBOARD_SERIES.msgsPerMin.map((v) => Math.round(v * (tenant.msgs24h / 60000)));
  return (
    <>
      <div className="grid-4" style={{ marginBottom: 12 }}>
        <window.KpiCard label="Plan" value={tenant.plan} color="violet" foot={'desde ' + new Date('2024-08-12').toLocaleDateString('es-DO')} />
        <window.KpiCard label="Mensajes 24h" value={tenant.msgs24h} delta={tenant.status === 'active' ? 3.4 : 0} series={series} color="orange" />
        <window.KpiCard label="Suscriptores ISP" value={tenant.subs} color="blue" foot="end-users servidos" />
        <window.KpiCard label="Error rate" value={tenant.errorRate.toFixed(1) + '%'} color={tenant.errorRate > 2 ? 'danger' : tenant.errorRate > 1 ? 'warn' : 'green'} delta={tenant.errorRate > 2 ? 240 : -8} deltaTone={tenant.errorRate > 2 ? 'down' : 'up'} foot="últimos 60m" />
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 12, marginBottom: 12 }}>
        <window.SectionCard title="Mensajes por canal (24h)" icon="bar-chart-3">
          <window.BarChart data={series.slice(36)} height={180} color="orange" xLabels={['24h', '20h', '16h', '12h', '8h', '4h', 'ahora']} />
        </window.SectionCard>
        <window.SectionCard title="Información" icon="info">
          <dl className="kv">
            <dt>Tenant ID</dt><dd>{tenant.id}</dd>
            <dt>UUID</dt><dd style={{ wordBreak: 'break-all' }}>{tenant.uuid}</dd>
            <dt>Slug</dt><dd>{tenant.slug}.blaze.do</dd>
            <dt>Plan</dt><dd className="kv-text">{tenant.plan}</dd>
            <dt>Región</dt><dd className="kv-text">{tenant.region}</dd>
            <dt>Estado</dt><dd className="kv-text"><window.StatusBadge status={tenant.status} /></dd>
            <dt>Onboarded</dt><dd className="kv-text">12 ago 2024</dd>
            <dt>Última actividad</dt><dd className="kv-text">hace {window.timeAgo(tenant.lastActive)}</dd>
          </dl>
        </window.SectionCard>
      </div>

      <window.SectionCard title="Actividad reciente" icon="activity" flush>
        <window.DataTable density="compact" columns={[
          { label: 'Hora', width: 130, render: (a) => <span className="mono" style={{ color: 'var(--app-fg3)' }}>{window.fmtDateTime(a.ts)}</span> },
          { label: 'Actor', width: 200, render: (a) => <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>{a.actor.type === 'user' ? <window.UserAvatar name={a.actor.name} size={18} /> : <span style={{ width: 18, height: 18, borderRadius: 4, background: 'var(--app-panel-3)', display: 'inline-grid', placeItems: 'center' }}><window.Icon name="cpu" size={11} /></span>}<span>{a.actor.name}</span></span> },
          { label: 'Acción', width: 200, render: (a) => <span className="mono" style={{ color: 'var(--st-violet-fg)' }}>{a.action}</span> },
          { label: 'Target', render: (a) => <span className="mono" style={{ color: 'var(--app-fg2)' }}>{a.target}</span> }
        ]} rows={window.AUDIT_EVENTS.filter(a => a.tenant === tenant.id || !a.tenant).slice(0, 6)} />
      </window.SectionCard>
    </>
  );
}

function TenantProviders({ tenant }) {
  const billing = window.PROVIDERS_ISP.find(p => p.id === tenant.billing);
  const pagos = window.PROVIDERS_PAY.filter(p => tenant.pagos.includes(p.id));
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
      <window.SectionCard title="Billing / OSS" icon="router" sub="Sistema fuente del ISP">
        {billing ? (
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
              <span className="pv-ico" style={{ width: 36, height: 36, borderRadius: 8, fontSize: 13 }}>{billing.name[0]}</span>
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 700, fontSize: 14 }}>{billing.name}</div>
                <div className="mono" style={{ fontSize: 11, color: 'var(--app-fg3)' }}>{billing.version} · {billing.apiBase}</div>
              </div>
              <window.StatusBadge status={billing.status} />
            </div>
            <dl className="kv">
              <dt>Latencia avg</dt><dd>{billing.latency}ms</dd>
              <dt>Tenants compartidos</dt><dd>{billing.tenants}</dd>
              <dt>Auth</dt><dd>OAuth2 client_credentials</dd>
              <dt>Webhook URL</dt><dd>{'https://api.blaze.do/v3/webhooks/billing/' + tenant.slug}</dd>
              <dt>Última sync</dt><dd>hace 4m</dd>
            </dl>
          </div>
        ) : <window.EmptyState title="Sin provider de billing" sub="Conecta Mikrowisp, WispHub u otro." />}
      </window.SectionCard>

      <window.SectionCard title="Pagos" icon="credit-card" sub={pagos.length + ' gateways conectados'}>
        {pagos.length === 0 ? <window.EmptyState title="Sin gateways" sub="Este tenant aún no procesa pagos." /> : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {pagos.map(p => (
              <div key={p.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0', borderBottom: '1px solid var(--app-divider)' }}>
                <span className="pv-ico" style={{ width: 28, height: 28, fontSize: 11 }}>{p.name.slice(0, 2).toUpperCase()}</span>
                <div style={{ flex: 1 }}>
                  <div style={{ fontWeight: 600, fontSize: 13 }}>{p.name}</div>
                  <div className="mono" style={{ fontSize: 10.5, color: 'var(--app-fg3)' }}>{p.currency} · {p.authMode} · {p.latency || '—'}ms</div>
                </div>
                <window.StatusBadge status={p.status} />
              </div>
            ))}
          </div>
        )}
      </window.SectionCard>

      <window.SectionCard title="Canales" icon="message-square">
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {tenant.channels.map(c => (
            <div key={c} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 0', borderBottom: '1px solid var(--app-divider)' }}>
              <window.ChannelChip name={c} size="lg" />
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 600, fontSize: 13 }}>{window.CHANNEL_LABEL[c]}</div>
                <div className="mono" style={{ fontSize: 10.5, color: 'var(--app-fg3)' }}>
                  {c === 'whatsapp' ? 'phone_number_id=104982304' : c === 'telegram' ? 'bot @' + tenant.slug + 'bot' : 'workspace #' + tenant.slug + '-soporte'}
                </div>
              </div>
              <window.StatusBadge status="active" />
            </div>
          ))}
        </div>
      </window.SectionCard>

      <window.SectionCard title="Webhooks salientes" icon="webhook">
        <div className="mono" style={{ fontSize: 11.5, lineHeight: 1.9 }}>
          <div><span style={{ color: 'var(--st-success-fg)' }}>POST</span> https://erp.{tenant.slug}.com/webhooks/blaze/pagos <span style={{ color: 'var(--app-fg3)', marginLeft: 6 }}>· 24h: 1,842 · ok</span></div>
          <div><span style={{ color: 'var(--st-success-fg)' }}>POST</span> https://crm.{tenant.slug}.com/webhooks/blaze/mensajes <span style={{ color: 'var(--app-fg3)', marginLeft: 6 }}>· 24h: 8,412 · ok</span></div>
          <div><span style={{ color: 'var(--st-warn-fg)' }}>POST</span> https://staging.{tenant.slug}.com/hook <span style={{ color: 'var(--st-warn-fg)', marginLeft: 6 }}>· 24h: 142 · 4 fallos</span></div>
        </div>
      </window.SectionCard>
    </div>
  );
}

// Módulos reales del backend (tenant.ModuleFlags) con etiqueta + icono.
const BACKEND_MODULES = [
  { id: 'wacloud',     name: 'WhatsApp Cloud', desc: 'Envío/recepción vía Meta Cloud API', icon: 'message-square' },
  { id: 'telegram',    name: 'Telegram',       desc: 'Bot de Telegram', icon: 'send' },
  { id: 'chatwoot',    name: 'Chatwoot',       desc: 'Bridge con inbox Chatwoot', icon: 'messages-square' },
  { id: 'mikrowisp',   name: 'Mikrowisp',      desc: 'Billing/OSS Mikrowisp', icon: 'router' },
  { id: 'wisphub',     name: 'WispHub',        desc: 'Billing/OSS WispHub', icon: 'router' },
  { id: 'domiisp',     name: 'DomiISP',        desc: 'Billing/OSS DomiISP', icon: 'router' },
  { id: 'oficable',    name: 'Oficable',       desc: 'Billing/OSS Oficable', icon: 'router' },
  { id: 'smartolt',    name: 'SmartOLT',       desc: 'Gestión de OLTs', icon: 'network' },
  { id: 'voltage',     name: 'Voltage',        desc: 'Monitoreo de voltaje', icon: 'zap' },
  { id: 'cardnet',     name: 'CardNET',        desc: 'Pasarela de pagos CardNET', icon: 'credit-card' },
  { id: 'azul',        name: 'Azul',           desc: 'Pasarela Azul (Popular)', icon: 'credit-card' },
  { id: 'paypal',      name: 'PayPal',         desc: 'Pasarela PayPal', icon: 'credit-card' },
  { id: 'cybersource', name: 'CyberSource',    desc: 'Procesador Visa/CyberSource', icon: 'credit-card' },
  { id: 'monitoring',  name: 'Monitoring',     desc: 'Ingesta de alertas NMS (PRTG…)', icon: 'activity' },
  { id: 'blazechat',   name: 'BlazeChat',      desc: 'Chat interno Blaze', icon: 'message-circle' },
  { id: 'blazeteams',  name: 'BlazeTeams',     desc: 'Notificaciones a equipos', icon: 'users' },
  { id: 'blazebot',    name: 'BlazeBot',       desc: 'Agente/bot Blaze', icon: 'bot' }
];

function TenantModules({ tenant }) {
  const live = tenant._live === true;
  const [saving, setSaving] = _as(null);   // id del módulo guardando
  const [err, setErr] = _as(null);

  // Demo: catálogo cosmético original; Live: flags reales del backend.
  if (!live) {
    const all = [
      { id: 'mensajeria',  name: 'Mensajería',  desc: 'Inbox unificado, plantillas WhatsApp, broadcasts', icon: 'message-square' },
      { id: 'billing',     name: 'Billing sync', desc: 'Sincroniza clientes, facturas y servicios con el OSS', icon: 'file-text' },
      { id: 'pagos',       name: 'Pagos',        desc: 'Procesa pagos online y reconcilia con el OSS', icon: 'credit-card' },
      { id: 'automation',  name: 'Automation',   desc: 'Workflows: corte automático, recordatorios, escalado', icon: 'workflow' },
      { id: 'ai_agent',    name: 'AI Agent',     desc: 'Agente IA para soporte L1 (BlazeAI)', icon: 'sparkles' },
      { id: 'campaigns',   name: 'Campañas',     desc: 'Envío masivo segmentado (requiere plan Pro)', icon: 'megaphone' }
    ];
    return <div className="grid-3">{all.map(m => <ModuleCard key={m.id} m={m} on={tenant.modules.includes(m.id)} />)}</div>;
  }

  const flags = tenant.modulesRaw || {};
  async function toggle(id) {
    setSaving(id); setErr(null);
    try {
      const next = Object.assign({}, flags, { [id]: !flags[id] });
      await window.BC.updateModules(tenant.uuid, next);
    } catch (e) { setErr(e.message); }
    finally { setSaving(null); }
  }
  return (
    <>
      {err && <div style={{ fontSize: 12, color: 'var(--st-danger-fg)', padding: '8px 10px', background: 'var(--st-danger-bg)', borderRadius: 6, marginBottom: 10 }}>{err}</div>}
      <div className="grid-3">
        {BACKEND_MODULES.map(m => <ModuleCard key={m.id} m={m} on={!!flags[m.id]} saving={saving === m.id} onToggle={() => toggle(m.id)} />)}
      </div>
    </>
  );
}

function ModuleCard({ m, on, saving, onToggle }) {
  return (
    <div className="card" style={{ padding: 14, opacity: on ? 1 : 0.6, cursor: onToggle ? 'pointer' : 'default' }} onClick={onToggle}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
        <span style={{ width: 32, height: 32, borderRadius: 8, background: on ? 'rgba(255,90,31,0.12)' : 'var(--app-panel-3)', color: on ? 'var(--blaze-orange)' : 'var(--app-fg3)', display: 'grid', placeItems: 'center' }}>
          <window.Icon name={m.icon} size={16} />
        </span>
        <div style={{ flex: 1 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, justifyContent: 'space-between' }}>
            <span style={{ fontWeight: 700, fontSize: 13 }}>{m.name}</span>
            {saving ? <window.Icon name="loader" size={14} /> : <Toggle on={on} />}
          </div>
          <div style={{ fontSize: 12, color: 'var(--app-fg3)', marginTop: 4 }}>{m.desc}</div>
        </div>
      </div>
    </div>
  );
}

function Toggle({ on }) {
  return (
    <span style={{ display: 'inline-flex', width: 28, height: 16, borderRadius: 999, background: on ? 'var(--blaze-orange)' : 'var(--app-panel-3)', border: '1px solid ' + (on ? 'var(--accent-hover)' : 'var(--app-border-strong)'), padding: 1, transition: 'all 200ms var(--ease-blaze)' }}>
      <span style={{ width: 12, height: 12, borderRadius: 999, background: '#fff', transform: 'translateX(' + (on ? 12 : 0) + 'px)', transition: 'transform 200ms var(--ease-blaze)' }}></span>
    </span>
  );
}

// Scopes reales del control plane (admin:* = wildcard).
const SCOPE_OPTIONS = ['messaging:read', 'messaging:write', 'payments:read', 'payments:write', 'isp:read', 'isp:write', 'billing:read', 'network:read', 'blazehub:read', 'blazehub:write', 'admin:*'];

function TenantApiKeys({ tenant, role }) {
  const bc = window.useBC();
  const live = tenant._live === true;
  const [issuing, setIssuing] = _as(false);
  const [rawKey, setRawKey] = _as(null);
  const [revoking, setRevoking] = _as(null);

  // Demo: keys mock filtradas. Live: carga real on-mount.
  _ae(() => { if (live && !bc.keysByTenant[tenant.uuid]) window.BC.loadKeys(tenant.uuid).catch(() => {}); }, [live, tenant.uuid]);

  const slot = live ? (bc.keysByTenant[tenant.uuid] || { loading: true, data: [] }) : { loading: false, data: window.API_KEYS.filter(k => k.tenant === tenant.id) };
  const keys = slot.data || [];
  const canCreate = window.hasPerm(role, 'apikey:create');

  const columns = live ? keyColumnsLive((k) => setRevoking(k)) : ApiKeyColumns;

  return (
    <window.SectionCard title="API Keys de este tenant" icon="key-round" sub={(slot.loading ? '…' : keys.length) + ' keys'} flush
      actions={canCreate && <button className="btn btn-primary btn-sm" onClick={() => setIssuing(true)}><window.Icon name="plus" size={12} /> Emitir key</button>}>
      {slot.loading ? <window.EmptyState icon="loader" title="Cargando keys…" />
       : slot.error ? <window.EmptyState icon="circle-x" title="Error" sub={slot.error} />
       : keys.length === 0 ? <window.EmptyState title="Sin keys" sub="Aún no se ha emitido ninguna API key para este tenant." />
       : <window.DataTable density="cozy" columns={columns} rows={keys} />}

      {issuing && <IssueKeyModal tenant={tenant} live={live} onClose={() => setIssuing(false)} onIssued={(d) => { setIssuing(false); setRawKey(d); }} />}
      {rawKey && <RawKeyModal data={rawKey} tenant={tenant} onClose={() => setRawKey(null)} />}
      {revoking && <window.ConfirmDialog title="Revocar API key" danger confirmLabel="Revocar"
        message={'"' + revoking.name + '" dejará de funcionar de inmediato. Esta acción no se puede deshacer.'}
        onClose={() => setRevoking(null)} onConfirm={() => window.BC.revokeKey(tenant.uuid, revoking.id).catch(() => {})} />}
    </window.SectionCard>
  );
}

// Columnas para keys reales (sin IP/preview inventados; revocar real).
function keyColumnsLive(onRevoke) {
  return [
    { label: 'Key', render: (k) => (
      <div>
        <div style={{ fontWeight: 600, fontSize: 12.5, color: 'var(--app-fg1)' }}>{k.name}</div>
        <div className="mono" style={{ fontSize: 10.5, color: 'var(--app-fg3)', marginTop: 2 }}>{k.preview}</div>
      </div>
    ) },
    { label: 'Scopes', render: (k) => <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>{k.scopes.map(s => <span key={s} className="sb neutral sb-square mono" style={{ fontSize: 10.5 }}>{s}</span>)}</div> },
    { label: 'Creada', width: 130, render: (k) => <span style={{ fontSize: 11.5, color: 'var(--app-fg3)' }}>hace {window.timeAgo(k.createdAt)}</span> },
    { label: 'Último uso', width: 130, render: (k) => <span style={{ fontSize: 11.5, color: 'var(--app-fg3)' }}>hace {window.timeAgo(k.lastUsed)}</span> },
    { label: 'Estado', width: 100, render: (k) => <window.StatusBadge status={k.status} /> },
    { label: '', width: 90, render: (k) => (
      <div style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }}>
        {k.status === 'active' && <button className="btn btn-danger btn-sm" onClick={() => onRevoke(k)}>Revocar</button>}
      </div>
    ) }
  ];
}

// Modal de emisión real (POST /admin/tenants/:uuid/apikeys).
// Si `tenant` es null y se pasa `tenants`, muestra selector de tenant (vista global).
function IssueKeyModal({ tenant, tenants, live, onClose, onIssued }) {
  const [name, setName] = _as('');
  const [scopes, setScopes] = _as(['messaging:read', 'messaging:write']);
  const [selUuid, setSelUuid] = _as(tenant ? tenant.uuid : ((tenants && tenants[0] && tenants[0].uuid) || ''));
  const [busy, setBusy] = _as(false);
  const [err, setErr] = _as(null);
  function toggleScope(s) { setScopes(p => p.includes(s) ? p.filter(x => x !== s) : [...p, s]); }
  async function submit() {
    const uuid = tenant ? tenant.uuid : selUuid;
    if (!uuid) { setErr('Selecciona un tenant'); return; }
    if (!name.trim() || scopes.length === 0) { setErr('Nombre y al menos un scope'); return; }
    if (!live) { onIssued({ raw_token: 'bk_demo_' + window.randHex(20), name: name.trim(), scopes, created_at: new Date().toISOString() }, uuid); return; }
    setBusy(true); setErr(null);
    try { onIssued(await window.BC.issueKey(uuid, { name: name.trim(), scopes }), uuid); }
    catch (e) { setErr(e.message); setBusy(false); }
  }
  return (
    <window.Modal title="Emitir nueva API key" sub={live ? 'POST /admin/tenants/:uuid/apikeys' : 'Solo visible una vez'} onClose={onClose}
      actions={<><button className="btn btn-ghost" onClick={onClose} disabled={busy}>Cancelar</button><button className="btn btn-primary" onClick={submit} disabled={busy || !name.trim()}>{busy ? 'Emitiendo…' : 'Emitir key'}</button></>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        {!tenant && tenants && (
          <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)' }}>Tenant</span>
            <select value={selUuid} onChange={(e) => setSelUuid(e.target.value)} style={{ background: 'var(--app-input-bg)', border: '1px solid var(--app-border)', borderRadius: 6, padding: '8px 10px', color: 'var(--app-fg1)', fontFamily: 'inherit', fontSize: 13 }}>
              {tenants.map(t => <option key={t.uuid} value={t.uuid}>{t.name}</option>)}
            </select>
          </label>
        )}
        <label style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
          <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)' }}>Nombre descriptivo</span>
          <input value={name} onChange={(e) => setName(e.target.value)} autoFocus placeholder="ej. Production · WhatsApp dispatcher" style={{ background: 'var(--app-input-bg)', border: '1px solid var(--app-border)', borderRadius: 6, padding: '8px 10px', color: 'var(--app-fg1)', fontFamily: 'inherit', fontSize: 13 }} />
        </label>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)', marginBottom: 6 }}>Scopes <span style={{ color: 'var(--app-fg3)', fontWeight: 400 }}>({scopes.length})</span></div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {SCOPE_OPTIONS.map(s => <button key={s} className={'fb-chip' + (scopes.includes(s) ? ' is-active' : '')} onClick={() => toggleScope(s)}>{s}</button>)}
          </div>
        </div>
        {err && <div style={{ fontSize: 12, color: 'var(--st-danger-fg)', padding: '6px 8px', background: 'var(--st-danger-bg)', borderRadius: 6 }}>{err}</div>}
      </div>
    </window.Modal>
  );
}

// Muestra el token RAW una sola vez (raw_token de la respuesta real).
function RawKeyModal({ data, tenant, onClose }) {
  const raw = data.raw_token || data.raw || '';
  return (
    <window.Modal title="Tu nueva API key" sub="⚠️ Esta es la ÚNICA vez que verás la key completa. Cópiala ahora." onClose={onClose}
      actions={<><button className="btn btn-ghost" onClick={() => navigator.clipboard && navigator.clipboard.writeText(raw)}><window.Icon name="copy" size={12} /> Copiar</button><button className="btn btn-primary" onClick={onClose}>Listo, la guardé</button></>}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div style={{ padding: 14, background: 'var(--st-warn-bg)', border: '1px solid rgba(242,169,0,0.3)', borderRadius: 8, display: 'flex', gap: 10 }}>
          <window.Icon name="triangle-alert" size={16} style={{ color: 'var(--st-warn-fg)', flex: '0 0 16px' }} />
          <div style={{ fontSize: 12, color: 'var(--app-fg1)' }}><strong>No podemos recuperar esta key más adelante.</strong> Guárdala en tu gestor de secretos.</div>
        </div>
        <div>
          <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--app-fg2)', marginBottom: 6 }}>Token</div>
          <div style={{ display: 'flex', gap: 6 }}>
            <input readOnly value={raw} style={{ flex: 1, background: 'var(--app-input-bg)', border: '1px solid var(--blaze-orange)', borderRadius: 6, padding: '10px 12px', color: 'var(--app-fg1)', fontFamily: 'var(--font-mono)', fontSize: 12 }} />
            <button className="btn btn-secondary" onClick={() => navigator.clipboard && navigator.clipboard.writeText(raw)}><window.Icon name="copy" size={12} /> Copiar</button>
          </div>
        </div>
        <dl className="kv">
          <dt>Nombre</dt><dd className="kv-text">{data.name}</dd>
          {tenant && <><dt>Tenant</dt><dd className="mono">{tenant.name}</dd></>}
          <dt>Scopes</dt><dd>{(data.scopes || []).join(', ')}</dd>
        </dl>
      </div>
    </window.Modal>
  );
}

function TenantTemplates({ tenant }) {
  const bc = window.useBC();
  const live = tenant._live === true;
  const [syncing, setSyncing] = _as(false);
  _ae(() => { if (live && !bc.templatesByTenant[tenant.uuid]) window.BC.loadTemplates(tenant.uuid).catch(() => {}); }, [live, tenant.uuid]);

  const slot = live ? (bc.templatesByTenant[tenant.uuid] || { loading: true, data: [] }) : { loading: false, data: window.TEMPLATES };
  const tpls = slot.data || [];
  async function sync() { setSyncing(true); try { await window.BC.syncTemplates(tenant.uuid); } catch (e) {} finally { setSyncing(false); } }

  return (
    <window.SectionCard title="Plantillas WhatsApp Cloud" icon="file-text" sub={(slot.loading ? '…' : tpls.length) + ' plantillas'} flush
      actions={<button className="btn btn-ghost btn-sm" onClick={live ? sync : undefined} disabled={syncing}><window.Icon name="refresh-cw" size={12} /> {syncing ? 'Sincronizando…' : 'Sync ahora'}</button>}>
      {slot.loading ? <window.EmptyState icon="loader" title="Cargando plantillas…" />
       : slot.error ? <window.EmptyState icon="circle-x" title="Error" sub={slot.error} />
       : tpls.length === 0 ? <window.EmptyState icon="file-text" title="Sin plantillas" sub="No hay plantillas registradas para este tenant." />
       : <window.DataTable density="cozy" columns={[
        { label: 'Nombre', render: (t) => <span className="mono" style={{ fontWeight: 600, color: 'var(--st-violet-fg)' }}>{t.name}</span> },
        { label: 'Categoría', width: 140, render: (t) => <span className={'sb sb-square ' + (t.category === 'UTILITY' ? 'info' : t.category === 'AUTHENTICATION' ? 'success' : 'warn')}>{t.category}</span> },
        { label: 'Idioma', width: 80, render: (t) => <span className="mono">{t.lang}</span> },
        { label: 'Estado', width: 130, render: (t) => <window.StatusBadge tone={t.status === 'APPROVED' ? 'success' : t.status === 'PENDING' ? 'warn' : 'danger'} label={t.status} /> }
      ]} rows={tpls} />}
    </window.SectionCard>
  );
}

function TenantAudit({ tenant }) {
  const events = window.AUDIT_EVENTS.filter(a => a.tenant === tenant.id);
  return (
    <window.SectionCard title="Auditoría · " icon="shield-check" sub={tenant.name} flush>
      {events.length === 0
        ? <window.EmptyState icon="shield-check" title="Sin eventos" sub="Este tenant aún no ha generado eventos auditables." />
        : <AuditTable events={events} />}
    </window.SectionCard>
  );
}

/* =========================================================================
   API KEYS — global
   ========================================================================= */
const ApiKeyColumns = [
  { label: 'Key', render: (k) => (
    <div>
      <div style={{ fontWeight: 600, fontSize: 12.5, color: 'var(--app-fg1)' }}>{k.name}</div>
      <div className="mono" style={{ fontSize: 10.5, color: 'var(--app-fg3)', marginTop: 2 }}>{k.preview}</div>
    </div>
  ) },
  { label: 'Tenant', width: 160, render: (k) => <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><window.TenantAvatar tenant={{ id: k.tenant, name: k.tenantName }} size={16} /> <span className="truncate" style={{ maxWidth: 130 }}>{k.tenantName}</span></span> },
  { label: 'Scopes', render: (k) => <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>{k.scopes.map(s => <span key={s} className="sb neutral sb-square mono" style={{ fontSize: 10.5 }}>{s}</span>)}</div> },
  { label: 'Última IP', width: 130, render: (k) => <span className="mono" style={{ fontSize: 11, color: 'var(--app-fg3)' }}>{k.lastIp}</span> },
  { label: 'Último uso', width: 130, render: (k) => <span style={{ fontSize: 11.5, color: 'var(--app-fg3)' }}>hace {window.timeAgo(k.lastUsed)}</span> },
  { label: 'Estado', width: 100, render: (k) => <window.StatusBadge status={k.status} /> },
  { label: '', width: 80, render: (k) => (
    <div style={{ display: 'flex', gap: 4, justifyContent: 'flex-end' }}>
      {k.status === 'active' && <button className="btn btn-danger btn-sm">Revocar</button>}
      <button className="btn btn-ghost btn-sm btn-icon-only"><window.Icon name="more-horizontal" size={13} /></button>
    </div>
  ) }
];

function PageApiKeys({ tenant, role }) {
  const bc = window.useBC();
  const live = bc.mode === 'live';
  const [issuing, setIssuing] = _as(false);
  const [rawKey, setRawKey] = _as(null);
  const [revoking, setRevoking] = _as(null);
  const [search, setSearch] = _as('');

  // Tenants en alcance (uno o todos).
  const scope = _am(() => tenant.id === '*' ? bc.tenants : bc.tenants.filter(t => t.id === tenant.id), [tenant, bc.tenants]);

  // Live: carga keys de cada tenant en alcance (una vez).
  _ae(() => {
    if (!live) return;
    scope.forEach(t => { if (!bc.keysByTenant[t.uuid]) window.BC.loadKeys(t.uuid).catch(() => {}); });
  }, [live, scope]);

  const rows = _am(() => {
    const ql = search.toLowerCase();
    let all;
    if (live) {
      all = [];
      scope.forEach(t => { (bc.keysByTenant[t.uuid] || {}).data && (bc.keysByTenant[t.uuid].data).forEach(k => all.push(k)); });
    } else {
      all = window.API_KEYS.filter(k => tenant.id === '*' || k.tenant === tenant.id);
    }
    return all.filter(k => !ql || (k.name + ' ' + k.tenantName + ' ' + k.id).toLowerCase().includes(ql));
  }, [live, scope, search, bc.keysByTenant]);

  const loading = live && scope.some(t => (bc.keysByTenant[t.uuid] || {}).loading) && rows.length === 0;

  const columns = live ? [
    { label: 'Key', render: (k) => <div><div style={{ fontWeight: 600, fontSize: 12.5, color: 'var(--app-fg1)' }}>{k.name}</div><div className="mono" style={{ fontSize: 10.5, color: 'var(--app-fg3)', marginTop: 2 }}>{k.preview}</div></div> },
    { label: 'Tenant', width: 180, render: (k) => <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><window.TenantAvatar tenant={{ id: k.tenant, name: k.tenantName }} size={16} /> <span className="truncate" style={{ maxWidth: 150 }}>{k.tenantName}</span></span> },
    { label: 'Scopes', render: (k) => <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>{k.scopes.map(s => <span key={s} className="sb neutral sb-square mono" style={{ fontSize: 10.5 }}>{s}</span>)}</div> },
    { label: 'Último uso', width: 120, render: (k) => <span style={{ fontSize: 11.5, color: 'var(--app-fg3)' }}>hace {window.timeAgo(k.lastUsed)}</span> },
    { label: 'Estado', width: 90, render: (k) => <window.StatusBadge status={k.status} /> },
    { label: '', width: 90, render: (k) => k.status === 'active' ? <div style={{ display: 'flex', justifyContent: 'flex-end' }}><button className="btn btn-danger btn-sm" onClick={() => setRevoking(k)}>Revocar</button></div> : null }
  ] : ApiKeyColumns;

  return (
    <div className="page">
      <div className="page-header">
        <div>
          <h1 className="page-title">API Keys {live && <span className="sb success sb-square" style={{ fontSize: 10, verticalAlign: 'middle' }}>live</span>}</h1>
          <p className="page-sub">{rows.length} keys · {rows.filter(k => k.status === 'active').length} activas · {rows.filter(k => k.status === 'revoked').length} revocadas{tenant.id !== '*' && ' · ' + tenant.name}</p>
        </div>
        <div className="page-actions">
          {window.hasPerm(role, 'apikey:create') && <button className="btn btn-primary btn-sm" onClick={() => setIssuing(true)} disabled={live && scope.length === 0}><window.Icon name="plus" size={12} /> Emitir API key</button>}
        </div>
      </div>

      <div className="card" style={{ marginBottom: 12, background: 'var(--st-info-bg)', borderColor: 'rgba(30,123,255,0.3)' }}>
        <div className="card-body" style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <window.Icon name="shield" size={16} style={{ color: 'var(--st-info-fg)' }} />
          <div style={{ flex: 1, fontSize: 12.5 }}>
            <strong style={{ color: 'var(--app-fg1)' }}>Las keys solo se muestran una vez al emitirlas.</strong>
            <span style={{ color: 'var(--app-fg2)', marginLeft: 6 }}>Cópiala a tu gestor de secretos antes de cerrar el modal — no hay forma de recuperarla luego.</span>
          </div>
        </div>
      </div>

      <div className="card">
        <window.FilterBar>
          <window.FilterSearch value={search} onChange={setSearch} placeholder="Buscar por nombre, tenant, key prefix…" />
          <span className="fb-spacer"></span>
        </window.FilterBar>
        {loading ? <window.EmptyState icon="loader" title="Cargando API keys…" />
         : <window.DataTable density="cozy" columns={columns} rows={rows} />}
      </div>

      {issuing && <IssueKeyModal tenant={tenant.id !== '*' ? scope[0] : null} tenants={tenant.id === '*' ? scope : null} live={live}
        onClose={() => setIssuing(false)} onIssued={(d) => { setIssuing(false); setRawKey(d); }} />}
      {rawKey && <RawKeyModal data={rawKey} onClose={() => setRawKey(null)} />}
      {revoking && <window.ConfirmDialog title="Revocar API key" danger confirmLabel="Revocar"
        message={'"' + revoking.name + '" dejará de funcionar de inmediato.'}
        onClose={() => setRevoking(null)} onConfirm={() => window.BC.revokeKey(revoking.tenant, revoking.id).catch(() => {})} />}
    </div>
  );
}

/* =========================================================================
   TEMPLATES (global)
   ========================================================================= */
function PageTemplates({ tenant }) {
  return (
    <div className="page">
      <div className="page-header">
        <div>
          <h1 className="page-title">Templates WhatsApp</h1>
          <p className="page-sub">{window.TEMPLATES.length} plantillas · sincronizadas con Meta Business Manager</p>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost btn-sm"><window.Icon name="refresh-cw" size={12} /> Sync con Meta</button>
          <button className="btn btn-primary btn-sm"><window.Icon name="plus" size={12} /> Nueva plantilla</button>
        </div>
      </div>
      <div className="grid-3" style={{ marginBottom: 12 }}>
        <window.KpiCard label="Aprobadas" value={window.TEMPLATES.filter(t => t.status === 'APPROVED').length} color="green" />
        <window.KpiCard label="Pendientes" value={window.TEMPLATES.filter(t => t.status === 'PENDING').length} color="warn" />
        <window.KpiCard label="Rechazadas" value={window.TEMPLATES.filter(t => t.status === 'REJECTED').length} color="danger" foot="revisa políticas de Meta" />
      </div>
      <div className="card">
        <window.FilterBar>
          <window.FilterSearch value="" onChange={() => {}} placeholder="Buscar plantilla…" />
          <window.FilterChip label="Estado" icon="plus" />
          <window.FilterChip label="Categoría" icon="plus" />
          <window.FilterChip label="Idioma" icon="plus" />
        </window.FilterBar>
        <window.DataTable density="cozy" columns={[
          { label: 'Nombre', render: (t) => <div><div className="mono" style={{ fontWeight: 600, color: 'var(--st-violet-fg)' }}>{t.name}</div><div style={{ fontSize: 11, color: 'var(--app-fg3)', marginTop: 2 }}>{t.name === 'factura_lista_ES' ? 'Su factura de {{1}} está lista. Monto: {{2}}. Vence el {{3}}.' : t.name === 'pago_recibido_ES' ? 'Confirmamos su pago de {{1}}. Gracias.' : 'Plantilla operacional'}</div></div> },
          { label: 'Categoría', width: 140, render: (t) => <span className={'sb sb-square ' + (t.category === 'UTILITY' ? 'info' : t.category === 'AUTHENTICATION' ? 'success' : 'warn')}>{t.category}</span> },
          { label: 'Idioma', width: 80, render: (t) => <span className="mono">{t.lang}</span> },
          { label: 'Estado', width: 130, render: (t) => <window.StatusBadge tone={t.status === 'APPROVED' ? 'success' : t.status === 'PENDING' ? 'warn' : 'danger'} label={t.status} /> },
          { label: 'Tenants usando', width: 130, numeric: true, render: () => <span className="mono">{Math.floor(Math.random() * 8) + 1}</span> },
          { label: 'Envíos 24h', width: 100, numeric: true, render: () => <span className="mono">{(Math.floor(Math.random() * 5000) + 200).toLocaleString()}</span> },
          { label: 'Última sync', width: 130, render: () => <span style={{ fontSize: 11, color: 'var(--app-fg3)' }}>hace {Math.floor(Math.random() * 30) + 1}m</span> }
        ]} rows={window.TEMPLATES} />
      </div>
    </div>
  );
}

/* =========================================================================
   AUDITORÍA
   ========================================================================= */
const ACTION_TONE = {
  'tenant.created':    { tone: 'success', ico: 'plus' },
  'tenant.updated':    { tone: 'info',    ico: 'pencil' },
  'apikey.created':    { tone: 'success', ico: 'key-round' },
  'apikey.revoked':    { tone: 'danger',  ico: 'key-round' },
  'config.updated':    { tone: 'info',    ico: 'settings' },
  'template.synced':   { tone: 'info',    ico: 'refresh-cw' },
  'template.rejected': { tone: 'danger',  ico: 'x' },
  'retry.executed':    { tone: 'warn',    ico: 'rotate-cw' },
  'message.viewed':    { tone: 'info',    ico: 'eye' }
};

function AuditTable({ events }) {
  return (
    <window.DataTable density="cozy" columns={[
      { label: 'Hora', width: 150, render: (a) => <span className="mono" style={{ color: 'var(--app-fg3)' }}>{window.fmtDateTime(a.ts)}</span> },
      { label: 'Acción', width: 240, render: (a) => {
        const c = ACTION_TONE[a.action] || { tone: 'neutral', ico: 'circle' };
        return <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
          <span className={'tl-marker ' + c.tone} style={{ width: 22, height: 22 }}><window.Icon name={c.ico} size={11} /></span>
          <span className="mono" style={{ fontWeight: 600, color: 'var(--st-violet-fg)' }}>{a.action}</span>
        </span>;
      } },
      { label: 'Actor', width: 220, render: (a) => (
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          {a.actor.type === 'user'
            ? <window.UserAvatar name={a.actor.name} size={18} />
            : <span style={{ width: 18, height: 18, borderRadius: 4, background: 'var(--app-panel-3)', display: 'inline-grid', placeItems: 'center' }}><window.Icon name="cpu" size={11} /></span>}
          <span>
            <span style={{ fontWeight: 600 }}>{a.actor.name}</span>
            {a.actor.role && <span style={{ color: 'var(--app-fg3)', marginLeft: 4, fontSize: 11 }}>· {a.actor.role}</span>}
          </span>
        </span>
      ) },
      { label: 'Target', render: (a) => <span className="mono" style={{ color: 'var(--app-fg2)' }}>{a.target}</span> },
      { label: 'Tenant', width: 160, render: (a) => a.tenant
        ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}><window.TenantAvatar tenant={{ id: a.tenant, name: (window.TENANTS.find(t => t.id === a.tenant) || {}).name || a.tenant }} size={16} /> {(window.TENANTS.find(t => t.id === a.tenant) || {}).name || a.tenant}</span>
        : <span style={{ color: 'var(--app-fg-faint)' }}>global</span> },
      { label: 'Detalles', render: (a) => <span className="mono" style={{ fontSize: 11, color: 'var(--app-fg3)' }}>{Object.entries(a.meta).slice(0, 2).map(([k, v]) => k + '=' + (Array.isArray(v) ? v.length + ' items' : String(v))).join(' · ')}</span> }
    ]} rows={events} />
  );
}

function PageAudit({ tenant }) {
  const [search, setSearch] = _as('');
  const rows = _am(() => {
    const ql = search.toLowerCase();
    return window.AUDIT_EVENTS.filter(a => {
      if (tenant.id !== '*' && a.tenant && a.tenant !== tenant.id) return false;
      if (ql && !(a.action + ' ' + a.actor.name + ' ' + a.target).toLowerCase().includes(ql)) return false;
      return true;
    });
  }, [search, tenant]);
  return (
    <div className="page">
      <div className="page-header">
        <div>
          <h1 className="page-title">Auditoría</h1>
          <p className="page-sub">Registro inmutable · {rows.length} eventos · retención 365 días</p>
        </div>
        <div className="page-actions">
          <button className="btn btn-ghost btn-sm"><window.Icon name="calendar" size={12} /> Últimos 7 días</button>
          <button className="btn btn-ghost btn-sm"><window.Icon name="download" size={12} /> Export JSONL</button>
          <button className="btn btn-secondary btn-sm"><window.Icon name="shield-check" size={12} /> Compliance report</button>
        </div>
      </div>
      <div className="card">
        <window.FilterBar>
          <window.FilterSearch value={search} onChange={setSearch} placeholder="Buscar acción, actor, target…" />
          <window.FilterChip label="Acción" icon="plus" />
          <window.FilterChip label="Actor" icon="plus" />
          <window.FilterChip label="Tipo" icon="plus" />
        </window.FilterBar>
        <AuditTable events={rows} />
      </div>
    </div>
  );
}

Object.assign(window, { PageTenants, PageTenantDetail, PageApiKeys, PageTemplates, PageAudit, AuditTable });
