// Governance & compliance: every setting a client can configure, in one place.
// Live mode reads GET /v1/settings and writes the two editable surfaces
// (retention, privacy). Everything else is shown read-only with the reason it
// isn't editable yet. Demo mode renders a fixture with the controls locked.

const GOV_TEXT = {
  'pt-PT': {
    title: 'Governança e Conformidade',
    subtitle: 'Lei 2/10 · quanto tempo guardamos cada dado · o que a API esconde · quem viu o quê.',
    live: 'Ligado à API', demo: 'Modo demonstração', demoNote: 'Valores de exemplo. Ligue a API (?api=…&token=…) para editar.',
    retention: 'Retenção de dados', retentionHint: 'Dias até um dado ser apagado. Vazio = guardado sem limite.',
    notEnforced: 'Ainda não aplicado', dataClass: 'Tipo de dado', stored: 'Guardado agora', keep: 'Guardar durante', legalHold: 'Retenção legal',
    days: 'dias', noLimit: 'sem limite', rows: 'registos', oldest: 'mais antigo', save: 'Guardar', saved: 'Guardado', saving: 'A guardar…',
    privacy: 'Privacidade dos atributos', privacyHint: 'Campos marcados nunca saem da API para este cliente. A extracção continua; só a exposição muda.',
    hidden: 'escondido', shown: 'visível',
    exports: 'Exportações com cadeia de custódia', exportsEmpty: 'Nenhuma exportação.', newExport: 'Nova exportação',
    readOnly: 'Só leitura', none: 'Nenhum configurado.',
    rules: 'Regras de alerta', channels: 'Canais de notificação', subscriptions: 'Subscrições', watchlists: 'Listas de vigilância',
    eventTypes: 'Tipos de evento', eventTypesHint: 'Registo global partilhado por todos os clientes.',
    audit: 'Registo de auditoria', auditEmpty: 'Sem entradas ainda.', before: 'antes', after: 'depois', ago: 'atrás',
    error: 'Não foi possível carregar as definições',
    enforcedNote: 'As políticas ficam guardadas, mas ainda não corre nenhuma limpeza automática: nada é apagado.',
    why: {
      rules: 'A edição de regras chega com a API de escrita do motor de regras. As regras já correm; hoje editam-se na base de dados.',
      notification_channels: 'Precisa primeiro de um cofre para as credenciais de SMS/WhatsApp/webhook.',
      subscriptions: 'Depende de contas de utilizador (login ainda não existe).',
      watchlists: 'Listas biométricas exigem um registo de autorização por entrada; só leitura até isso existir.',
      exports: 'Precisa de contas de utilizador e do pipeline de clips; ainda não há nada para exportar.',
    },
  },
  en: {
    title: 'Governance & Compliance',
    subtitle: 'Law 2/10 · how long each kind of data is kept · what the API hides · who looked at what.',
    live: 'Connected to API', demo: 'Demo mode', demoNote: 'Example values. Connect the API (?api=…&token=…) to edit.',
    retention: 'Data retention', retentionHint: 'Days until data is deleted. Empty = kept with no limit.',
    notEnforced: 'Not enforced yet', dataClass: 'Data type', stored: 'Stored now', keep: 'Keep for', legalHold: 'Legal hold',
    days: 'days', noLimit: 'no limit', rows: 'rows', oldest: 'oldest', save: 'Save', saved: 'Saved', saving: 'Saving…',
    privacy: 'Attribute privacy', privacyHint: 'Checked fields never leave the API for this client. Extraction continues; only exposure changes.',
    hidden: 'hidden', shown: 'shown',
    exports: 'Chain-of-custody exports', exportsEmpty: 'No exports.', newExport: 'New export',
    readOnly: 'Read-only', none: 'None configured.',
    rules: 'Alert rules', channels: 'Notification channels', subscriptions: 'Subscriptions', watchlists: 'Watchlists',
    eventTypes: 'Event types', eventTypesHint: 'Global registry shared by every client.',
    audit: 'Audit log', auditEmpty: 'No entries yet.', before: 'before', after: 'after', ago: 'ago',
    error: 'Could not load settings',
    enforcedNote: 'Policies are stored, but no clean-up job runs yet: nothing is deleted automatically.',
    why: {},
  },
};

