// ─── Configuración (Empresa + Permisos admin-only + Datos) ──────────────────
// Movido a screens/configuracion/ (P10) — SIN split: SPEC.md §1 ya marca esta
// carpeta "tal cual — ya está bien" (sobre el techo ~400 — FIX P13 ítem 16,
// Review P10 BAJA-5: el número puntual de líneas quedó borrado acá a
// propósito, los conteos en comentarios siempre vencen; ver `wc -l` real —
// pero UN tema cohesivo: 3 tabs de una sola pantalla de Configuración, mismo
// criterio de excepción documentada que otros archivos del bundle, ej.
// screens/finanzas/factura.jsx). Pestaña Empresa: edita el singleton
// companyConfig (persistente). Pestaña Permisos: SOLO visible para ADMIN/JEFE
// — matriz usuario × sección con toggle, patrón "la web propone, la consola
// aplica" (POST /api/permisos-estado, tabla `permisos_estado` — NO escribe
// `usuario.permisos` directo, ver comentario de PermisosTab más abajo; el
// comentario viejo de este header decía lo contrario, corregido acá). Pestaña
// Datos: exportar/restablecer localStorage. P19 (recorrido §6): pestaña Empresa
// suma sección "Reglas de negocio" con el umbral de ganancia mínimo (%) del
// aviso de margen — mismo singleton companyConfig/empresa, ver dominio/margen.js.
//
// VERIFICACIÓN EMPÍRICA (P10, arbitraje P2 #1 — "la matriz de Permisos
// probablemente NO persiste, verificar"): el arbitraje asumía que
// PermisosTab escribía PATCH /api/usuarios (bloqueado por GRANT, confirmado
// en P2) — pero el código YA usaba `/api/permisos-estado` desde antes de la
// reestructuración (tabla dedicada, RLS propia: SELECT para habilitados,
// INSERT/UPDATE solo ADMIN vía `es_admin()`, GRANT explícito a `authenticated`
// — supabase/migrations/20260629093000_permisos_estado.sql). Confirmado
// EN VIVO (:3021, admin.demo@micossrl.com): togglear un permiso + "Guardar
// propuesta" persiste de verdad — `select propuesta from permisos_estado`
// refleja el cambio tras el POST, sobrevive a un refresh. NO hizo falta
// ninguna migración — el arbitraje P2 estaba basado en una premisa
// desactualizada (mapa-comprension.md §otros YA documentaba el patrón
// propone/aplica correcto antes de que P2 corriera).

const { useState: _useStateCfg, useEffect: _useEffectCfg, useMemo: _useMemoCfg } = React;

// Campos editables de la empresa
const _EMPRESA_FIELDS = [
  { key: 'nombre',           label: 'Razón social',         span: 2 },
  { key: 'cuit',             label: 'CUIT',                 mono: true },
  { key: 'situacionIva',     label: 'Situación IVA' },
  { key: 'ingresosBrutos',   label: 'Ingresos Brutos',      mono: true },
  { key: 'inicioActividades',label: 'Inicio de actividades', type: 'date', mono: true },
  { key: 'direccion',        label: 'Domicilio' },
  { key: 'localidad',        label: 'Localidad' },
  { key: 'telefono',         label: 'Teléfono (ERP)',       mono: true },
  { key: 'whatsapp',         label: 'WhatsApp',             mono: true },
  { key: 'email',            label: 'Email',                span: 2 },
  { key: 'emailDomain',      label: 'Dominio de email',     mono: true },
  { key: 'website',          label: 'Sitio web',            mono: true },
];

// Campo label+input a scope de módulo (no se redefine en cada render → no pierde foco)
function CfgField({ label, span, children }) {
  return (
    <div className="flex flex-col gap-1.5" style={{ gridColumn: span === 2 ? 'span 2' : 'auto' }}>
      <label className="text-[11px] font-bold uppercase tracking-[0.12em]" style={{ color: 'var(--muted-foreground)' }}>
        {label}
      </label>
      {children}
    </div>
  );
}

// Toggle estilo switch usando tokens
function CfgToggle({ on, onChange, disabled }) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={on}
      disabled={disabled}
      onClick={() => !disabled && onChange(!on)}
      className={disabled ? 'is-disabled' : 'cursor-pointer'}
      style={{
        width: 38, height: 22, borderRadius: 999, flexShrink: 0, position: 'relative',
        border: '1px solid ' + (on ? 'transparent' : 'var(--border)'),
        background: on ? 'var(--accent)' : 'var(--muted)',
        transition: 'background .15s, border-color .15s',
      }}
    >
      <span style={{
        position: 'absolute', top: 2, left: on ? 18 : 2, width: 16, height: 16, borderRadius: 999,
        background: on ? 'var(--accent-foreground)' : 'var(--muted-foreground)',
        transition: 'left .15s', boxShadow: 'var(--shadow-xs)',
      }} />
    </button>
  );
}

