// LOLA Consola Web — Módulo de SEGURIDAD: equipo interno de la consola (usuarios con
// rol='admin'), roles de seguridad y su matriz de permisos por módulo/acción. TODO real
// contra MongoDB (GET/POST/PATCH/DELETE /admin/equipo y /admin/roles) — nada en memoria del
// navegador. Sobrescribe window.SeguridadView (se carga después de views.jsx).
//
// Alcance honesto (actualizado 21-jul-2026, verificado línea por línea contra el backend):
// los permisos se guardan Y se hacen cumplir de verdad — cada ruta /admin/* pasa por
// requirePermiso(modulo, accion) o requireSuperAdmin (ver middleware/permisos.js). La única
// acción de la matriz que hoy NO se aplica como guardia en ningún endpoint es "consulta"
// (existe en la UI/backend pero ninguna ruta la exige) — el resto (lectura/escritura/
// actualizacion/eliminacion) sí está cableado 1:1 con lo que muestra esta pantalla.

const api = () => window.LOLA.api;

// ── Íconos locales para acciones ──
const _Ico = (p, path) => React.createElement('svg', { width: p.size || 16, height: p.size || 16, viewBox: '0 0 24 24',
  fill: 'none', stroke: 'currentColor', strokeWidth: p.sw || 1.9, strokeLinecap: 'round', strokeLinejoin: 'round' }, path);
const IEye = (p) => _Ico(p, [React.createElement('path', { key: 1, d: 'M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z' }), React.createElement('circle', { key: 2, cx: 12, cy: 12, r: 3 })]);
const IPlus = (p) => _Ico(p, React.createElement('path', { d: 'M12 5v14M5 12h14' }));
const IPencil = (p) => _Ico(p, React.createElement('path', { d: 'M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z' }));
const ITrash = (p) => _Ico(p, [React.createElement('path', { key: 1, d: 'M3 6h18M8 6V4h8v2M6 6l1 14h10l1-14' })]);
const IDownload = (p) => _Ico(p, [React.createElement('path', { key: 1, d: 'M12 3v12' }), React.createElement('path', { key: 2, d: 'm7 12 5 5 5-5' }), React.createElement('path', { key: 3, d: 'M5 21h14' })]);
const ICheck = (p) => _Ico(Object.assign({ sw: 3 }, p), React.createElement('path', { d: 'M20 6 9 17l-5-5' }));
const IX = (p) => _Ico(Object.assign({ sw: 2.4 }, p), React.createElement('path', { d: 'M18 6 6 18M6 6l12 12' }));
const ICopy = (p) => _Ico(p, [React.createElement('rect', { key: 1, x: 9, y: 9, width: 13, height: 13, rx: 2 }), React.createElement('path', { key: 2, d: 'M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1' })]);

// ── Modelo (debe coincidir con MODULOS/ACCIONES de equipo.controller.js) ──
const MODULES = [
  { id: 'aprobaciones', label: 'Aprobaciones', desc: 'Contratos de clientes y proveedores', icon: 'Check', color: '#1D57A9' },
  { id: 'usuarios', label: 'Usuarios', desc: 'Cuentas, roles y permisos', icon: 'Users', color: '#2E6EC4' },
  { id: 'comisiones', label: 'Comisiones', desc: 'Tasas e ingresos de la plataforma', icon: 'Wallet', color: '#1F8A5B' },
  { id: 'publicidad', label: 'Publicidad', desc: 'Campañas y anuncios en redes', icon: 'Megaphone', color: '#7A5AF8' },
  { id: 'pagos', label: 'Pagos', desc: 'Transacciones y liquidaciones', icon: 'Tag', color: '#0E9CA6' },
  { id: 'reportes', label: 'Reportes', desc: 'Analítica y exportaciones', icon: 'Trend', color: '#D98A0B' },
  { id: 'bitacora', label: 'Bitácora', desc: 'Auditoría del sistema', icon: 'Book', color: '#5B6470' },
  { id: 'config', label: 'Configuración', desc: 'Ajustes, seguridad y equipo', icon: 'Gear', color: '#C0392B' },
  { id: 'catalogos', label: 'Catálogos', desc: 'Tipos de transporte y de carga', icon: 'Truck', color: '#B45309' },
];
const ACTIONS = [
  { id: 'lectura', label: 'Lectura', sub: 'Ver', Icn: IEye },
  { id: 'escritura', label: 'Escritura', sub: 'Crear', Icn: IPlus },
  { id: 'actualizacion', label: 'Actualización', sub: 'Actualizar', Icn: IPencil },
  { id: 'eliminacion', label: 'Eliminación', sub: 'Eliminar', Icn: ITrash },
  { id: 'consulta', label: 'Consulta', sub: 'Exportar', Icn: IDownload },
];
const TOTAL_PERMS = MODULES.length * ACTIONS.length;
const AV = ['#1D57A9', '#1F8A5B', '#7A5AF8', '#0E9CA6', '#D98A0B', '#C0392B', '#2E6EC4'];
const iniciales = (n) => (n || '?').split(' ').filter(Boolean).slice(0, 2).map((w) => w[0]).join('').toUpperCase();
const arroba = (email) => '@' + String(email || '').split('@')[0];
const esRolAdmin = (slugOrRol) => !slugOrRol || slugOrRol === 'admin';