const DATA_CLASS_TEXT = {
  segments:     { pt: 'Gravação contínua', en: 'Continuous recording', dpt: 'Vídeo bruto de cada câmara', den: 'Raw video from each camera' },
  clips:        { pt: 'Clips', en: 'Clips', dpt: 'Excertos guardados de eventos', den: 'Saved excerpts around events' },
  observations: { pt: 'Observações', en: 'Observations', dpt: 'Cada pessoa/veículo seguido, com descrição', den: 'Each tracked person/vehicle, with description' },
  detections:   { pt: 'Detecções', en: 'Detections', dpt: 'Posição em cada frame', den: 'Position in every frame' },
  embeddings:   { pt: 'Impressões biométricas', en: 'Biometric fingerprints', dpt: 'Vectores de rosto/corpo para reconhecer alguém', den: 'Face/body vectors used to recognise someone' },
  events:       { pt: 'Eventos e alertas', en: 'Events & alerts', dpt: 'Entradas, saídas, regras accionadas', den: 'Entries, exits, triggered rules' },
  audit:        { pt: 'Auditoria', en: 'Audit trail', dpt: 'Quem viu ou alterou o quê', den: 'Who viewed or changed what' },
};

const ATTR_TEXT = {
  gender: { pt: 'Género', en: 'Gender', legal: true },
  estimated_age_range: { pt: 'Faixa etária', en: 'Age range', legal: true },
  clothing: { pt: 'Vestuário', en: 'Clothing' },
  accessories: { pt: 'Acessórios', en: 'Accessories' },
  carrying: { pt: 'Objectos transportados', en: 'Items carried' },
  action: { pt: 'Acção', en: 'Action' },
  direction: { pt: 'Direcção', en: 'Direction' },
  location_context: { pt: 'Contexto do local', en: 'Location context' },
};

const SEV_LABEL = ['info', 'low', 'medium', 'high', 'critical'];