// ─── Pestaña Empresa ───────────────────────────────
function EmpresaTab() {
  const [form, setForm] = _useStateCfg(() => Object.assign({}, window.COMPANY_CONFIG || {}));
  const [savedAt, setSavedAt] = _useStateCfg(0);
  const set = (k, v) => setForm((f) => Object.assign({}, f, { [k]: v }));

  // P19 (recorrido §6): umbral de ganancia mínimo — no es un campo de _EMPRESA_FIELDS (no es
  // dato fiscal/identidad, es una REGLA de negocio) pero comparte el mismo singleton
  // companyConfig/empresa, así que el dirty-check y el guardar lo tratan igual.
  const dirty = _useMemoCfg(() => {
    const cur = window.COMPANY_CONFIG || {};
    const camposDirty = _EMPRESA_FIELDS.some((f) => (form[f.key] || '') !== (cur[f.key] || ''));
    const umbralDirty = Number(form.umbralGananciaPct ?? 80) !== Number(cur.umbralGananciaPct ?? 80);
    return camposDirty || umbralDirty;
  }, [form, savedAt]);

  // H2 de la consolidación del lote UX (03/07/2026): antes esto era fire-and-forget +
  // setSavedAt inmediato — si el PATCH fallaba (RLS de `empresa`: solo ADMIN puede
  // UPDATE → 400 para un JEFE), el botón decía "Guardado" igual y el usuario nunca se
  // enteraba de que su cambio se perdió. Ahora se espera el resultado REAL del PATCH
  // (setSingleton devuelve Promise<boolean>): "Guardado" SOLO con 2xx; si falla, error
  // visible acá mismo y el form conserva lo tipeado (el rollback del store ya restauró
  // COMPANY_CONFIG, así que dirty vuelve a true y el botón re-ofrece "Guardar cambios").
  const [saving, setSaving] = _useStateCfg(false);
  const [saveError, setSaveError] = _useStateCfg(null);
  const guardar = () => {
    if (saving) return;
    setSaving(true);
    setSaveError(null);
    Promise.resolve(window.MicosStore.setSingleton('companyConfig', form)).then((ok) => {
      setSaving(false);
      if (ok) {
        setSavedAt(Date.now());
      } else {
        setSaveError('No se pudo guardar. Verificá tus permisos (solo un administrador puede modificar estos datos) e intentá de nuevo.');
      }
    });
  };
  const descartar = () => { setSaveError(null); setForm(Object.assign({}, window.COMPANY_CONFIG || {})); };

  return (
    <div className="rounded-2xl bg-card p-6 sm:p-7" style={{ border: '1px solid var(--border)', boxShadow: 'var(--shadow-card)' }}>
      <div className="flex items-start justify-between gap-4 mb-6">
        <div>
          <p className="text-[11px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--accent)' }}>Configuración · Empresa</p>
          <h2 className="text-[19px] font-extrabold mt-1" style={{ color: 'var(--foreground)' }}>Datos de la empresa</h2>
          <p className="text-[12.5px] mt-1" style={{ color: 'var(--muted-foreground)' }}>
            Estos datos aparecen en presupuestos, órdenes de compra, facturas y demás documentos.
          </p>
        </div>
      </div>

      <div className="grid grid-cols-2 gap-x-5 gap-y-4">
        {/* P17a (recorrido §9): FIX — `f.mono` aplicaba `fontFamily:
            'var(--font-mono, monospace)'`, una variable CSS que no existe en
            styles.css (grep sin resultados); caía siempre al fallback
            genérico `monospace` del navegador, nunca a JetBrains Mono. Afectaba
            a TODOS los campos mono de este tab (CUIT incluido, no solo IIBB).
            Se reemplaza por la clase real `.font-mono` (mismo patrón que
            record-form.jsx/party-selector.jsx) — CUIT e IIBB quedan
            consistentes de verdad, no solo "igual de rotos". */}
        {_EMPRESA_FIELDS.map((f) => (
          <CfgField key={f.key} label={f.label} span={f.span}>
            <input
              className={`input${f.mono ? ' font-mono tabular-nums' : ''}`}
              type={f.type || 'text'}
              value={form[f.key] || ''}
              onChange={(e) => set(f.key, e.target.value)}
            />
          </CfgField>
        ))}
      </div>

      {/* P19 (recorrido §6): umbral de margen — separado visualmente de los datos fiscales
          de arriba porque es una regla de negocio, no un dato de identidad de la empresa. */}
      <div className="mt-7 pt-6" style={{ borderTop: '1px solid var(--border)' }}>
        <p className="text-[11px] font-bold uppercase tracking-[0.18em] mb-3" style={{ color: 'var(--accent)' }}>
          Reglas de negocio
        </p>
        <div className="grid grid-cols-2 gap-x-5 gap-y-4">
          <CfgField label="Umbral de ganancia mínimo (%)">
            <input
              className="input"
              type="number"
              min="0"
              step="1"
              value={form.umbralGananciaPct ?? ''}
              onChange={(e) => set('umbralGananciaPct', e.target.value === '' ? null : Number(e.target.value))}
              style={{ fontFamily: 'var(--font-mono, monospace)' }}
            />
          </CfgField>
        </div>
        <p className="text-[12px] mt-2.5" style={{ color: 'var(--muted-foreground)' }}>
          Si un presupuesto se guarda con 1 o más ítems con ganancia por debajo de este umbral,
          se crea una notificación de margen (Notificaciones de margen) — no bloquea guardar.
        </p>
      </div>

      <div className="flex items-center justify-end gap-2.5 mt-7">
        {saveError && (
          <p className="text-[12.5px] font-bold mr-auto" role="alert" style={{ color: 'var(--destructive)' }}>
            {saveError}
          </p>
        )}
        <button className="btn btn-outline" onClick={descartar} aria-disabled={!dirty || saving} disabled={!dirty || saving}>Descartar</button>
        <button className="btn btn-primary" onClick={guardar} aria-disabled={!dirty || saving} disabled={!dirty || saving}>
          {saving ? 'Guardando…' : (dirty ? 'Guardar cambios' : 'Guardado')}
        </button>
      </div>
    </div>
  );
}