function emptyPerms(val) {
  const p = {};
  MODULES.forEach((m) => { p[m.id] = {}; ACTIONS.forEach((a) => { p[m.id][a.id] = !!val; }); });
  return p;
}
function normPerms(perms) {
  // Rellena huecos (por si el servidor manda un objeto parcial) sin perder lo que sí venga.
  const p = emptyPerms(false);
  MODULES.forEach((m) => { ACTIONS.forEach((a) => { if (perms && perms[m.id] && perms[m.id][a.id]) p[m.id][a.id] = true; }); });
  return p;
}
function statsOf(perms) {
  let granted = 0, full = 0, del = 0;
  MODULES.forEach((m) => {
    let c = 0;
    ACTIONS.forEach((a) => { if (perms[m.id] && perms[m.id][a.id]) c++; });
    granted += c; if (c === ACTIONS.length) full++;
    if (perms[m.id] && perms[m.id].eliminacion) del++;
  });
  return { granted, full, del, pct: Math.round(granted / TOTAL_PERMS * 100) };
}
function nivel(pct) {
  if (pct >= 100) return ['Total', '#1F8A5B'];
  if (pct >= 70) return ['Alto', '#1D57A9'];
  if (pct >= 40) return ['Medio', '#1D57A9'];
  if (pct >= 15) return ['Mínimo', '#D98A0B'];
  if (pct > 0) return ['Bajo', '#D98A0B'];
  return ['Ninguno', '#8A929E'];
}

// ── Ring de porcentaje ──
function Ring({ pct, size = 66, color, sw = 6 }) {
  const t = window.WC;
  const r = (size - sw) / 2, c = 2 * Math.PI * r;
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ flexShrink: 0 }}>
      <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke={t.grid} strokeWidth={sw}/>
      <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke={color} strokeWidth={sw} strokeLinecap="round"
        strokeDasharray={c} strokeDashoffset={c * (1 - pct / 100)} transform={`rotate(-90 ${size / 2} ${size / 2})`} style={{ transition: 'stroke-dashoffset .5s' }}/>
      <text x="50%" y="50%" textAnchor="middle" dominantBaseline="central" fontFamily={t.mono} fontWeight="800" fontSize={size * 0.26} fill={t.text}>{pct}%</text>
    </svg>
  );
}

// ── Matriz de permisos (compartida por usuarios y roles) ──
function PermMatrix({ perms, onToggle, readOnly }) {
  const t = window.WC, I = window.WI;
  const cols = `minmax(190px, 1.5fr) repeat(${ACTIONS.length}, minmax(64px, 1fr))`;
  return (
    <div style={{ border: `1px solid ${t.border}`, borderRadius: 14, overflow: 'hidden', overflowX: 'auto' }}>
      <div style={{ minWidth: 620 }}>
        <div style={{ display: 'grid', gridTemplateColumns: cols, background: '#FAFBFC', borderBottom: `1px solid ${t.border}` }}>
          <div style={{ padding: '13px 18px', fontFamily: t.mono, fontSize: 10.5, letterSpacing: 1, textTransform: 'uppercase', color: t.ter, fontWeight: 700 }}>Módulo</div>
          {ACTIONS.map((a) => (
            <div key={a.id} style={{ padding: '11px 6px', textAlign: 'center', background: a.id === 'eliminacion' ? '#FDF3F2' : 'transparent' }}>
              <div style={{ color: a.id === 'eliminacion' ? t.danger : t.sec, display: 'flex', justifyContent: 'center', marginBottom: 4 }}><a.Icn size={16}/></div>
              <div style={{ fontSize: 11.5, fontWeight: 700, color: a.id === 'eliminacion' ? t.danger : t.text }}>{a.label}</div>
              <div style={{ fontSize: 10, color: t.ter, fontFamily: t.mono }}>{a.sub}</div>
            </div>
          ))}
        </div>
        {MODULES.map((m, i) => {
          const Icn = I[m.icon] || I.Grid;
          return (
            <div key={m.id} style={{ display: 'grid', gridTemplateColumns: cols, borderBottom: i < MODULES.length - 1 ? `1px solid ${t.grid}` : 'none', alignItems: 'center' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '12px 18px' }}>
                <span style={{ width: 34, height: 34, borderRadius: 9, background: m.color + '16', color: m.color, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><Icn size={17}/></span>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 700, color: t.text }}>{m.label}</div>
                  <div style={{ fontSize: 11.5, color: t.ter, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{m.desc}</div>
                </div>
              </div>
              {ACTIONS.map((a) => {
                const on = !!(perms[m.id] && perms[m.id][a.id]);
                const del = a.id === 'eliminacion';
                return (
                  <div key={a.id} style={{ display: 'flex', justifyContent: 'center', padding: '10px 6px', background: del ? '#FDF3F2' : 'transparent', height: '100%', alignItems: 'center' }}>
                    <button disabled={readOnly} onClick={() => onToggle && onToggle(m.id, a.id)} title={a.label}
                      style={{ width: 30, height: 30, borderRadius: 8, cursor: readOnly ? 'default' : 'pointer',
                        border: `1.5px solid ${on ? (del ? t.danger : t.brand) : t.border}`,
                        background: on ? (del ? t.danger : t.brand) : '#fff', color: on ? '#fff' : '#C6CCD4',
                        display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: readOnly ? .92 : 1 }}>
                      {on ? <ICheck size={15}/> : <IX size={14}/>}
                    </button>
                  </div>
                );
              })}
            </div>
          );
        })}
      </div>
    </div>
  );
}

function AccessCard({ perms, actions }) {
  const t = window.WC;
  const s = statsOf(perms);
  const [nvl, col] = nivel(s.pct);
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 18, padding: '16px 18px', border: `1px solid ${t.border}`, borderRadius: 14, flexWrap: 'wrap' }}>
      <Ring pct={s.pct} color={col}/>
      <div style={{ flex: 1, minWidth: 180 }}>
        <div style={{ fontSize: 15, fontWeight: 800, color: t.text }}>Nivel de acceso: <span style={{ color: col }}>{nvl}</span></div>
        <div style={{ display: 'flex', gap: 16, marginTop: 6, flexWrap: 'wrap', fontSize: 12.5, color: t.sec }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}><ICheck size={13}/> {s.granted} de {TOTAL_PERMS} permisos</span>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}><window.WI.Grid size={13}/> {s.full} módulos completos</span>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, color: s.del ? t.danger : t.ter }}><ITrash size={13}/> {s.del} con eliminación</span>
        </div>
      </div>
      {actions}
    </div>
  );
}