const GOV_DEMO = {
  org: { name: 'Banco de Fomento Angola', slug: 'bfa', plan: {} },
  privacy: { excluded_attributes: ['gender', 'estimated_age_range'], excludable: Object.keys(ATTR_TEXT), editable: true },
  retention: {
    enforced: false, editable: true, site_overrides: [],
    defaults: [
      { data_class: 'segments', retain_days: 30, legal_hold: false, rows: 8640, oldest: new Date(Date.now() - 29 * 864e5).toISOString() },
      { data_class: 'clips', retain_days: 90, legal_hold: true, rows: 412, oldest: new Date(Date.now() - 88 * 864e5).toISOString() },
      { data_class: 'observations', retain_days: 30, legal_hold: false, rows: 18230, oldest: new Date(Date.now() - 30 * 864e5).toISOString() },
      { data_class: 'detections', retain_days: 7, legal_hold: false, rows: 912400, oldest: null },
      { data_class: 'embeddings', retain_days: 30, legal_hold: false, rows: 16100, oldest: new Date(Date.now() - 30 * 864e5).toISOString() },
      { data_class: 'events', retain_days: 365, legal_hold: false, rows: 5120, oldest: new Date(Date.now() - 200 * 864e5).toISOString() },
      { data_class: 'audit', retain_days: null, legal_hold: false, rows: 2301, oldest: new Date(Date.now() - 200 * 864e5).toISOString() },
    ],
  },
  rules: { editable: false, reason: 'Rule editing lands with the rule-engine write API.', items: [
    { id: 'r1', name: 'Pessoa junto ao cofre após o fecho', enabled: true, zone: 'Antecâmara Cofre', camera: 'Sala de Cofre', severity_override: 4 },
    { id: 'r2', name: 'Permanência > 3 min no ATM', enabled: true, zone: 'Caixa ATM', camera: 'ATM Exterior', severity_override: 3 },
  ] },
  notification_channels: { editable: false, reason: 'Needs secret storage for channel credentials first.', items: [
    { id: 1, kind: 'whatsapp', name: 'Sala de controlo', enabled: true },
    { id: 2, kind: 'email', name: 'Segurança central', enabled: true },
  ] },
  subscriptions: { editable: false, reason: 'Depends on user accounts.', items: [
    { id: 1, channel: 'Sala de controlo', kind: 'whatsapp', min_severity: 3, kinds: null, enabled: true },
  ] },
  watchlists: { editable: false, reason: 'Needs an authorisation trail per entry.', items: [
    { id: 'w1', name: 'Pessoas barradas', reason: 'Decisão da direcção de segurança', entries: 3 },
  ] },
  exports: { editable: false, reason: 'Needs user accounts and the clip pipeline.', items: [] },
  event_types: { editable: false, reason: 'Global registry shared by every client.', items: [
    { kind: 'zone_entry', default_severity: 0, producer: 'pipeline', enabled: true },
    { kind: 'zone_exit', default_severity: 0, producer: 'pipeline', enabled: true },
    { kind: 'rule_triggered', default_severity: 3, producer: 'rule_engine', enabled: true },
    { kind: 'camera_offline', default_severity: 2, producer: 'platform', enabled: true },
  ] },
  audit: { items: [
    { action: 'settings.retention.update', subject_id: 'clips', context: { before: { retain_days: 30, legal_hold: false }, after: { retain_days: 90, legal_hold: true } }, at: new Date(Date.now() - 3600e3).toISOString() },
    { action: 'search.attributes', subject_id: null, context: {}, at: new Date(Date.now() - 7200e3).toISOString() },
  ] },
};

function govApi() {
  return window.VERACAM_API || null;
}

async function govFetch(path, opts = {}) {
  const api = govApi();
  const res = await fetch(`${api.base}/v1${path}`, {
    ...opts,
    headers: { 'Content-Type': 'application/json', ...(api.token ? { Authorization: `Bearer ${api.token}` } : {}) },
  });
  const body = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(body?.error?.message || `HTTP ${res.status}`);
  return body.data;
}

function fmtCount(n) {
  if (n == null) return '—';
  return n.toLocaleString('pt-PT');
}

function daysSince(iso) {
  if (!iso) return null;
  return Math.max(0, Math.floor((Date.now() - Date.parse(iso)) / 864e5));
}

function GovHeader({ T, live, org }) {
  return (
    <div className="px-5 pt-5 pb-3 flex flex-wrap items-start justify-between gap-3 shrink-0">
      <div className="flex items-start gap-3">
        <div className="w-10 h-10 rounded-md bg-forest-500/10 border border-forest-500/30 text-forest-400 flex items-center justify-center shrink-0">
          <Icons.Shield size={20} />
        </div>
        <div>
          <h1 className="font-display text-xl font-semibold text-slate-100">{T.title}</h1>
          <p className="text-[13px] text-slate-400 mt-0.5">{T.subtitle}</p>
        </div>
      </div>
      <div className="flex items-center gap-2">
        {org && <Pill tone="ghost"><Icons.Building size={11} /> {org.name}</Pill>}
        {live ? <Pill tone="forest">● {T.live}</Pill> : <Pill tone="amber">{T.demo}</Pill>}
      </div>
    </div>
  );
}

function LockNote({ T, reason }) {
  return (
    <div className="flex items-start gap-2 px-4 py-2.5 text-[11.5px] text-slate-400 border-t border-slate-700/60 bg-slate-900/30">
      <span className="font-mono text-[10px] uppercase tracking-wider text-slate-500 shrink-0 pt-px">{T.readOnly}</span>
      <span>{reason}</span>
    </div>
  );
}