// ─── Pestaña Permisos (admin propone · jefe ve) ─────
// Aparato "la web propone, la consola aplica" — DOS JSON en el backend
// (tabla permisos_estado, GET/POST /api/permisos-estado):
//   - propuesta: lo que el ADMIN deja propuesto con los toggles (el "después").
//   - backup: foto del estado real al momento de Guardar (el "antes").
// Los toggles acumulan local; al GUARDAR, UNA sola petición escribe los dos.
// NO toca usuario.permisos: lo aplica Fran+Lin por consola comparando los dos.
//   - JEFE: solo lectura. USUARIO: ni ve la pestaña (gate en ConfiguracionScreen).
function PermisosTab({ readOnly }) {
  const usuarios = window.USUARIOS || [];
  const secciones = window.MICOS_SECCIONES || [];
  const [loading, setLoading] = _useStateCfg(true);
  const [guardada, setGuardada] = _useStateCfg({}); // propuesta persistida en backend
  const [local, setLocal] = _useStateCfg({});       // propuesta con toggles sin guardar
  const [saving, setSaving] = _useStateCfg(false);

  // Cargar la propuesta guardada del backend al montar.
  _useEffectCfg(() => {
    let cancelled = false;
    fetch('/api/permisos-estado', { credentials: 'same-origin' })
      .then((r) => r.ok ? r.json() : { propuesta: {} })
      .then((d) => { if (!cancelled) { setGuardada(d.propuesta || {}); setLocal(d.propuesta || {}); setLoading(false); } })
      .catch(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, []);

  // valor REAL (deny-by-default) y valor EFECTIVO (real + lo propuesto en local)
  const realVal = (u, href) => u.rol === 'ADMIN' ? true : !!(u.permisos && u.permisos[href] === true);
  const propVal = (u, href) => {
    const pu = local[u.id];
    return (pu && href in pu) ? pu[href] : realVal(u, href);
  };
  const esPropuesto = (u, href) => {
    const pu = local[u.id];
    return !!(pu && href in pu && pu[href] !== realVal(u, href));
  };

  // togglear (solo admin, nunca sobre una fila admin): edita `local`; si el valor
  // vuelve al real, lo saca (no queda propuesta espuria).
  const toggle = (u, href, val) => {
    if (readOnly || u.rol === 'ADMIN') return;
    const next = Object.assign({}, local);
    const pu = Object.assign({}, next[u.id] || {});
    if (val === realVal(u, href)) delete pu[href]; else pu[href] = val;
    if (Object.keys(pu).length === 0) delete next[u.id]; else next[u.id] = pu;
    setLocal(next);
  };

  // resumen legible: por usuario, qué gana (+) y qué pierde (−)
  const cambios = [];
  Object.keys(local).forEach((uid) => {
    const u = usuarios.find((x) => x.id === uid);
    if (!u) return;
    const pu = local[uid], adds = [], rems = [];
    Object.keys(pu).forEach((href) => {
      const lbl = (secciones.find((s) => s.href === href) || {}).label || href;
      if (pu[href] && !realVal(u, href)) adds.push(lbl);
      else if (!pu[href] && realVal(u, href)) rems.push(lbl);
    });
    if (adds.length || rems.length) cambios.push({ u, adds, rems });
  });

  const dirty = JSON.stringify(local) !== JSON.stringify(guardada);

  // backup = foto del estado real ("antes") de lo que la propuesta toca.
  const snapshotBackup = (prop) => {
    const bk = {};
    Object.keys(prop).forEach((uid) => {
      const u = usuarios.find((x) => x.id === uid);
      if (!u) return;
      bk[uid] = {};
      Object.keys(prop[uid]).forEach((href) => { bk[uid][href] = realVal(u, href); });
    });
    return bk;
  };

  const guardar = () => {
    if (saving || !dirty) return;
    setSaving(true);
    fetch('/api/permisos-estado', {
      method: 'POST', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ propuesta: local, backup: snapshotBackup(local) }),
    })
      .then((r) => r.ok ? r.json() : Promise.reject(r))
      .then(() => { setGuardada(local); setSaving(false); })
      .catch(() => { setSaving(false); window.alert('No se pudo guardar la propuesta.'); });
  };
  const cancelar = () => setLocal(guardada); // descarta los cambios sin guardar

  return (
    <div className="space-y-5">
      <div className="rounded-2xl bg-card overflow-hidden" style={{ border: '1px solid var(--border)', boxShadow: 'var(--shadow-card)' }}>
        <div className="p-6 sm:p-7" style={{ borderBottom: '1px solid var(--border)' }}>
          <p className="text-[11px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--accent)' }}>Configuración · Permisos</p>
          <h2 className="text-[19px] font-extrabold mt-1" style={{ color: 'var(--foreground)' }}>Acceso por sección</h2>
          <p className="text-[12.5px] mt-1" style={{ color: 'var(--muted-foreground)' }}>
            {readOnly
              ? 'Vista de solo lectura: quién accede a qué sección. Los cambios los propone un administrador y se aplican por consola.'
              : 'Cada cambio queda como una propuesta — no se aplica solo. Guardás la propuesta y se revisa y ejecuta por consola (con respaldo). El Panel general y Configuración siempre se ven.'}
          </p>
        </div>

        <div className="overflow-x-auto">
          <table className="w-full" style={{ borderCollapse: 'collapse', minWidth: 720 }}>
            <thead>
              <tr style={{ borderBottom: '1px solid var(--border)' }}>
                <th className="text-left px-5 py-3 sticky left-0 z-10" style={{ background: 'var(--card)', minWidth: 200 }}>
                  <span className="text-[10.5px] font-bold uppercase tracking-[0.14em]" style={{ color: 'var(--muted-foreground)' }}>Usuario</span>
                </th>
                {secciones.map((s) => (
                  <th key={s.href} className="px-2 py-3 text-center" style={{ minWidth: 84 }}>
                    <span className="text-[10.5px] font-bold" style={{ color: 'var(--muted-foreground)' }}>{s.label}</span>
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {usuarios.map((u) => {
                const isAdminRow = u.rol === 'ADMIN';
                const tone = _rolTone(u.rol);
                return (
                  <tr key={u.id} style={{ borderBottom: '1px solid color-mix(in oklab, var(--border) 55%, transparent)' }}>
                    <td className="px-5 py-3 sticky left-0 z-10" style={{ background: 'var(--card)' }}>
                      <div className="flex items-center gap-3">
                        <div className="h-8 w-8 rounded-lg flex items-center justify-center text-[11px] font-extrabold flex-shrink-0"
                          style={{ background: tone.bg, color: tone.fg }}>
                          {_inicialesUsuario(u)}
                        </div>
                        <div className="min-w-0">
                          <p className="text-[13px] font-bold truncate" style={{ color: 'var(--foreground)' }}>{u.nombre} {u.apellido}</p>
                          <p className="text-[10.5px] font-bold uppercase tracking-[0.1em]" style={{ color: tone.fg }}>{u.rol}</p>
                        </div>
                      </div>
                    </td>
                    {secciones.map((s) => {
                      const propuesto = esPropuesto(u, s.href);
                      return (
                        <td key={s.href} className="px-2 py-3">
                          <div className="flex justify-center">
                            <div className="relative rounded-full" style={propuesto
                              ? { padding: 3, background: 'color-mix(in oklab, var(--accent) 16%, transparent)', boxShadow: '0 0 0 1px var(--accent)' }
                              : { padding: 3 }}>
                              <CfgToggle
                                on={propVal(u, s.href)}
                                disabled={readOnly || isAdminRow}
                                onChange={(val) => toggle(u, s.href, val)}
                              />
                            </div>
                          </div>
                        </td>
                      );
                    })}
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {/* Panel de propuesta — solo admin, solo si hay cambios respecto al estado real */}
      {!readOnly && cambios.length > 0 && (
        <div className="rounded-2xl bg-card p-6 sm:p-7" style={{ border: '1px solid color-mix(in oklab, var(--accent) 40%, var(--border))', boxShadow: 'var(--shadow-card)' }}>
          <div className="flex items-start justify-between gap-4 flex-wrap">
            <div>
              <p className="text-[11px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--accent)' }}>Propuesta pendiente</p>
              <h3 className="text-[16px] font-extrabold mt-1" style={{ color: 'var(--foreground)' }}>
                Cambios propuestos ({cambios.length}){dirty ? ' · sin guardar' : ' · guardada'}
              </h3>
              <p className="text-[12px] mt-1" style={{ color: 'var(--muted-foreground)' }}>
                No están aplicados. Guardá la propuesta y pasámela para revisarla (comparo contra el respaldo) y ejecutarla por consola.
              </p>
            </div>
            <div className="flex items-center gap-2.5">
              <button className="btn btn-outline" onClick={cancelar} disabled={!dirty} aria-disabled={!dirty}>Cancelar</button>
              <button className="btn btn-primary" onClick={guardar} disabled={!dirty || saving} aria-disabled={!dirty || saving}>
                {saving ? 'Guardando…' : (dirty ? 'Guardar propuesta' : 'Guardada')}
              </button>
            </div>
          </div>

          <div className="mt-5 space-y-2.5">
            {cambios.map((c) => (
              <div key={c.u.id} className="flex items-center gap-3 rounded-xl px-3.5 py-2.5" style={{ background: 'var(--surface)', border: '1px solid color-mix(in oklab, var(--border) 60%, transparent)' }}>
                <div className="h-7 w-7 rounded-lg flex items-center justify-center text-[10.5px] font-extrabold flex-shrink-0"
                  style={{ background: _rolTone(c.u.rol).bg, color: _rolTone(c.u.rol).fg }}>
                  {_inicialesUsuario(c.u)}
                </div>
                <span className="text-[13px] font-bold flex-shrink-0" style={{ color: 'var(--foreground)' }}>{c.u.nombre} {c.u.apellido}</span>
                <div className="flex items-center gap-1.5 flex-wrap">
                  {c.adds.map((l) => (
                    <span key={'a' + l} className="text-[11px] font-bold px-2 py-0.5 rounded-md" style={{ background: 'color-mix(in oklab, var(--accent) 14%, transparent)', color: 'var(--accent)' }}>+ {l}</span>
                  ))}
                  {c.rems.map((l) => (
                    <span key={'r' + l} className="text-[11px] font-bold px-2 py-0.5 rounded-md" style={{ background: 'color-mix(in oklab, var(--destructive) 12%, transparent)', color: 'var(--destructive)' }}>− {l}</span>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// Iniciales del avatar (B11): `u.iniciales` lo calcula `_augmentUsuarios` SOLO sobre
// la semilla de data-usuarios.js. Tras `MicosStore.hydrate()` las filas se reemplazan
// por las reales de `/api/usuarios` (columnas crudas de la tabla `usuarios`, sin ese
// campo derivado) → quedaban sin iniciales = avatar vacío. Deriva on-the-fly con el
// mismo criterio si falta.
function _inicialesUsuario(u) {
  if (u.iniciales) return u.iniciales;
  return (((u.nombre || '')[0] || '') + ((u.apellido || '')[0] || '')).toUpperCase();
}

// Tono del avatar por rol (ADMIN/JEFE/USUARIO). A scope de módulo (reusado por el panel).
function _rolTone(rol) {
  if (rol === 'ADMIN') return { bg: 'color-mix(in oklab, var(--accent) 14%, transparent)', fg: 'var(--accent)' };
  if (rol === 'JEFE')  return { bg: 'color-mix(in oklab, var(--foreground) 8%, transparent)', fg: 'var(--foreground)' };
  return { bg: 'var(--muted)', fg: 'var(--muted-foreground)' };
}

// ─── Pestaña Datos (respaldo) ──────────────────────
function DatosTab() {
  const exportar = () => {
    try {
      const json = window.MicosStore.exportJSON();
      const blob = new Blob([json], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'micos-datos-' + new Date().toISOString().slice(0, 10) + '.json';
      document.body.appendChild(a); a.click(); a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    } catch (_) {}
  };
  const restablecer = () => {
    if (!window.confirm('¿Restablecer todos los datos a los de fábrica? Se pierden los cambios y registros que hayas creado.')) return;
    window.MicosStore.resetFactory();
    location.reload();
  };
  return (
    <div className="rounded-2xl bg-card p-6 sm:p-7" style={{ border: '1px solid var(--border)', boxShadow: 'var(--shadow-card)' }}>
      <p className="text-[11px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--accent)' }}>Configuración · Datos</p>
      <h2 className="text-[19px] font-extrabold mt-1" style={{ color: 'var(--foreground)' }}>Datos y respaldo</h2>
      <p className="text-[12.5px] mt-1 mb-6" style={{ color: 'var(--muted-foreground)' }}>
        Respaldo local de todo lo cargado (JSON). El almacenamiento es por navegador.
      </p>
      <div className="flex flex-col sm:flex-row gap-3">
        <button className="btn btn-outline" onClick={exportar}>
          <IconDownload size={15} /> Exportar datos (JSON)
        </button>
        <button className="btn btn-outline" onClick={restablecer}
          style={{ color: 'var(--destructive)', borderColor: 'color-mix(in oklab, var(--destructive) 40%, var(--border))' }}>
          <IconTrash size={15} /> Restablecer a fábrica
        </button>
      </div>
    </div>
  );
}

// ─── Pantalla ──────────────────────────────────────
function ConfiguracionScreen() {
  const rol = (window.MicosCurrentUser && window.MicosCurrentUser().rol) || '';
  const isAdmin = rol === 'ADMIN';
  const isJefe = rol === 'JEFE';
  const canSeePermisos = isAdmin || isJefe; // jefe lo ve read-only; admin propone
  const [tab, setTab] = _useStateCfg('empresa');

  const tabs = [{ key: 'empresa', label: 'Empresa' }];
  if (canSeePermisos) tabs.push({ key: 'permisos', label: 'Permisos' });
  tabs.push({ key: 'datos', label: 'Datos' });

  return (
    <div className="space-y-6 pt-2 max-w-[1100px]">
      <div className="animate-fade-in-up">
        <p className="text-[11px] font-bold uppercase tracking-[0.18em]" style={{ color: 'var(--muted-foreground)' }}>Herramientas · Configuración</p>
        <h1 className="text-[clamp(26px,3.4vw,34px)] leading-[1.05] font-extrabold tracking-[-0.02em] mt-1" style={{ color: 'var(--foreground)' }}>
          Configuración
        </h1>
      </div>

      {/* Tabs */}
      <div className="flex items-center gap-1.5">
        {tabs.map((t) => {
          const active = tab === t.key;
          return (
            <button
              key={t.key}
              onClick={() => setTab(t.key)}
              className="px-4 py-2 rounded-xl text-[13px] font-bold cursor-pointer transition-colors"
              style={{
                background: active ? 'color-mix(in oklab, var(--accent) 12%, transparent)' : 'transparent',
                color: active ? 'var(--accent)' : 'var(--muted-foreground)',
                border: '1px solid ' + (active ? 'color-mix(in oklab, var(--accent) 30%, transparent)' : 'var(--border)'),
              }}
            >
              {t.label}
            </button>
          );
        })}
      </div>

      {tab === 'empresa' && <EmpresaTab />}
      {tab === 'permisos' && canSeePermisos && <PermisosTab readOnly={isJefe} />}
      {tab === 'datos' && <DatosTab />}
    </div>
  );
}

Object.assign(window, { ConfiguracionScreen });