function SmallBtn({ t, children, onClick, tone, disabled }) {
  const c = tone === 'danger' ? t.danger : t.brand;
  return (
    <button onClick={onClick} disabled={disabled} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, cursor: disabled ? 'default' : 'pointer', padding: '8px 13px', borderRadius: 9,
      border: `1px solid ${t.border}`, background: '#fff', color: c, fontFamily: t.font, fontSize: 12.5, fontWeight: 700, opacity: disabled ? .5 : 1 }}>{children}</button>
  );
}
function BigBtn({ t, children, onClick, kind, disabled }) {
  const primary = kind !== 'ghost';
  return (
    <button onClick={onClick} disabled={disabled} style={{ display: 'inline-flex', alignItems: 'center', gap: 8, cursor: disabled ? 'default' : 'pointer',
      padding: '11px 17px', borderRadius: 11, border: primary ? 'none' : `1px solid ${t.border}`, background: primary ? t.brand : '#fff',
      color: primary ? '#fff' : t.text, fontFamily: t.font, fontSize: 14, fontWeight: 700, opacity: disabled ? .5 : 1,
      boxShadow: primary ? '0 10px 22px -12px rgba(29,87,169,.8)' : 'none' }}>{children}</button>
  );
}
function SaveBar({ t, dirty, saved, busy, onSave, onDiscard }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '12px 16px', borderRadius: 12,
      background: saved ? t.success + '12' : '#FAFBFC', border: `1px solid ${saved ? t.success + '55' : t.border}` }}>
      <span style={{ width: 9, height: 9, borderRadius: '50%', background: saved ? t.success : dirty ? t.accent : t.ter, flexShrink: 0 }}/>
      <span style={{ flex: 1, fontSize: 13.5, fontWeight: 600, color: saved ? t.success : t.sec }}>
        {saved ? 'Permisos guardados' : dirty ? 'Tienes cambios sin guardar' : 'Los cambios se aplican al guardar'}</span>
      {dirty && !saved && onDiscard && <SmallBtn t={t} onClick={onDiscard}>Descartar</SmallBtn>}
      <BigBtn t={t} onClick={onSave} disabled={(!dirty && !saved) || busy}>{saved ? 'Guardado' : busy ? 'Guardando…' : 'Guardar cambios'}</BigBtn>
    </div>
  );
}

// ── Modal: contraseña temporal tras crear un miembro del equipo ──
function TempPasswordModal({ t, email, tempPassword, onClose }) {
  const [copiado, setCopiado] = React.useState(false);
  const copiar = () => {
    try { navigator.clipboard.writeText(tempPassword); setCopiado(true); setTimeout(() => setCopiado(false), 1600); } catch (e) {}
  };
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 95, background: 'rgba(10,20,32,.6)', backdropFilter: 'blur(3px)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 22 }}>
      <div onClick={(e) => e.stopPropagation()} style={{ background: '#fff', borderRadius: 18, width: '100%', maxWidth: 420, padding: 26, boxShadow: '0 40px 100px -30px rgba(0,0,0,.6)' }}>
        <div style={{ width: 44, height: 44, borderRadius: 12, background: t.success + '14', color: t.success, display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 14 }}><ICheck size={22}/></div>
        <div style={{ fontSize: 18, fontWeight: 800, color: t.text }}>Cuenta creada</div>
        <div style={{ fontSize: 13.5, color: t.ter, marginTop: 6, marginBottom: 16, lineHeight: 1.5 }}>
          Compártele esta contraseña temporal a <b style={{ color: t.text }}>{email}</b>. No se volverá a mostrar — pídele que la cambie en cuanto entre.
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, background: '#FAFBFC', border: `1.5px dashed ${t.border}`, borderRadius: 12, padding: '13px 16px' }}>
          <span style={{ flex: 1, fontFamily: t.mono, fontSize: 16, fontWeight: 700, color: t.text, letterSpacing: .5 }}>{tempPassword}</span>
          <SmallBtn t={t} onClick={copiar}><ICopy size={14}/> {copiado ? 'Copiado' : 'Copiar'}</SmallBtn>
        </div>
        <div style={{ marginTop: 20, display: 'flex', justifyContent: 'flex-end' }}><BigBtn t={t} onClick={onClose}>Entendido</BigBtn></div>
      </div>
    </div>
  );
}