function RetentionRow({ row, T, isPt, canEdit, onSave }) {
  const [days, setDays] = React.useState(row.retain_days ?? '');
  const [hold, setHold] = React.useState(!!row.legal_hold);
  const [state, setState] = React.useState('idle');
  React.useEffect(() => { setDays(row.retain_days ?? ''); setHold(!!row.legal_hold); }, [row.retain_days, row.legal_hold]);
  const dirty = String(days) !== String(row.retain_days ?? '') || hold !== !!row.legal_hold;
  const txt = DATA_CLASS_TEXT[row.data_class] || {};
  const age = daysSince(row.oldest);
  const over = row.retain_days && age != null && age > row.retain_days;

  async function save() {
    setState('saving');
    try {
      await onSave(row.data_class, days === '' ? null : Number(days), hold);
      setState('saved');
      setTimeout(() => setState('idle'), 1600);
    } catch (e) {
      setState('error:' + e.message);
    }
  }

  return (
    <div className="grid grid-cols-12 gap-3 items-center px-4 py-3 border-b border-slate-800/70 last:border-0">
      <div className="col-span-12 md:col-span-4">
        <div className="text-[13px] text-slate-100 font-medium">{isPt ? txt.pt : txt.en}</div>
        <div className="text-[11px] text-slate-500">{isPt ? txt.dpt : txt.den} · <span className="font-mono">{row.data_class}</span></div>
      </div>
      <div className="col-span-6 md:col-span-3 font-mono text-[11.5px] text-slate-300">
        {fmtCount(row.rows)} <span className="text-slate-500">{T.rows}</span>
        {age != null && (
          <div className={`text-[10.5px] ${over ? 'text-amber-300' : 'text-slate-500'}`}>{T.oldest}: {age} {T.days}</div>
        )}
      </div>
      <div className="col-span-6 md:col-span-2">
        <label className="flex items-center gap-1.5">
          <input
            id={`ret-${row.data_class}`}
            type="number" min="1" max="3650" inputMode="numeric"
            disabled={!canEdit}
            value={days}
            placeholder="∞"
            onChange={(e) => setDays(e.target.value)}
            className="w-20 h-8 px-2 rounded bg-slate-900 border border-slate-700 text-slate-100 font-mono text-[12.5px] focus:outline-none focus:border-forest-500 disabled:opacity-60"
          />
          <span className="text-[11px] text-slate-500">{days === '' ? T.noLimit : T.days}</span>
        </label>
      </div>
      <div className="col-span-6 md:col-span-2">
        <label className="inline-flex items-center gap-2 text-[12px] text-slate-300 cursor-pointer">
          <input id={`hold-${row.data_class}`} type="checkbox" disabled={!canEdit} checked={hold}
            onChange={(e) => setHold(e.target.checked)} className="accent-amber-500" />
          {T.legalHold}
        </label>
      </div>
      <div className="col-span-6 md:col-span-1 flex justify-end">
        {state === 'saved' ? (
          <span className="text-[11px] text-forest-400 inline-flex items-center gap-1"><Icons.Check size={12} />{T.saved}</span>
        ) : (
          <Button size="sm" variant={dirty ? 'primary' : 'outline'} disabled={!canEdit || !dirty || state === 'saving'} onClick={save}>
            {state === 'saving' ? T.saving : T.save}
          </Button>
        )}
      </div>
      {state.startsWith('error:') && (
        <div className="col-span-12 text-[11px] text-red-300">{state.slice(6)}</div>
      )}
    </div>
  );
}