// ═══════════════════════ VISTA PRINCIPAL ═══════════════════════
function SeguridadView() {
  const t = window.WC, I = window.WI;
  const isMobile = window.useIsMobile(920);
  const { data: eqData, loading: loadEq, error: errEq, reload: reloadEq } = window.useAsync(() => api().equipo(), []);
  const { data: rolData, loading: loadR, error: errR, reload: reloadR } = window.useAsync(() => api().roles(), []);

  const [modo, setModo] = React.useState('usuarios');
  const [selU, setSelU] = React.useState(null);
  const [selR, setSelR] = React.useState(null);
  const [tab, setTab] = React.useState('permisos');
  const [modal, setModal] = React.useState(null);
  const [tempInfo, setTempInfo] = React.useState(null);
  const [busyAction, setBusyAction] = React.useState(false);

  if (loadEq || loadR) return <window.Loading label="Cargando equipo y roles…"/>;
  if (errEq || errR) return <window.ErrState error={errEq || errR} onRetry={() => { reloadEq(); reloadR(); }}/>;

  const users = (eqData && eqData.items) || [];
  const roles = (rolData && rolData.items) || [];
  const roleBySlug = (slug) => roles.find((r) => r.id === slug || r.slug === slug) || { nombre: 'Administrador', sistema: true, permisos: emptyPerms(true) };
  const usersOf = (slug) => users.filter((u) => (u.rolAdmin || 'admin') === slug).length;

  const user = users.find((u) => u.id === selU) || users[0];
  const role = roles.find((r) => r.id === selR) || roles[0];

  // ── acciones sobre usuarios ──
  const crearUser = async (data) => {
    setBusyAction(true);
    try {
      const r = await api().crearEquipo({ nombre: data.nombre, email: data.email, rolAdmin: data.rol });
      await reloadEq(); setSelU(r.usuario.id); setModal(null);
      setTempInfo({ email: r.usuario.email, tempPassword: r.tempPassword });
    } catch (e) { alert(e.message || 'No se pudo crear la cuenta'); }
    setBusyAction(false);
  };
  const editarUser = async (data) => {
    setBusyAction(true);
    try { await api().actualizarEquipo(data.id, { nombre: data.nombre, email: data.email }); await reloadEq(); setModal(null); }
    catch (e) { alert(e.message || 'No se pudo guardar'); }
    setBusyAction(false);
  };
  const borrarUser = async (u) => {
    if (esRolAdmin(u.rolAdmin)) { alert('No puedes eliminar al Administrador.'); return; }
    if (!confirm('¿Eliminar a ' + u.nombre + '?')) return;
    setBusyAction(true);
    try { await api().borrarEquipo(u.id); setSelU(null); await reloadEq(); }
    catch (e) { alert(e.message || 'No se pudo eliminar'); }
    setBusyAction(false);
  };
  const cambiarRolU = async (rid) => {
    setBusyAction(true);
    try { await api().actualizarEquipo(user.id, { rolAdmin: rid }); await reloadEq(); }
    catch (e) { alert(e.message || 'No se pudo cambiar el rol'); }
    setBusyAction(false);
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, justifyContent: 'space-between', flexWrap: 'wrap' }}>
        <div style={{ display: 'inline-flex', gap: 3, background: t.grid, borderRadius: 11, padding: 3 }}>
          {[['usuarios', 'Usuarios'], ['roles', 'Roles']].map(([id, l]) => (
            <button key={id} onClick={() => setModo(id)} style={{ cursor: 'pointer', padding: '9px 18px', borderRadius: 9, border: 'none',
              background: modo === id ? '#fff' : 'transparent', color: modo === id ? t.brand : t.sec, fontFamily: t.font, fontSize: 13.5, fontWeight: modo === id ? 700 : 600,
              boxShadow: modo === id ? '0 1px 3px rgba(0,0,0,.12)' : 'none' }}>{l}</button>
          ))}
        </div>
        {modo === 'usuarios'
          ? <BigBtn t={t} onClick={() => setModal({ mode: 'crear' })}><IPlus size={17}/> Crear usuario</BigBtn>
          : <BigBtn t={t} onClick={async () => { setBusyAction(true); try { const r = await api().crearRol({ nombre: 'Nuevo rol', descripcion: 'Describe este rol.' }); await reloadR(); setSelR(r.rol.id); } catch (e) { alert(e.message || 'No se pudo crear el rol'); } setBusyAction(false); }}><IPlus size={17}/> Nuevo rol</BigBtn>}
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '272px 1fr', gap: 18, alignItems: 'start' }}>
        {/* Lista izquierda */}
        <div style={{ background: '#fff', border: `1px solid ${t.border}`, borderRadius: 16, overflow: 'hidden' }}>
          <div style={{ padding: '13px 16px', borderBottom: `1px solid ${t.grid}`, fontFamily: t.mono, fontSize: 10.5, letterSpacing: 1, textTransform: 'uppercase', color: t.ter, fontWeight: 700 }}>
            {modo === 'usuarios' ? `Usuarios ${users.length}` : `Roles ${roles.length}`}
          </div>
          {modo === 'usuarios' ? users.map((u, i) => {
            const on = user && u.id === user.id; const r = roleBySlug(u.rolAdmin || 'admin');
            return (
              <button key={u.id} onClick={() => { setSelU(u.id); setTab('permisos'); }} style={{ width: '100%', textAlign: 'left', cursor: 'pointer', border: 'none',
                borderLeft: `3px solid ${on ? t.brand : 'transparent'}`, background: on ? '#F4F8FF' : '#fff', padding: '12px 15px', display: 'flex', alignItems: 'center', gap: 11 }}>
                <span style={{ width: 38, height: 38, borderRadius: '50%', background: AV[i % AV.length], color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontFamily: t.mono, fontSize: 13, flexShrink: 0 }}>{iniciales(u.nombre)}</span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 700, color: t.text, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{u.nombre}</div>
                  <div style={{ fontSize: 11.5, color: t.ter }}>{r.nombre}</div>
                </div>
                {esRolAdmin(u.rolAdmin) && <span style={{ color: t.brand, flexShrink: 0 }}><I.Shield size={15}/></span>}
              </button>
            );
          }) : roles.map((r) => {
            const on = role && r.id === role.id;
            return (
              <button key={r.id} onClick={() => setSelR(r.id)} style={{ width: '100%', textAlign: 'left', cursor: 'pointer', border: 'none',
                borderLeft: `3px solid ${on ? t.brand : 'transparent'}`, background: on ? '#F4F8FF' : '#fff', padding: '13px 15px', display: 'flex', alignItems: 'center', gap: 11 }}>
                <span style={{ width: 9, height: 9, borderRadius: '50%', background: r.sistema ? t.brand : t.success, flexShrink: 0 }}/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 700, color: t.text }}>{r.nombre}</div>
                  <div style={{ fontSize: 11.5, color: t.ter }}>{usersOf(r.slug)} usuario(s)</div>
                </div>
                {r.sistema && <span style={{ color: t.brand, flexShrink: 0 }}><I.Shield size={15}/></span>}
              </button>
            );
          })}
          {modo === 'usuarios' && !users.length && <div style={{ padding: 18, fontSize: 13, color: t.ter }}>Todavía no hay cuentas de equipo.</div>}
        </div>

        {/* Detalle derecha */}
        {modo === 'usuarios' ? (
          user ? <UsuarioDetalle key={user.id} t={t} I={I} user={user} roles={roles} roleBySlug={roleBySlug} tab={tab} setTab={setTab}
            onEditar={() => setModal({ mode: 'editar', user })} onBorrar={() => borrarUser(user)} onCambiarRol={cambiarRolU} busyAction={busyAction} reload={reloadEq}/>
            : <div style={{ background: '#fff', border: `1px solid ${t.border}`, borderRadius: 16, padding: 40, textAlign: 'center', color: t.ter, fontSize: 13.5 }}>Crea la primera cuenta del equipo.</div>
        ) : (
          role ? <RolDetalle key={role.id} t={t} I={I} role={role} usersOf={usersOf} reload={reloadR}/>
            : <div style={{ background: '#fff', border: `1px solid ${t.border}`, borderRadius: 16, padding: 40, textAlign: 'center', color: t.ter, fontSize: 13.5 }}>Crea un rol para empezar.</div>
        )}
      </div>

      {modal && <UserModal t={t} roles={roles} modal={modal} busy={busyAction} onClose={() => setModal(null)} onSave={modal.mode === 'crear' ? crearUser : editarUser}/>}
      {tempInfo && <TempPasswordModal t={t} email={tempInfo.email} tempPassword={tempInfo.tempPassword} onClose={() => setTempInfo(null)}/>}
    </div>
  );
}

// ── Detalle de un usuario del equipo (permisos + actividad real) ──
function UsuarioDetalle({ t, I, user, roles, roleBySlug, tab, setTab, onEditar, onBorrar, onCambiarRol, busyAction, reload }) {
  const admin = esRolAdmin(user.rolAdmin);
  const roleActual = roleBySlug(user.rolAdmin || 'admin');
  const [draft, setDraft] = React.useState(() => normPerms(admin ? emptyPerms(true) : user.permisos));
  const [saved, setSaved] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => { setDraft(normPerms(admin ? emptyPerms(true) : user.permisos)); setSaved(false); }, [user.id, user.updatedAt]);

  const base = normPerms(admin ? emptyPerms(true) : user.permisos);
  const dirty = JSON.stringify(draft) !== JSON.stringify(base);
  const toggle = (mid, aid) => { if (admin) return; setDraft((p) => ({ ...p, [mid]: { ...p[mid], [aid]: !p[mid][aid] } })); };

  // Auditoría de persistencia 21-jul-2026: guardaba de verdad pero nunca refrescaba `eqData`
  // (a diferencia de RolDetalle.guardar, que sí hace reload) — la barra volvía a mostrar
  // "cambios sin guardar" al toque, y si el admin cambiaba de usuario y volvía, veía los
  // permisos VIEJOS aunque el backend ya tuviera los correctos.
  const guardar = async () => {
    setBusy(true);
    try {
      await window.LOLA.api.permisosEquipo(user.id, draft);
      if (reload) await reload();
      setSaved(true); setTimeout(() => setSaved(false), 1600);
    }
    catch (e) { alert(e.message || 'No se pudo guardar'); }
    setBusy(false);
  };

  return (
    <div style={{ background: '#fff', border: `1px solid ${t.border}`, borderRadius: 16, padding: '22px 24px' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 13, flexWrap: 'wrap' }}>
        <span style={{ width: 46, height: 46, borderRadius: '50%', background: t.brand, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 800, fontFamily: t.mono, fontSize: 15 }}>{iniciales(user.nombre)}</span>
        <div style={{ flex: 1, minWidth: 120 }}>
          <div style={{ fontSize: 18, fontWeight: 800, color: t.text, letterSpacing: -.3 }}>{user.nombre}</div>
          <div style={{ fontSize: 12.5, color: t.ter, fontFamily: t.mono }}>{arroba(user.email)} · {roleActual.nombre}</div>
        </div>
        <SmallBtn t={t} onClick={onEditar}><window.WI.Gear size={14}/> Editar</SmallBtn>
        {!admin && <SmallBtn t={t} tone="danger" onClick={onBorrar} disabled={busyAction}><ITrash size={14}/></SmallBtn>}
      </div>

      <div style={{ display: 'flex', gap: 22, borderBottom: `1px solid ${t.grid}`, margin: '18px 0 20px' }}>
        {[['permisos', 'Roles y permisos'], ['actividad', 'Actividad']].map(([id, l]) => (
          <button key={id} onClick={() => setTab(id)} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '0 0 12px', marginBottom: -1,
            borderBottom: `2px solid ${tab === id ? t.brand : 'transparent'}`, color: tab === id ? t.brand : t.sec, fontFamily: t.font, fontSize: 14, fontWeight: 700 }}>{l}</button>
        ))}
      </div>

      {tab === 'permisos' ? (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div style={{ background: '#FAFBFC', border: `1px solid ${t.border}`, borderRadius: 12, padding: '14px 16px', display: 'flex', alignItems: 'center', gap: 14, flexWrap: 'wrap' }}>
            <div style={{ minWidth: 130 }}>
              <div style={{ fontSize: 10.5, color: t.ter, fontFamily: t.mono, textTransform: 'uppercase', letterSpacing: .8, fontWeight: 700, marginBottom: 6 }}>Rol de seguridad</div>
              <select value={user.rolAdmin || 'admin'} onChange={(e) => onCambiarRol(e.target.value)} disabled={admin}
                style={{ padding: '9px 12px', borderRadius: 9, border: `1.5px solid ${t.border}`, fontFamily: t.font, fontSize: 13.5, color: t.text, background: '#fff', minWidth: 170 }}>
                {admin && <option value="admin">Administrador</option>}
                {roles.filter((r) => !r.sistema).map((r) => <option key={r.id} value={r.slug}>{r.nombre}</option>)}
              </select>
            </div>
            <div style={{ flex: 1, minWidth: 160, fontSize: 12.5, color: t.sec }}>{roleActual.descripcion}</div>
            {!admin && <SmallBtn t={t} onClick={() => setDraft(normPerms(roleActual.permisos))}><window.WI.Shield size={13}/> Heredar del rol</SmallBtn>}
          </div>

          {admin ? (
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '13px 16px', borderRadius: 12, background: t.brand + '0e', border: `1px solid ${t.brand}33`, color: t.brand, fontSize: 13, fontWeight: 600 }}>
              <I.Shield size={17}/> El Administrador tiene control total y no se puede modificar.
            </div>
          ) : (
            <AccessCard perms={draft} actions={
              <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                <SmallBtn t={t} onClick={() => setDraft(emptyPerms(true))}><ICheck size={13}/> Conceder todo</SmallBtn>
                <SmallBtn t={t} onClick={() => setDraft(emptyPerms(false))}><IX size={13}/> Quitar todo</SmallBtn>
              </div>}/>
          )}

          <PermMatrix perms={draft} onToggle={toggle} readOnly={admin}/>

          {!admin && <SaveBar t={t} dirty={dirty} saved={saved} busy={busy} onSave={guardar} onDiscard={() => setDraft(base)}/>}
        </div>
      ) : (
        <ActividadTab t={t} user={user}/>
      )}
    </div>
  );
}