function PrivacyPanel({ data, T, isPt, canEdit, onSave }) {
  const [sel, setSel] = React.useState(new Set(data.excluded_attributes));
  const [state, setState] = React.useState('idle');
  React.useEffect(() => { setSel(new Set(data.excluded_attributes)); }, [data.excluded_attributes.join(',')]);
  const dirty = [...sel].sort().join(',') !== [...data.excluded_attributes].sort().join(',');
  function toggle(a) {
    const n = new Set(sel);
    n.has(a) ? n.delete(a) : n.add(a);
    setSel(n);
  }
  async function save() {
    setState('saving');
    try { await onSave([...sel]); setState('saved'); setTimeout(() => setState('idle'), 1600); }
    catch (e) { setState('error:' + e.message); }
  }
  return (
    <Section
      title={<><Icons.Scan size={13} /> {T.privacy}</>}
      action={state === 'saved'
        ? <span className="text-[11px] text-forest-400 inline-flex items-center gap-1"><Icons.Check size={12} />{T.saved}</span>
        : <Button size="sm" variant={dirty ? 'primary' : 'outline'} disabled={!canEdit || !dirty || state === 'saving'} onClick={save}>{state === 'saving' ? T.saving : T.save}</Button>}
    >
      <p className="px-4 pt-3 text-[12px] text-slate-400">{T.privacyHint}</p>
      <div className="p-4 grid grid-cols-1 sm:grid-cols-2 gap-2">
        {data.excludable.map((a) => {
          const on = sel.has(a);
          const txt = ATTR_TEXT[a] || { pt: a, en: a };
          return (
            <label key={a} className={`flex items-center justify-between gap-3 px-3 py-2 rounded border cursor-pointer transition-colors ${on ? 'bg-red-500/10 border-red-500/40' : 'bg-slate-900/40 border-slate-700/70 hover:border-slate-600'}`}>
              <span className="flex items-center gap-2">
                <input id={`attr-${a}`} type="checkbox" disabled={!canEdit} checked={on} onChange={() => toggle(a)} className="accent-red-500" />
                <span className="text-[12.5px] text-slate-100">{isPt ? txt.pt : txt.en}</span>
                {txt.legal && <Pill tone="amber" className="!text-[9.5px] !py-0">Q4</Pill>}
              </span>
              <span className={`font-mono text-[10.5px] ${on ? 'text-red-300' : 'text-slate-500'}`}>{on ? T.hidden : T.shown}</span>
            </label>
          );
        })}
      </div>
      {state.startsWith('error:') && <div className="px-4 pb-3 text-[11px] text-red-300">{state.slice(6)}</div>}
    </Section>
  );
}

function ReadOnlyPanel({ T, title, icon: Icon, block, render, k }) {
  return (
    <Section title={<><Icon size={13} /> {title} <span className="font-mono text-slate-500 normal-case tracking-normal">· {block.items.length}</span></>}>
      <div className="divide-y divide-slate-800/60">
        {block.items.length === 0
          ? <div className="px-4 py-3 text-[12px] text-slate-500">{T.none}</div>
          : block.items.map((it, i) => <div key={it.id ?? i} className="px-4 py-2.5">{render(it)}</div>)}
      </div>
      <LockNote T={T} reason={(T.why && T.why[k]) || block.reason} />
    </Section>
  );
}

function AuditRow({ a, T, lang }) {
  const c = a.context || {};
  const show = (v) => (v == null ? '∅' : typeof v === 'object' ? JSON.stringify(v) : String(v));
  return (
    <div className="px-4 py-2.5 flex flex-wrap items-baseline gap-x-3 gap-y-1 hover:bg-slate-850/30">
      <div className="font-mono text-[10px] text-slate-500 w-24 shrink-0">{relTime(Date.parse(a.at), lang)}</div>
      <div className="font-mono text-[11.5px] text-slate-200">{a.action}</div>
      {a.subject_id && <Pill tone="ghost" className="!text-[10px]">{a.subject_id}</Pill>}
      {'before' in c && (
        <div className="basis-full md:basis-auto md:ml-auto font-mono text-[10.5px] text-slate-400 break-all">
          <span className="text-slate-500">{T.before}</span> {show(c.before)} <span className="text-slate-500">→ {T.after}</span> <span className="text-slate-200">{show(c.after)}</span>
        </div>
      )}
    </div>
  );
}