// ── Detalle de un rol (nombre/descripción/permisos, con guardado explícito) ──
function RolDetalle({ t, I, role, usersOf, reload }) {
  const [draft, setDraft] = React.useState(() => ({ nombre: role.nombre, descripcion: role.descripcion, permisos: normPerms(role.permisos) }));
  const [saved, setSaved] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => { setDraft({ nombre: role.nombre, descripcion: role.descripcion, permisos: normPerms(role.permisos) }); setSaved(false); }, [role.id, role.updatedAt]);

  const base = { nombre: role.nombre, descripcion: role.descripcion, permisos: normPerms(role.permisos) };
  const dirty = JSON.stringify(draft) !== JSON.stringify(base);
  const toggle = (mid, aid) => { if (role.sistema) return; setDraft((d) => ({ ...d, permisos: { ...d.permisos, [mid]: { ...d.permisos[mid], [aid]: !d.permisos[mid][aid] } } })); };

  const guardar = async () => {
    setBusy(true);
    try { await window.LOLA.api.actualizarRol(role.id, draft); await reload(); setSaved(true); setTimeout(() => setSaved(false), 1600); }
    catch (e) { alert(e.message || 'No se pudo guardar'); }
    setBusy(false);
  };
  const borrar = async () => {
    if (!confirm('¿Eliminar el rol "' + role.nombre + '"?')) return;
    try { await window.LOLA.api.borrarRol(role.id); await reload(); }
    catch (e) { alert(e.message || 'No se pudo eliminar el rol'); }
  };

  return (
    <div style={{ background: '#fff', border: `1px solid ${t.border}`, borderRadius: 16, padding: '22px 24px' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
        <div style={{ flex: 1, minWidth: 160 }}>
          <div style={{ fontSize: 10.5, color: t.ter, fontFamily: t.mono, textTransform: 'uppercase', letterSpacing: .8, fontWeight: 700, marginBottom: 6 }}>Nombre del rol</div>
          <input value={draft.nombre} disabled={role.sistema} onChange={(e) => setDraft((d) => ({ ...d, nombre: e.target.value }))}
            style={{ width: '100%', boxSizing: 'border-box', padding: '11px 13px', borderRadius: 10, border: `1.5px solid ${t.border}`, fontFamily: t.font, fontSize: 14.5, fontWeight: 700, color: t.text, background: role.sistema ? '#FAFBFC' : '#fff' }}/>
        </div>
        <div style={{ fontSize: 12.5, color: t.ter, fontFamily: t.mono, alignSelf: 'flex-end', paddingBottom: 12 }}>{usersOf(role.slug)} usuario(s)</div>
        {!role.sistema && <SmallBtn t={t} tone="danger" onClick={borrar}><ITrash size={14}/></SmallBtn>}
      </div>
      <div style={{ marginTop: 14 }}>
        <div style={{ fontSize: 10.5, color: t.ter, fontFamily: t.mono, textTransform: 'uppercase', letterSpacing: .8, fontWeight: 700, marginBottom: 6 }}>Descripción</div>
        <input value={draft.descripcion} disabled={role.sistema} onChange={(e) => setDraft((d) => ({ ...d, descripcion: e.target.value }))}
          style={{ width: '100%', boxSizing: 'border-box', padding: '11px 13px', borderRadius: 10, border: `1.5px solid ${t.border}`, fontFamily: t.font, fontSize: 14, color: t.text, background: role.sistema ? '#FAFBFC' : '#fff' }}/>
      </div>

      {role.sistema && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '13px 16px', borderRadius: 12, margin: '16px 0 0',
          background: t.brand + '0e', border: `1px solid ${t.brand}33`, color: t.brand, fontSize: 13, fontWeight: 600 }}>
          <I.Shield size={17}/> El rol Administrador tiene control total y no se puede modificar.
        </div>
      )}

      <div style={{ margin: '16px 0' }}>
        <AccessCard perms={draft.permisos} actions={!role.sistema &&
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            <SmallBtn t={t} onClick={() => setDraft((d) => ({ ...d, permisos: emptyPerms(true) }))}><ICheck size={13}/> Conceder todo</SmallBtn>
            <SmallBtn t={t} onClick={() => setDraft((d) => ({ ...d, permisos: emptyPerms(false) }))}><IX size={13}/> Quitar todo</SmallBtn>
          </div>}/>
      </div>

      <PermMatrix perms={draft.permisos} onToggle={toggle} readOnly={role.sistema}/>

      {!role.sistema && <div style={{ marginTop: 16 }}><SaveBar t={t} dirty={dirty} saved={saved} busy={busy} onSave={guardar} onDiscard={() => setDraft(base)}/></div>}
    </div>
  );
}

// ── Actividad reciente REAL del usuario (bitácora filtrada por actor) ──
function ActividadTab({ t, user }) {
  const { data, loading, error } = window.useAsync(() => window.LOLA.api.bitacora(user.id), [user.id]);
  const human = window.lolaHumanAccion || ((a) => a);
  const M = window.LOLA.map;
  if (loading) return <window.Loading label="Cargando actividad…"/>;
  if (error) return <window.ErrState error={error}/>;
  const items = (data && data.items) || [];
  if (!items.length) return <window.EmptyState icon="Clock" title="Sin actividad todavía" sub="Las acciones de esta persona en la consola aparecerán aquí."/>;
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      {items.slice(0, 12).map((e, i) => (
        <div key={e.id || i} style={{ display: 'flex', gap: 13, padding: '13px 2px', borderBottom: i < Math.min(items.length, 12) - 1 ? `1px solid ${t.grid}` : 'none' }}>
          <span style={{ width: 34, height: 34, borderRadius: 9, background: t.brand + '16', color: t.brand, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><window.WI.Clock size={16}/></span>
          <div style={{ flex: 1 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', gap: 10 }}>
              <span style={{ fontSize: 14, fontWeight: 700, color: t.text }}>{human(e.accion)}</span>
              <span style={{ fontSize: 11.5, color: t.ter, fontFamily: t.mono, whiteSpace: 'nowrap' }}>{M.fecha(e.createdAt)}</span>
            </div>
            <div style={{ fontSize: 12.5, color: t.sec, marginTop: 2 }}>{[e.entidad, e.detalle].filter(Boolean).join(' · ')}</div>
          </div>
        </div>
      ))}
      <div style={{ fontSize: 12, color: t.ter, fontFamily: t.mono, marginTop: 12 }}>Registro completo en Bitácora / Auditoría.</div>
    </div>
  );
}

// ── Modal crear/editar usuario ──
function UserModal({ t, roles, modal, busy, onClose, onSave }) {
  const u = modal.user || {};
  const [nombre, setNombre] = React.useState(u.nombre || '');
  const [email, setEmail] = React.useState(u.email || '');
  const [rol, setRol] = React.useState(u.rolAdmin || (roles.find((r) => !r.sistema) || {}).slug || 'soporte');
  const fld = { width: '100%', boxSizing: 'border-box', padding: '11px 13px', borderRadius: 10, border: `1.5px solid ${t.border}`, fontFamily: t.font, fontSize: 14, color: t.text, marginTop: 6 };
  const lbl = { fontSize: 11, color: t.sec, fontFamily: t.mono, textTransform: 'uppercase', letterSpacing: .8, fontWeight: 700 };
  const ok = () => {
    if (!nombre.trim()) { alert('Escribe el nombre.'); return; }
    if (!email.trim()) { alert('Escribe el correo.'); return; }
    onSave({ id: u.id, nombre: nombre.trim(), email: email.trim(), rol });
  };
  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 90, background: 'rgba(10,20,32,.6)', backdropFilter: 'blur(3px)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 22 }}>
      <div onClick={(e) => e.stopPropagation()} style={{ background: '#fff', borderRadius: 18, width: '100%', maxWidth: 440, padding: 24, boxShadow: '0 40px 100px -30px rgba(0,0,0,.6)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 11, marginBottom: 18 }}>
          <span style={{ width: 38, height: 38, borderRadius: 10, background: t.brand + '14', color: t.brand, display: 'flex', alignItems: 'center', justifyContent: 'center' }}><window.WI.User size={20}/></span>
          <div style={{ fontSize: 17, fontWeight: 800, color: t.text }}>{modal.mode === 'crear' ? 'Crear usuario' : 'Editar usuario'}</div>
        </div>
        <label style={{ display: 'block', marginBottom: 13 }}><span style={lbl}>Nombre completo</span><input value={nombre} onChange={(e) => setNombre(e.target.value)} placeholder="Nombre y apellido" style={fld}/></label>
        <label style={{ display: 'block', marginBottom: 13 }}><span style={lbl}>Correo</span><input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="correo@lola.gt" style={fld}/></label>
        {modal.mode === 'crear' && (
          <label style={{ display: 'block', marginBottom: 20 }}><span style={lbl}>Rol</span>
            <select value={rol} onChange={(e) => setRol(e.target.value)} style={fld}>{roles.filter((r) => !r.sistema).map((r) => <option key={r.id} value={r.slug}>{r.nombre}</option>)}</select></label>
        )}
        {modal.mode === 'crear' && <div style={{ fontSize: 12, color: t.ter, marginBottom: 16, lineHeight: 1.5 }}>Se genera una contraseña temporal que se muestra una sola vez al crear la cuenta.</div>}
        <div style={{ display: 'flex', gap: 10, justifyContent: 'flex-end' }}>
          <BigBtn t={t} kind="ghost" onClick={onClose}>Cancelar</BigBtn>
          <BigBtn t={t} onClick={ok} disabled={busy}>{busy ? 'Guardando…' : (modal.mode === 'crear' ? 'Crear usuario' : 'Guardar')}</BigBtn>
        </div>
      </div>
    </div>
  );
}

window.SeguridadView = SeguridadView;