function GovernanceView() {
  const { lang } = useI18n();
  const isPt = lang !== 'en';
  const T = GOV_TEXT[isPt ? 'pt-PT' : 'en'];
  const live = !!(govApi() && window.VERACAM_LIVE);
  const [data, setData] = React.useState(live ? null : GOV_DEMO);
  const [err, setErr] = React.useState(null);

  const load = React.useCallback(async () => {
    if (!live) return;
    try { setData(await govFetch('/settings')); setErr(null); }
    catch (e) { setErr(e.message); }
  }, [live]);
  React.useEffect(() => { load(); }, [load]);

  async function saveRetention(cls, retain_days, legal_hold) {
    await govFetch(`/settings/retention/${cls}`, { method: 'PUT', body: JSON.stringify({ retain_days, legal_hold }) });
    await load();
  }
  async function savePrivacy(excluded_attributes) {
    await govFetch('/settings/privacy', { method: 'PUT', body: JSON.stringify({ excluded_attributes }) });
    await load();
  }

  if (err) {
    return (
      <div className="flex flex-col h-full overflow-auto">
        <GovHeader T={T} live={live} />
        <div className="px-5"><div className="p-4 rounded border border-red-500/40 bg-red-500/10 text-[13px] text-red-200">{T.error}: {err}</div></div>
      </div>
    );
  }
  if (!data) {
    return <div className="flex flex-col h-full overflow-auto"><GovHeader T={T} live={live} /><div className="px-5 text-slate-500 text-[13px]">…</div></div>;
  }

  const canEdit = live;
  return (
    <div className="flex flex-col h-full overflow-auto">
      <GovHeader T={T} live={live} org={data.org} />
      <div className="px-5 pb-6 space-y-4">
        {!live && (
          <div className="px-4 py-2.5 rounded border border-amber-500/30 bg-amber-500/5 text-[12px] text-amber-200">{T.demoNote}</div>
        )}

        <Section
          title={<><Icons.Clock size={13} /> {T.retention}</>}
          action={!data.retention.enforced && <Pill tone="amber" title={T.enforcedNote || data.retention.enforced_note}>{T.notEnforced}</Pill>}
        >
          <div className="px-4 pt-3 pb-1 text-[12px] text-slate-400">
            {T.retentionHint}
            {!data.retention.enforced && <span className="text-amber-300/90"> {T.enforcedNote || data.retention.enforced_note}</span>}
          </div>
          <div className="hidden md:grid grid-cols-12 gap-3 px-4 py-2 text-[10px] uppercase tracking-wider text-slate-500 border-b border-slate-700/60">
            <div className="col-span-4">{T.dataClass}</div>
            <div className="col-span-3">{T.stored}</div>
            <div className="col-span-2">{T.keep}</div>
            <div className="col-span-2">{T.legalHold}</div>
            <div className="col-span-1"></div>
          </div>
          {data.retention.defaults.map((r) => (
            <RetentionRow key={r.data_class} row={r} T={T} isPt={isPt} canEdit={canEdit} onSave={saveRetention} />
          ))}
        </Section>

        <div className="grid grid-cols-1 xl:grid-cols-2 gap-4">
          <PrivacyPanel data={data.privacy} T={T} isPt={isPt} canEdit={canEdit} onSave={savePrivacy} />

          <Section
            title={<><Icons.Briefcase size={13} /> {T.exports}</>}
            action={<Button size="sm" variant="outline" disabled title={data.exports.reason}>{T.newExport}</Button>}
          >
            {data.exports.items.length === 0
              ? <div className="px-4 py-4 text-[12px] text-slate-500">{T.exportsEmpty}</div>
              : <div className="divide-y divide-slate-800/60">{data.exports.items.map((x) => (
                  <div key={x.id} className="px-4 py-2.5 flex items-center justify-between gap-3 font-mono text-[11.5px]">
                    <span className="text-slate-200">{x.id}</span>
                    <Pill tone={x.status === 'ready' ? 'forest' : x.status === 'failed' ? 'red' : 'slate'}>{x.status}</Pill>
                    <span className="text-slate-500 truncate">{x.sha256 || '—'}</span>
                  </div>))}
                </div>}
            <LockNote T={T} reason={(T.why && T.why.exports) || data.exports.reason} />
          </Section>
        </div>

        <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
          <ReadOnlyPanel T={T} title={T.rules} icon={Icons.Bolt} block={data.rules} k="rules" render={(r) => (
            <div className="flex items-center justify-between gap-2">
              <div className="min-w-0">
                <div className="text-[12.5px] text-slate-100 truncate">{r.name}</div>
                <div className="text-[10.5px] text-slate-500 truncate">{[r.camera, r.zone].filter(Boolean).join(' › ') || '—'}</div>
              </div>
              {r.severity_override != null && <Pill tone={r.severity_override >= 3 ? 'red' : 'slate'}>{SEV_LABEL[r.severity_override]}</Pill>}
            </div>
          )} />
          <ReadOnlyPanel T={T} title={T.channels} icon={Icons.Bell} block={data.notification_channels} k="notification_channels" render={(c) => (
            <div className="flex items-center justify-between gap-2">
              <span className="text-[12.5px] text-slate-100">{c.name}</span>
              <Pill tone="ghost">{c.kind}</Pill>
            </div>
          )} />
          <ReadOnlyPanel T={T} title={T.subscriptions} icon={Icons.UserCheck} block={data.subscriptions} k="subscriptions" render={(s) => (
            <div className="text-[12px] text-slate-200">{s.channel} <span className="text-slate-500">≥ {SEV_LABEL[s.min_severity]}</span></div>
          )} />
          <ReadOnlyPanel T={T} title={T.watchlists} icon={Icons.List} block={data.watchlists} k="watchlists" render={(w) => (
            <div className="flex items-center justify-between gap-2">
              <span className="text-[12.5px] text-slate-100">{w.name}</span>
              <span className="font-mono text-[11px] text-slate-400">{w.entries}</span>
            </div>
          )} />
        </div>

        <div className="grid grid-cols-1 xl:grid-cols-3 gap-4">
          <Section title={<><Icons.Layers size={13} /> {T.eventTypes}</>}>
            <div className="divide-y divide-slate-800/60">
              {data.event_types.items.map((e) => (
                <div key={e.kind} className="px-4 py-2 flex items-center justify-between gap-2">
                  <span className="font-mono text-[11.5px] text-slate-200">{e.kind}</span>
                  <span className="flex items-center gap-2">
                    <span className="text-[10.5px] text-slate-500">{e.producer}</span>
                    <Pill tone={e.default_severity >= 3 ? 'red' : e.default_severity >= 2 ? 'amber' : 'slate'}>{SEV_LABEL[e.default_severity]}</Pill>
                  </span>
                </div>
              ))}
            </div>
            <LockNote T={T} reason={T.eventTypesHint} />
          </Section>

          <Section className="xl:col-span-2" title={<><Icons.List size={13} /> {T.audit}</>}>
            <div className="divide-y divide-slate-800/60">
              {data.audit.items.length === 0
                ? <div className="px-4 py-3 text-[12px] text-slate-500">{T.auditEmpty}</div>
                : data.audit.items.map((a, i) => <AuditRow key={i} a={a} T={T} lang={lang} />)}
            </div>
          </Section>
        </div>
      </div>
    </div>
  );
}

window.GovernanceView = GovernanceView;
