// ─────────────────────────────────────────────────────
// SISTEMA — admin config panel: 6 tabs.
// Gate: rol === 'admin' enforced in app.jsx (can.sistema).
// Tabs: Usuarios / Operarios / Materiales / Proveedores / Tareas / Tokens
// ─────────────────────────────────────────────────────

// ── Shared helpers ───────────────────────────────────

const sysOverlay = (onClick) => ({
  style: {
    position: 'fixed', inset: 0, background: 'rgba(0,0,0,.35)',
    zIndex: 200, display: 'flex', alignItems: 'flex-start',
    justifyContent: 'center', padding: '40px 20px', overflow: 'auto',
  },
  onClick,
});

const sysModal = (maxWidth = 440) => ({
  style: {
    background: 'var(--surface)', border: '1px solid var(--line)',
    borderRadius: 8, width: `min(${maxWidth}px,100%)`,
    boxShadow: '0 20px 60px rgba(0,0,0,.18)',
  },
  onClick: (e) => e.stopPropagation(),
});

const SysModalHeader = ({ title, onClose }) => (
  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px 18px', borderBottom: '1px solid var(--line)' }}>
    <h3 style={{ margin: 0, fontSize: 15 }}>{title}</h3>
    <button className="icon-btn" onClick={onClose}><Icon name="x" /></button>
  </div>
);

const SysModalFooter = ({ onClose, saving, label }) => (
  <div style={{ display: 'flex', justifyContent: 'flex-end', padding: '12px 18px', borderTop: '1px solid var(--line)', gap: 8 }}>
    <button type="button" className="btn ghost" onClick={onClose}>Cancelar</button>
    <button type="submit" className="btn primary" disabled={saving}>{saving ? 'Guardando…' : label}</button>
  </div>
);

const SysTabHeader = ({ title, children }) => (
  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '20px 24px 12px', borderBottom: '1px solid var(--line)' }}>
    <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>{title}</h3>
    <div style={{ display: 'flex', gap: 8 }}>{children}</div>
  </div>
);

const SysEmptyRow = ({ cols, msg }) => (
  <tr><td colSpan={cols} style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)', fontSize: 13 }}>{msg}</td></tr>
);

// ─────────────── USUARIOS ───────────────

// RBAC (B3): el usuario se asigna a un ROL (rol_id). Los checkboxes de
// módulos existen sólo como override legacy opcional, y se generan desde el
// catálogo (/api/modulos), ya no hardcodeados.
const UsuariosModal = ({ editUser, roles, modulos, onClose, onSaved, onToast }) => {
  const isEdit = !!editUser;
  const [form, setForm] = React.useState({
    username: editUser?.username || '',
    password: '',
    rol: editUser?.rol || 'user',
    rol_id: editUser?.rol_id != null ? String(editUser.rol_id) : '',
    permisos: editUser
      ? (() => { try { return JSON.parse(editUser.permisos || '["leads"]'); } catch { return ['leads']; } })()
      : ['leads'],
  });
  const [showOverride, setShowOverride] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const togglePerm = (p) => setForm(f => ({
    ...f,
    permisos: f.permisos.includes(p) ? f.permisos.filter(x => x !== p) : [...f.permisos, p],
  }));

  const submit = async (e) => {
    e.preventDefault();
    if (!form.username.trim()) return onToast('Username requerido', true);
    if (!isEdit && !form.password) return onToast('Contraseña requerida', true);
    setSaving(true);
    const payload = {
      username: form.username.trim(),
      rol: form.rol,
      rol_id: form.rol_id === '' ? null : Number(form.rol_id),
      permisos: form.permisos,
    };
    if (form.password) payload.password = form.password;
    try {
      isEdit
        ? await API.put(`/api/usuarios/${editUser.id}`, payload)
        : await API.post('/api/usuarios', payload);
      onToast(isEdit ? 'Usuario actualizado ✓' : 'Usuario creado ✓');
      onSaved();
      onClose();
    } catch (e) {
      onToast(e.message || 'Error al guardar', true);
    } finally {
      setSaving(false);
    }
  };

  const labelStyle = { display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' };

  return (
    <div className="modal-bg open" {...sysOverlay(() => onClose())}>
      <div className="modal" {...sysModal(420)}>
        <SysModalHeader title={isEdit ? 'Editar usuario' : 'Nuevo usuario'} onClose={onClose} />
        <form onSubmit={submit} autoComplete="off">
          <div style={{ padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 12 }}>
            <label style={labelStyle}>
              Usuario *
              <input value={form.username} onChange={e => set('username', e.target.value)} required autoFocus />
            </label>
            <label style={labelStyle}>
              Contraseña {isEdit && <span style={{ fontWeight: 'normal', color: 'var(--ink-3)' }}>(vacío = no cambiar)</span>}
              <input type="password" value={form.password} onChange={e => set('password', e.target.value)} />
            </label>
            <label style={labelStyle}>
              Rol
              <select value={form.rol} onChange={e => set('rol', e.target.value)}>
                <option value="user">user</option>
                <option value="admin">admin</option>
              </select>
            </label>
            {form.rol === 'user' && (
              <label style={labelStyle}>
                Rol asignado
                <select value={form.rol_id} onChange={e => set('rol_id', e.target.value)}>
                  <option value="">— Sin rol —</option>
                  {(roles || []).filter(r => !r.es_admin).map(r => (
                    <option key={r.id} value={r.id}>{r.nombre}</option>
                  ))}
                </select>
              </label>
            )}
            {form.rol === 'user' && (
              <div>
                <button
                  type="button"
                  className="btn ghost"
                  style={{ fontSize: 12, padding: '2px 6px' }}
                  onClick={() => setShowOverride(s => !s)}
                >{showOverride ? '▾' : '▸'} Permisos adicionales (override)</button>
                {showOverride && (
                  <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', marginTop: 8 }}>
                    {(modulos || []).map(({ key, label }) => (
                      <label key={key} style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, fontWeight: 'normal' }}>
                        <input type="checkbox" checked={form.permisos.includes(key)} onChange={() => togglePerm(key)} />
                        {label}
                      </label>
                    ))}
                  </div>
                )}
              </div>
            )}
          </div>
          <SysModalFooter onClose={onClose} saving={saving} label={isEdit ? 'Guardar' : 'Crear usuario'} />
        </form>
      </div>
    </div>
  );
};

const UsuariosTab = ({ onToast }) => {
  const [users, setUsers] = React.useState([]);
  const [roles, setRoles] = React.useState([]);
  const [modulos, setModulos] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [modal, setModal] = React.useState(null);

  const load = async () => {
    try {
      const [u, r, m] = await Promise.all([
        API.get('/api/usuarios'),
        API.get('/api/roles'),
        API.get('/api/modulos'),
      ]);
      setUsers(u); setRoles(r); setModulos(m);
    }
    catch (e) { onToast(e.message || 'Error al cargar usuarios', true); }
    finally { setLoading(false); }
  };
  React.useEffect(() => { load(); }, []);
  const rolNombre = (rolId) => (roles.find(r => r.id === rolId) || {}).nombre;

  const del = async (u) => {
    if (!confirm(`¿Eliminar usuario "${u.username}"?`)) return;
    try { await API.del(`/api/usuarios/${u.id}`); onToast('Usuario eliminado'); load(); }
    catch (e) { onToast(e.message || 'Error al eliminar', true); }
  };

  const PERM_LABELS = { leads: 'Leads', compras: 'Compras', partes: 'Partes', configurador: 'Presupuestador' };

  return (
    <>
      <SysTabHeader title="Usuarios">
        <button className="btn primary" onClick={() => setModal('new')}><Icon name="plus" /> Nuevo usuario</button>
      </SysTabHeader>
      <div className="tbl-wrap" style={{ margin: '0 24px 24px' }}>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead>
              <tr><th>Usuario</th><th>Rol</th><th>Permisos</th><th>Alta</th><th></th></tr>
            </thead>
            <tbody>
              {loading && <tr><td colSpan="5" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</td></tr>}
              {!loading && users.map(u => {
                let perms = [];
                try { perms = JSON.parse(u.permisos || '["leads"]'); } catch {}
                return (
                  <tr key={u.id} onClick={e => { if (!e.target.closest('button')) setModal(u); }}>
                    <td className="strong">{u.username}</td>
                    <td><span className={`pill ${u.rol === 'admin' ? 'info' : 'ok'}`}>{u.rol}</span></td>
                    <td>
                      {u.rol === 'admin'
                        ? <span style={{ color: 'var(--ink-3)', fontSize: 12 }}>Acceso completo</span>
                        : (
                          <>
                            {u.rol_id && rolNombre(u.rol_id) && (
                              <span className="pill info" style={{ marginRight: 4 }}>{rolNombre(u.rol_id)}</span>
                            )}
                            {perms.map(p => <span key={p} className="pill glass" style={{ marginRight: 4 }}>{PERM_LABELS[p] || p}</span>)}
                            {!u.rol_id && !perms.length && <span style={{ color: 'var(--ink-3)', fontSize: 12 }}>Sin acceso</span>}
                          </>
                        )
                      }
                    </td>
                    <td className="muted">{u.created_at ? new Date(u.created_at).toLocaleDateString('es-ES') : '—'}</td>
                    <td style={{ textAlign: 'right' }}>
                      <button className="btn ghost" style={{ marginRight: 4 }} onClick={() => setModal(u)}>Editar</button>
                      <button className="btn ghost" style={{ color: 'var(--err)' }} onClick={() => del(u)}>Eliminar</button>
                    </td>
                  </tr>
                );
              })}
              {!loading && !users.length && <SysEmptyRow cols={5} msg="No hay usuarios." />}
            </tbody>
          </table>
        </div>
      </div>
      {modal && (
        <UsuariosModal
          editUser={modal === 'new' ? null : modal}
          roles={roles}
          modulos={modulos}
          onClose={() => setModal(null)}
          onSaved={load}
          onToast={onToast}
        />
      )}
    </>
  );
};

// ─────────────── ROLES Y PERMISOS ───────────────

const RolModal = ({ rol, modulos, onClose, onSaved, onToast }) => {
  const isEdit = !!rol;
  const [form, setForm] = React.useState({
    nombre: rol?.nombre || '',
    descripcion: rol?.descripcion || '',
    es_admin: !!rol?.es_admin,
    modulos: Array.isArray(rol?.modulos) ? rol.modulos : [],
  });
  const [saving, setSaving] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const toggle = (m) => setForm(f => ({
    ...f,
    modulos: f.modulos.includes(m) ? f.modulos.filter(x => x !== m) : [...f.modulos, m],
  }));

  const submit = async (e) => {
    e.preventDefault();
    if (!form.nombre.trim()) return onToast('Nombre requerido', true);
    setSaving(true);
    const payload = {
      nombre: form.nombre.trim(),
      descripcion: form.descripcion.trim() || null,
      es_admin: form.es_admin,
      modulos: form.es_admin ? [] : form.modulos,
    };
    try {
      isEdit
        ? await API.put(`/api/roles/${rol.id}`, payload)
        : await API.post('/api/roles', payload);
      onToast(isEdit ? 'Rol actualizado ✓' : 'Rol creado ✓');
      onSaved();
      onClose();
    } catch (e) {
      onToast(e.message || 'Error al guardar', true);
    } finally {
      setSaving(false);
    }
  };

  const labelStyle = { display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' };

  return (
    <div className="modal-bg open" {...sysOverlay(() => onClose())}>
      <div className="modal" {...sysModal(460)}>
        <SysModalHeader title={isEdit ? 'Editar rol' : 'Nuevo rol'} onClose={onClose} />
        <form onSubmit={submit} autoComplete="off">
          <div style={{ padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 12 }}>
            <label style={labelStyle}>
              Nombre *
              <input value={form.nombre} onChange={e => set('nombre', e.target.value)} required autoFocus />
            </label>
            <label style={labelStyle}>
              Descripción
              <input value={form.descripcion} onChange={e => set('descripcion', e.target.value)} />
            </label>
            <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, fontWeight: 'normal' }}>
              <input type="checkbox" checked={form.es_admin} onChange={e => set('es_admin', e.target.checked)} />
              Administrador (acceso completo)
            </label>
            {!form.es_admin && (
              <div>
                <div style={{ fontSize: 12, color: 'var(--ink-2)', marginBottom: 6 }}>Módulos</div>
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                  {(modulos || []).map(({ key, label }) => (
                    <label key={key} style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, fontWeight: 'normal' }}>
                      <input type="checkbox" checked={form.modulos.includes(key)} onChange={() => toggle(key)} />
                      {label}
                    </label>
                  ))}
                </div>
              </div>
            )}
          </div>
          <SysModalFooter onClose={onClose} saving={saving} label={isEdit ? 'Guardar' : 'Crear rol'} />
        </form>
      </div>
    </div>
  );
};

const RolesTab = ({ onToast }) => {
  const [roles, setRoles] = React.useState([]);
  const [modulos, setModulos] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [modal, setModal] = React.useState(null);

  const load = async () => {
    try {
      const [r, m] = await Promise.all([API.get('/api/roles'), API.get('/api/modulos')]);
      setRoles(r); setModulos(m);
    } catch (e) { onToast(e.message || 'Error al cargar roles', true); }
    finally { setLoading(false); }
  };
  React.useEffect(() => { load(); }, []);

  const labelOf = (k) => (modulos.find(m => m.key === k) || {}).label || k;

  const del = async (rol) => {
    if (!confirm(`¿Eliminar rol "${rol.nombre}"?`)) return;
    try { await API.del(`/api/roles/${rol.id}`); onToast('Rol eliminado'); load(); }
    catch (e) { onToast(e.message || 'Error al eliminar', true); }
  };

  return (
    <>
      <SysTabHeader title="Roles y permisos">
        <button className="btn primary" onClick={() => setModal('new')}><Icon name="plus" /> Nuevo rol</button>
      </SysTabHeader>
      <div className="tbl-wrap" style={{ margin: '0 24px 24px' }}>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead>
              <tr><th>Rol</th><th>Descripción</th><th>Módulos</th><th></th></tr>
            </thead>
            <tbody>
              {loading && <tr><td colSpan="4" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</td></tr>}
              {!loading && roles.map(r => (
                <tr key={r.id} onClick={e => { if (!e.target.closest('button')) setModal(r); }}>
                  <td className="strong">{r.nombre}</td>
                  <td className="muted">{r.descripcion || '—'}</td>
                  <td>
                    {r.es_admin
                      ? <span style={{ color: 'var(--ink-3)', fontSize: 12 }}>Acceso completo</span>
                      : (r.modulos || []).length
                        ? r.modulos.map(m => <span key={m} className="pill glass" style={{ marginRight: 4 }}>{labelOf(m)}</span>)
                        : <span style={{ color: 'var(--ink-3)', fontSize: 12 }}>Sin módulos</span>
                    }
                  </td>
                  <td style={{ textAlign: 'right' }}>
                    <button className="btn ghost" style={{ marginRight: 4 }} onClick={() => setModal(r)}>Editar</button>
                    <button className="btn ghost" style={{ color: 'var(--err)' }} onClick={() => del(r)}>Eliminar</button>
                  </td>
                </tr>
              ))}
              {!loading && !roles.length && <SysEmptyRow cols={4} msg="No hay roles." />}
            </tbody>
          </table>
        </div>
      </div>
      {modal && (
        <RolModal
          rol={modal === 'new' ? null : modal}
          modulos={modulos}
          onClose={() => setModal(null)}
          onSaved={load}
          onToast={onToast}
        />
      )}
    </>
  );
};

// ─────────────── OPERARIOS ───────────────

const OperariosModal = ({ op, onClose, onSaved, onToast }) => {
  const isEdit = !!op;
  const [form, setForm] = React.useState({ nombre: op?.nombre || '', telefono: op?.telefono || '' });
  const [saving, setSaving] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const submit = async (e) => {
    e.preventDefault();
    if (!form.nombre.trim()) return onToast('Nombre requerido', true);
    setSaving(true);
    try {
      isEdit
        ? await API.put(`/api/operarios/${op.id}`, { nombre: form.nombre.trim(), telefono: form.telefono || null })
        : await API.post('/api/operarios', { nombre: form.nombre.trim(), telefono: form.telefono || null });
      onToast(isEdit ? 'Operario actualizado ✓' : 'Operario creado ✓');
      onSaved();
      onClose();
    } catch (e) {
      onToast(e.message || 'Error al guardar', true);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="modal-bg open" {...sysOverlay(() => onClose())}>
      <div className="modal" {...sysModal(380)}>
        <SysModalHeader title={isEdit ? `Editar operario · ${op.codigo}` : 'Nuevo operario'} onClose={onClose} />
        <form onSubmit={submit} autoComplete="off">
          <div style={{ padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 12 }}>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Nombre *
              <input value={form.nombre} onChange={e => set('nombre', e.target.value)} required autoFocus />
            </label>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Teléfono
              <input value={form.telefono} onChange={e => set('telefono', e.target.value)} placeholder="+34 600 000 000" />
            </label>
            {!isEdit && (
              <p style={{ margin: 0, fontSize: 12, color: 'var(--ink-3)' }}>El código se asigna automáticamente.</p>
            )}
          </div>
          <SysModalFooter onClose={onClose} saving={saving} label={isEdit ? 'Guardar' : 'Crear operario'} />
        </form>
      </div>
    </div>
  );
};

const OperariosTab = ({ onToast }) => {
  const [ops, setOps] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [modal, setModal] = React.useState(null);

  const load = async () => {
    try { setOps(await API.get('/api/operarios')); }
    catch (e) { onToast(e.message || 'Error', true); }
    finally { setLoading(false); }
  };
  React.useEffect(() => { load(); }, []);

  const del = async (op) => {
    if (!confirm(`¿Eliminar operario "${op.nombre}"?`)) return;
    try { await API.del(`/api/operarios/${op.id}`); onToast('Operario eliminado'); load(); }
    catch (e) { onToast(e.message || 'Error al eliminar', true); }
  };

  return (
    <>
      <SysTabHeader title="Operarios">
        <button className="btn primary" onClick={() => setModal('new')}><Icon name="plus" /> Nuevo operario</button>
      </SysTabHeader>
      <div className="tbl-wrap" style={{ margin: '0 24px 24px' }}>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead><tr><th>Código</th><th>Nombre</th><th>Teléfono</th><th></th></tr></thead>
            <tbody>
              {loading && <tr><td colSpan="4" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</td></tr>}
              {!loading && ops.map(op => (
                <tr key={op.id} onClick={e => { if (!e.target.closest('button')) setModal(op); }}>
                  <td className="num">{op.codigo}</td>
                  <td className="strong">{op.nombre}</td>
                  <td className="muted">{op.telefono || '—'}</td>
                  <td style={{ textAlign: 'right' }}>
                    <button className="btn ghost" style={{ marginRight: 4 }} onClick={() => setModal(op)}>Editar</button>
                    <button className="btn ghost" style={{ color: 'var(--err)' }} onClick={() => del(op)}>Eliminar</button>
                  </td>
                </tr>
              ))}
              {!loading && !ops.length && <SysEmptyRow cols={4} msg="No hay operarios." />}
            </tbody>
          </table>
        </div>
      </div>
      {modal && (
        <OperariosModal
          op={modal === 'new' ? null : modal}
          onClose={() => setModal(null)}
          onSaved={load}
          onToast={onToast}
        />
      )}
    </>
  );
};

// ─────────────── MATERIALES ───────────────

const MaterialModal = ({ mat, categorias, onClose, onSaved, onToast }) => {
  const isEdit = !!mat;
  const [form, setForm] = React.useState({
    nombre: mat?.nombre || '',
    codigo: mat?.codigo || '',
    familia: mat?.familia || '',
    unidad_medida: mat?.unidad_medida || 'ud',
    parent_id: mat?.parent_id != null ? String(mat.parent_id) : '',
    activo: mat?.activo !== false,
  });
  const [saving, setSaving] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const submit = async (e) => {
    e.preventDefault();
    if (!form.nombre.trim()) return onToast('Nombre requerido', true);
    setSaving(true);
    const payload = {
      nombre: form.nombre.trim(),
      codigo: form.codigo.trim() || null,
      familia: form.familia.trim() || null,
      unidad_medida: form.unidad_medida || 'ud',
      parent_id: form.parent_id ? Number(form.parent_id) : null,
      activo: form.activo,
    };
    try {
      isEdit
        ? await API.put(`/api/materiales/${mat.id}`, payload)
        : await API.post('/api/materiales', payload);
      onToast(isEdit ? 'Material actualizado ✓' : 'Material creado ✓');
      onSaved();
      onClose();
    } catch (e) {
      onToast(e.message || 'Error al guardar', true);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="modal-bg open" {...sysOverlay(() => onClose())}>
      <div className="modal" {...sysModal(460)}>
        <SysModalHeader title={isEdit ? 'Editar material' : 'Nuevo material'} onClose={onClose} />
        <form onSubmit={submit} autoComplete="off">
          <div style={{ padding: '16px 18px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <label style={{ gridColumn: '1/-1', display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Nombre *
              <input value={form.nombre} onChange={e => set('nombre', e.target.value)} required autoFocus />
            </label>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Código
              <input value={form.codigo} onChange={e => set('codigo', e.target.value)} placeholder="Opcional" />
            </label>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Unidad
              <input value={form.unidad_medida} onChange={e => set('unidad_medida', e.target.value)} />
            </label>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Familia
              <input value={form.familia} onChange={e => set('familia', e.target.value)} placeholder="Perfiles, Vidrios…" />
            </label>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Categoría padre
              <select value={form.parent_id} onChange={e => set('parent_id', e.target.value)}>
                <option value="">— Es una categoría —</option>
                {categorias.filter(c => !mat || c.id !== mat.id).map(c => (
                  <option key={c.id} value={c.id}>{c.nombre}</option>
                ))}
              </select>
            </label>
            <label style={{ gridColumn: '1/-1', display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, fontWeight: 'normal' }}>
              <input type="checkbox" checked={form.activo} onChange={e => set('activo', e.target.checked)} />
              Activo
            </label>
          </div>
          <SysModalFooter onClose={onClose} saving={saving} label={isEdit ? 'Guardar' : 'Crear material'} />
        </form>
      </div>
    </div>
  );
};

const MatProvModal = ({ matId, matNombre, proveedoresList, onClose, onSaved, onToast }) => {
  const [form, setForm] = React.useState({ proveedor_id: '', precio_referencia: '', plazo_dias: '' });
  const [saving, setSaving] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const submit = async (e) => {
    e.preventDefault();
    if (!form.proveedor_id) return onToast('Selecciona un proveedor', true);
    setSaving(true);
    try {
      await API.post(`/api/materiales/${matId}/proveedores`, {
        proveedor_id: Number(form.proveedor_id),
        precio_referencia: form.precio_referencia ? Number(form.precio_referencia) : null,
        plazo_dias: form.plazo_dias ? Number(form.plazo_dias) : null,
      });
      onToast('Proveedor vinculado ✓');
      onSaved();
      onClose();
    } catch (e) {
      onToast(e.message || 'Error', true);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="modal-bg open" {...sysOverlay(() => onClose())}>
      <div className="modal" {...sysModal(380)}>
        <SysModalHeader title={`Vincular proveedor · ${matNombre}`} onClose={onClose} />
        <form onSubmit={submit} autoComplete="off">
          <div style={{ padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 12 }}>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Proveedor *
              <select value={form.proveedor_id} onChange={e => set('proveedor_id', e.target.value)} required>
                <option value="">Seleccionar…</option>
                {proveedoresList.map(p => <option key={p.id} value={p.id}>{p.nombre}</option>)}
              </select>
            </label>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
                Precio ref. (€)
                <input type="number" step="0.01" min="0" value={form.precio_referencia} onChange={e => set('precio_referencia', e.target.value)} />
              </label>
              <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
                Plazo (días)
                <input type="number" step="1" min="0" value={form.plazo_dias} onChange={e => set('plazo_dias', e.target.value)} />
              </label>
            </div>
          </div>
          <SysModalFooter onClose={onClose} saving={saving} label="Vincular" />
        </form>
      </div>
    </div>
  );
};

const MaterialesTab = ({ onToast }) => {
  const [materiales, setMateriales] = React.useState([]);
  const [proveedoresList, setProveedoresList] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [search, setSearch] = React.useState('');
  const [modal, setModal] = React.useState(null);
  const [provModal, setProvModal] = React.useState(null);
  const [expanded, setExpanded] = React.useState(new Set());

  const load = async () => {
    try {
      const [mats, provs] = await Promise.all([API.get('/api/materiales'), API.get('/api/proveedores')]);
      setMateriales(mats);
      setProveedoresList(provs);
    } catch (e) {
      onToast(e.message || 'Error', true);
    } finally {
      setLoading(false);
    }
  };
  React.useEffect(() => { load(); }, []);

  const del = async (mat) => {
    if (!confirm(`¿Eliminar "${mat.nombre}"?`)) return;
    try { await API.del(`/api/materiales/${mat.id}`); onToast('Eliminado'); load(); }
    catch (e) { onToast(e.message || 'Error al eliminar', true); }
  };

  const unlinkProv = async (matId, provId, provNombre) => {
    if (!confirm(`¿Desvincular "${provNombre}"?`)) return;
    try { await API.del(`/api/materiales/${matId}/proveedores/${provId}`); onToast('Desvinculado'); load(); }
    catch (e) { onToast(e.message || 'Error', true); }
  };

  const toggleExpand = (id) => setExpanded(s => { const n = new Set(s); n.has(id) ? n.delete(id) : n.add(id); return n; });

  const cats = materiales.filter(m => !m.parent_id);
  const childrenOf = (catId) => materiales.filter(m => m.parent_id === catId);

  const filt = search.trim().toLowerCase();
  const visibleCats = filt
    ? cats.filter(c => c.nombre.toLowerCase().includes(filt) || childrenOf(c.id).some(k => k.nombre.toLowerCase().includes(filt)))
    : cats;

  return (
    <>
      <SysTabHeader title="Materiales">
        <input
          className="tb-input"
          placeholder="Buscar…"
          value={search}
          onChange={e => setSearch(e.target.value)}
          style={{ width: 180 }}
        />
        <button className="btn primary" onClick={() => setModal('new')}><Icon name="plus" /> Nuevo material</button>
      </SysTabHeader>
      <div style={{ padding: '16px 24px 24px' }}>
        {loading && <div style={{ textAlign: 'center', color: 'var(--ink-3)', padding: 32 }}>Cargando…</div>}
        {!loading && visibleCats.length === 0 && <div style={{ textAlign: 'center', color: 'var(--ink-3)', padding: 32 }}>Sin materiales.</div>}
        {!loading && visibleCats.map(cat => {
          const kids = childrenOf(cat.id).filter(k => !filt || k.nombre.toLowerCase().includes(filt) || cat.nombre.toLowerCase().includes(filt));
          const open = expanded.has(cat.id);
          return (
            <div key={cat.id} style={{ border: '1px solid var(--line)', borderRadius: 6, marginBottom: 8, overflow: 'hidden' }}>
              <div style={{ display: 'flex', alignItems: 'center', padding: '10px 14px', background: 'var(--surface-2)', gap: 8 }}>
                <button
                  className="btn ghost"
                  style={{ fontSize: 13, padding: '2px 6px', minWidth: 24 }}
                  onClick={() => toggleExpand(cat.id)}
                >{open ? '▾' : '▸'}</button>
                <span style={{ fontWeight: 600, flex: 1 }}>
                  {cat.nombre}
                  {cat.codigo && <code style={{ marginLeft: 8, fontWeight: 'normal', opacity: 0.55, fontSize: 11 }}>{cat.codigo}</code>}
                  <span style={{ marginLeft: 8, fontSize: 12, color: 'var(--ink-3)', fontWeight: 'normal' }}>{kids.length} {kids.length === 1 ? 'ítem' : 'ítems'}</span>
                  {!cat.activo && <span className="pill err" style={{ marginLeft: 8, fontSize: 11 }}>Inactivo</span>}
                </span>
                <button className="btn ghost" style={{ fontSize: 12 }} onClick={() => setModal(cat)}>Editar</button>
                <button className="btn ghost" style={{ fontSize: 12 }} onClick={() => setProvModal({ id: cat.id, nombre: cat.nombre })}>+ Proveedor</button>
                <button className="btn ghost" style={{ fontSize: 12, color: 'var(--err)' }} onClick={() => del(cat)}>Eliminar</button>
              </div>
              {open && (
                <div>
                  {kids.length === 0 && (
                    <div style={{ padding: '10px 14px 10px 48px', color: 'var(--ink-3)', fontSize: 13 }}>Sin ítems en esta categoría.</div>
                  )}
                  {kids.map(kid => (
                    <div key={kid.id} style={{ display: 'flex', alignItems: 'center', padding: '8px 14px 8px 48px', borderTop: '1px solid var(--line)', gap: 8 }}>
                      <span style={{ flex: 1, fontSize: 13 }}>
                        {kid.nombre}
                        {kid.codigo && <code style={{ marginLeft: 8, opacity: 0.55, fontSize: 11 }}>{kid.codigo}</code>}
                        <span style={{ marginLeft: 8, color: 'var(--ink-3)', fontSize: 12 }}>{kid.unidad_medida || 'ud'}</span>
                        {!kid.activo && <span className="pill err" style={{ marginLeft: 8, fontSize: 11 }}>Inactivo</span>}
                      </span>
                      <button className="btn ghost" style={{ fontSize: 11 }} onClick={() => setModal(kid)}>Editar</button>
                      <button className="btn ghost" style={{ fontSize: 11 }} onClick={() => setProvModal({ id: kid.id, nombre: kid.nombre })}>+ Proveedor</button>
                      <button className="btn ghost" style={{ fontSize: 11, color: 'var(--err)' }} onClick={() => del(kid)}>Eliminar</button>
                    </div>
                  ))}
                </div>
              )}
            </div>
          );
        })}
      </div>
      {modal && (
        <MaterialModal
          mat={modal === 'new' ? null : modal}
          categorias={cats}
          onClose={() => setModal(null)}
          onSaved={load}
          onToast={onToast}
        />
      )}
      {provModal && (
        <MatProvModal
          matId={provModal.id}
          matNombre={provModal.nombre}
          proveedoresList={proveedoresList}
          onClose={() => setProvModal(null)}
          onSaved={load}
          onToast={onToast}
        />
      )}
    </>
  );
};

// ─────────────── PROVEEDORES ───────────────

const ProveedorModal = ({ prov, onClose, onSaved, onToast }) => {
  const isEdit = !!prov;
  const blank = {
    nombre: '', contacto: '', email: '', telefono: '',
    comercial_nombre: '', comercial_telefono: '',
    produccion_nombre: '', produccion_telefono: '',
    email_presupuestos: '', email_pedidos: '', email_produccion: '', email_reclamaciones: '',
    notas: '',
  };
  const [form, setForm] = React.useState(prov ? { ...blank, ...Object.fromEntries(Object.keys(blank).map(k => [k, prov[k] || ''])) } : { ...blank });
  const [saving, setSaving] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const F = ({ label, k, type = 'text', full, placeholder }) => (
    <label style={{ gridColumn: full ? '1/-1' : undefined, display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
      {label}
      <input type={type} value={form[k]} onChange={e => set(k, e.target.value)} placeholder={placeholder} />
    </label>
  );

  const submit = async (e) => {
    e.preventDefault();
    if (!form.nombre.trim()) return onToast('Nombre requerido', true);
    setSaving(true);
    const payload = {};
    Object.keys(blank).forEach(k => { payload[k] = form[k].trim() || null; });
    payload.nombre = form.nombre.trim();
    try {
      isEdit
        ? await API.put(`/api/proveedores/${prov.id}`, payload)
        : await API.post('/api/proveedores', payload);
      onToast(isEdit ? 'Proveedor actualizado ✓' : 'Proveedor creado ✓');
      onSaved();
      onClose();
    } catch (e) {
      onToast(e.message || 'Error', true);
    } finally {
      setSaving(false);
    }
  };

  const sep = (label) => (
    <div style={{ gridColumn: '1/-1', borderTop: '1px solid var(--line)', paddingTop: 10, marginTop: 2 }}>
      <span style={{ fontSize: 11, color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '.05em' }}>{label}</span>
    </div>
  );

  return (
    <div className="modal-bg open" {...sysOverlay(() => onClose())}>
      <div className="modal" {...sysModal(580)}>
        <SysModalHeader title={isEdit ? 'Editar proveedor' : 'Nuevo proveedor'} onClose={onClose} />
        <form onSubmit={submit} autoComplete="off">
          <div style={{ padding: '16px 18px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <F label="Nombre *" k="nombre" full />
            <F label="Contacto principal" k="contacto" />
            <F label="Teléfono" k="telefono" />
            <F label="Email general" k="email" type="email" full />
            {sep('Contactos por área')}
            <F label="Comercial — nombre" k="comercial_nombre" />
            <F label="Comercial — teléfono" k="comercial_telefono" />
            <F label="Producción — nombre" k="produccion_nombre" />
            <F label="Producción — teléfono" k="produccion_telefono" />
            {sep('Emails de routing')}
            <F label="Presupuestos" k="email_presupuestos" type="email" />
            <F label="Pedidos" k="email_pedidos" type="email" />
            <F label="Producción" k="email_produccion" type="email" />
            <F label="Reclamaciones" k="email_reclamaciones" type="email" />
            <label style={{ gridColumn: '1/-1', display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
              Notas
              <textarea value={form.notas} onChange={e => set('notas', e.target.value)} rows={3} />
            </label>
          </div>
          <SysModalFooter onClose={onClose} saving={saving} label={isEdit ? 'Guardar' : 'Crear proveedor'} />
        </form>
      </div>
    </div>
  );
};

const ProveedoresTab = ({ onToast }) => {
  const [provs, setProvs] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [search, setSearch] = React.useState('');
  const [modal, setModal] = React.useState(null);
  const searchTimer = React.useRef(null);

  const load = async (q) => {
    try {
      const params = q ? { search: q } : undefined;
      setProvs(await API.get('/api/proveedores', params));
    } catch (e) { onToast(e.message || 'Error', true); }
    finally { setLoading(false); }
  };
  React.useEffect(() => { load(); }, []);

  const onSearch = (v) => {
    setSearch(v);
    clearTimeout(searchTimer.current);
    searchTimer.current = setTimeout(() => load(v), 300);
  };

  const del = async (p) => {
    if (!confirm(`¿Eliminar proveedor "${p.nombre}"?`)) return;
    try { await API.del(`/api/proveedores/${p.id}`); onToast('Proveedor eliminado'); load(search); }
    catch (e) { onToast(e.message || 'Error', true); }
  };

  return (
    <>
      <SysTabHeader title="Proveedores">
        <input className="tb-input" placeholder="Buscar…" value={search} onChange={e => onSearch(e.target.value)} style={{ width: 180 }} />
        <button className="btn primary" onClick={() => setModal('new')}><Icon name="plus" /> Nuevo proveedor</button>
      </SysTabHeader>
      <div className="tbl-wrap" style={{ margin: '0 24px 24px' }}>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead><tr><th>Nombre</th><th>Contacto</th><th>Teléfono</th><th>Email</th><th>Familias</th><th></th></tr></thead>
            <tbody>
              {loading && <tr><td colSpan="6" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</td></tr>}
              {!loading && provs.map(p => (
                <tr key={p.id} onClick={e => { if (!e.target.closest('button')) setModal(p); }}>
                  <td className="strong">{p.nombre}</td>
                  <td className="muted">{p.contacto || '—'}</td>
                  <td className="muted">{p.telefono || '—'}</td>
                  <td className="muted truncate">{p.email || '—'}</td>
                  <td>{(p.familias || []).map(f => <span key={f} className="pill glass" style={{ marginRight: 4, fontSize: 11 }}>{f}</span>)}</td>
                  <td style={{ textAlign: 'right' }}>
                    <button className="btn ghost" style={{ marginRight: 4 }} onClick={() => setModal(p)}>Editar</button>
                    <button className="btn ghost" style={{ color: 'var(--err)' }} onClick={() => del(p)}>Eliminar</button>
                  </td>
                </tr>
              ))}
              {!loading && !provs.length && <SysEmptyRow cols={6} msg="No hay proveedores." />}
            </tbody>
          </table>
        </div>
      </div>
      {modal && (
        <ProveedorModal
          prov={modal === 'new' ? null : modal}
          onClose={() => setModal(null)}
          onSaved={() => load(search)}
          onToast={onToast}
        />
      )}
    </>
  );
};

// ─────────────── TAREAS ───────────────

const TareasTab = ({ onToast }) => {
  const [tareas, setTareas] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [modal, setModal] = React.useState(null);
  const [form, setForm] = React.useState({ nombre: '', orden: '' });
  const [saving, setSaving] = React.useState(false);

  const load = async () => {
    try { setTareas(await API.get('/api/tareas')); }
    catch (e) { onToast(e.message || 'Error', true); }
    finally { setLoading(false); }
  };
  React.useEffect(() => { load(); }, []);

  const openNew = () => {
    const nextOrden = tareas.length ? Math.max(...tareas.map(t => t.orden || 0)) + 10 : 10;
    setForm({ nombre: '', orden: String(nextOrden) });
    setModal('new');
  };

  const openEdit = (t) => {
    setForm({ nombre: t.nombre, orden: String(t.orden || 0) });
    setModal(t);
  };

  const submit = async (e) => {
    e.preventDefault();
    if (!form.nombre.trim()) return onToast('Nombre requerido', true);
    setSaving(true);
    const payload = { nombre: form.nombre.trim(), orden: Number(form.orden) || 0 };
    try {
      modal === 'new'
        ? await API.post('/api/tareas', payload)
        : await API.put(`/api/tareas/${modal.id}`, payload);
      onToast(modal === 'new' ? 'Tarea creada ✓' : 'Tarea actualizada ✓');
      setModal(null);
      load();
    } catch (e) {
      onToast(e.message || 'Error', true);
    } finally {
      setSaving(false);
    }
  };

  const toggleActiva = async (t) => {
    try { await API.put(`/api/tareas/${t.id}`, { activa: !t.activa }); onToast(t.activa ? 'Desactivada' : 'Activada'); load(); }
    catch (e) { onToast(e.message || 'Error', true); }
  };

  const del = async (t) => {
    if (!confirm(`¿Eliminar tarea "${t.nombre}"?`)) return;
    try { await API.del(`/api/tareas/${t.id}`); onToast('Eliminada'); load(); }
    catch (e) { onToast(e.message || 'Error', true); }
  };

  return (
    <>
      <SysTabHeader title="Tareas de parte">
        <button className="btn primary" onClick={openNew}><Icon name="plus" /> Nueva tarea</button>
      </SysTabHeader>
      <div className="tbl-wrap" style={{ margin: '0 24px 24px' }}>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead><tr><th style={{ width: 64 }}>Orden</th><th>Nombre</th><th>Estado</th><th></th></tr></thead>
            <tbody>
              {loading && <tr><td colSpan="4" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</td></tr>}
              {!loading && tareas.map(t => (
                <tr key={t.id} style={{ opacity: t.activa ? 1 : 0.55 }}>
                  <td className="num muted">{t.orden}</td>
                  <td className="strong">{t.nombre}</td>
                  <td><span className={`pill ${t.activa ? 'ok' : 'warn'}`}><span className="d"/>{t.activa ? 'Activa' : 'Inactiva'}</span></td>
                  <td style={{ textAlign: 'right' }}>
                    <button className="btn ghost" style={{ marginRight: 4 }} onClick={() => openEdit(t)}>Editar</button>
                    <button className="btn ghost" style={{ marginRight: 4 }} onClick={() => toggleActiva(t)}>{t.activa ? 'Desactivar' : 'Activar'}</button>
                    <button className="btn ghost" style={{ color: 'var(--err)' }} onClick={() => del(t)}>Eliminar</button>
                  </td>
                </tr>
              ))}
              {!loading && !tareas.length && <SysEmptyRow cols={4} msg="No hay tareas." />}
            </tbody>
          </table>
        </div>
      </div>

      {modal && (
        <div className="modal-bg open" {...sysOverlay(() => setModal(null))}>
          <div className="modal" {...sysModal(360)}>
            <SysModalHeader title={modal === 'new' ? 'Nueva tarea' : 'Editar tarea'} onClose={() => setModal(null)} />
            <form onSubmit={submit} autoComplete="off">
              <div style={{ padding: '16px 18px', display: 'flex', flexDirection: 'column', gap: 12 }}>
                <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
                  Nombre *
                  <input value={form.nombre} onChange={e => setForm(f => ({ ...f, nombre: e.target.value }))} required autoFocus />
                </label>
                <label style={{ display: 'flex', flexDirection: 'column', gap: 4, fontSize: 12, color: 'var(--ink-2)' }}>
                  Orden (para ordenar en partes-app)
                  <input type="number" value={form.orden} onChange={e => setForm(f => ({ ...f, orden: e.target.value }))} />
                </label>
              </div>
              <SysModalFooter onClose={() => setModal(null)} saving={saving} label={modal === 'new' ? 'Crear' : 'Guardar'} />
            </form>
          </div>
        </div>
      )}
    </>
  );
};

// ─────────────── TOKENS ───────────────

const TokensTab = ({ onToast }) => {
  const [tokens, setTokens] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [cleaning, setCleaning] = React.useState(false);
  const [estadoFilter, setEstadoFilter] = React.useState('');

  const load = async (estado) => {
    try {
      const params = estado ? { estado } : undefined;
      setTokens(await API.get('/api/partes/tokens', params));
    } catch (e) { onToast(e.message || 'Error', true); }
    finally { setLoading(false); }
  };
  React.useEffect(() => { load(estadoFilter); }, [estadoFilter]);

  const cleanup = async () => {
    if (!confirm('¿Limpiar tokens expirados y sin usar?')) return;
    setCleaning(true);
    try {
      const r = await API.del('/api/partes/tokens/cleanup');
      onToast(`${r.deleted} ${r.deleted === 1 ? 'token eliminado' : 'tokens eliminados'} ✓`);
      load(estadoFilter);
    } catch (e) {
      onToast(e.message || 'Error', true);
    } finally {
      setCleaning(false);
    }
  };

  const fmtDt = (v) => v ? new Date(v).toLocaleString('es-ES', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit' }) : '—';

  const estadoCls = { virgen: 'warn', activo: 'ok', caducado: 'err', enviado: 'info' };

  return (
    <>
      <SysTabHeader title="Tokens de partes">
        <select
          value={estadoFilter}
          onChange={e => { setEstadoFilter(e.target.value); setLoading(true); }}
          style={{ fontSize: 13, padding: '4px 8px', border: '1px solid var(--line)', borderRadius: 5, background: 'var(--surface)', color: 'var(--ink)' }}
        >
          <option value="">Todos</option>
          <option value="virgen">Virgen</option>
          <option value="activo">Activo</option>
          <option value="caducado">Caducado</option>
          <option value="enviado">Enviado</option>
        </select>
        <button className="btn ghost" onClick={cleanup} disabled={cleaning}>{cleaning ? 'Limpiando…' : 'Limpiar expirados'}</button>
      </SysTabHeader>
      <div className="tbl-wrap" style={{ margin: '0 24px 24px' }}>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead><tr><th>Token</th><th>Operario</th><th>Creado</th><th>Expira</th><th>Enviado</th><th>Estado</th></tr></thead>
            <tbody>
              {loading && <tr><td colSpan="6" style={{ padding: '24px 16px', textAlign: 'center', color: 'var(--ink-3)' }}>Cargando…</td></tr>}
              {!loading && tokens.map((t, i) => (
                <tr key={t.id || i}>
                  <td><code style={{ fontSize: 11 }}>{String(t.token || '').slice(0, 18)}…</code></td>
                  <td className="muted">{t.operario || '—'}</td>
                  <td className="muted num">{fmtDt(t.created_at)}</td>
                  <td className="muted num">{fmtDt(t.expires_at)}</td>
                  <td className="muted num">{fmtDt(t.submitted_at)}</td>
                  <td><span className={`pill ${estadoCls[t.estado] || 'warn'}`}><span className="d"/>{t.estado || 'virgen'}</span></td>
                </tr>
              ))}
              {!loading && !tokens.length && <SysEmptyRow cols={6} msg="No hay tokens con ese filtro." />}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
};

// ─────────────── PRODUCTOS (imágenes) ───────────────

const ImgPlaceholder = () => (
  <div style={{
    width: '100%', aspectRatio: '4/3', background: 'var(--surface-2)',
    border: '2px dashed var(--line)', borderRadius: 6,
    display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
    color: 'var(--ink-3)', fontSize: 12, gap: 4,
  }}>
    <span style={{ fontSize: 22, opacity: 0.5 }}>▨</span>
    <span>Sin imagen</span>
  </div>
);

const ImgGrid = ({ items, uploadUrl, nameKey, codeKey, uploading, fileRefs, onFileChange }) => (
  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(160px, 1fr))', gap: 12 }}>
    {items.map(item => (
      <div key={item.id} style={{ border: '1px solid var(--line)', borderRadius: 8, overflow: 'hidden', background: 'var(--surface)' }}>
        {item.imagen_url
          ? <img src={item.imagen_url} alt={item[nameKey]} style={{ width: '100%', aspectRatio: '4/3', objectFit: 'cover', display: 'block' }} />
          : <ImgPlaceholder />
        }
        <div style={{ padding: '8px 10px' }}>
          <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 2, lineHeight: 1.3 }}>{item[nameKey]}</div>
          {item[codeKey] && <div style={{ fontSize: 11, color: 'var(--ink-3)', marginBottom: 6 }}>{item[codeKey]}</div>}
          <input
            type="file"
            accept="image/*"
            style={{ display: 'none' }}
            ref={el => { fileRefs.current[`${uploadUrl}_${item.id}`] = el; }}
            onChange={e => onFileChange(item.id, e.target.files[0], uploadUrl)}
          />
          <button
            className="btn ghost"
            style={{ fontSize: 11, width: '100%' }}
            disabled={uploading === `${uploadUrl}_${item.id}`}
            onClick={() => fileRefs.current[`${uploadUrl}_${item.id}`]?.click()}
          >
            {uploading === `${uploadUrl}_${item.id}` ? 'Subiendo…' : item.imagen_url ? 'Cambiar foto' : 'Añadir foto'}
          </button>
        </div>
      </div>
    ))}
  </div>
);

// Galería para subir fotos a los materiales (BOM/compras). Las fotos
// de los productos vendibles se gestionan en "Catálogo de productos".
const FotosMaterialesTab = ({ onToast }) => {
  const [materiales, setMateriales] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [uploading, setUploading] = React.useState(null);
  const fileRefs = React.useRef({});

  const load = async () => {
    try {
      setMateriales(await API.get('/api/materiales'));
    } catch (e) {
      onToast(e.message || 'Error', true);
    } finally {
      setLoading(false);
    }
  };
  React.useEffect(() => { load(); }, []);

  const handleFile = async (id, file, baseUrl) => {
    if (!file) return;
    const key = `${baseUrl}_${id}`;
    setUploading(key);
    const fd = new FormData();
    fd.append('imagen', file);
    try {
      const r = await fetch(`${baseUrl}/${id}/imagen`, { method: 'POST', body: fd, credentials: 'include' });
      const json = await r.json();
      if (!r.ok) throw new Error(json.error || 'Error');
      onToast('Imagen actualizada ✓');
      load();
    } catch (e) {
      onToast(e.message || 'Error al subir', true);
    } finally {
      setUploading(null);
    }
  };

  const byCategoria = React.useMemo(() => {
    const cats = materiales.filter(m => !m.parent_id);
    const childrenOf = (catId) => materiales.filter(m => m.parent_id === catId);
    return cats.map(c => ({ cat: c, items: childrenOf(c.id) })).filter(({ items }) => items.length > 0);
  }, [materiales]);

  const conImgMateriales = materiales.filter(m => m.imagen_url).length;

  return (
    <>
      <SysTabHeader title="Fotos de materiales">
        <span className="muted" style={{ fontSize: 12 }}>{conImgMateriales}/{materiales.filter(m => m.parent_id).length} con foto</span>
      </SysTabHeader>

      <div style={{ padding: '16px 24px 24px' }}>
        {loading && <div style={{ textAlign: 'center', color: 'var(--ink-3)', padding: 32 }}>Cargando…</div>}

        {!loading && (
          byCategoria.length === 0
            ? <div style={{ textAlign: 'center', color: 'var(--ink-3)', padding: 32 }}>No hay materiales con ítems.</div>
            : byCategoria.map(({ cat, items }) => (
                <div key={cat.id} style={{ marginBottom: 28 }}>
                  <h4 style={{ margin: '0 0 12px', fontSize: 12, color: 'var(--ink-3)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '.06em' }}>
                    {cat.nombre}
                  </h4>
                  <ImgGrid
                    items={items}
                    uploadUrl="/api/materiales"
                    nameKey="nombre"
                    codeKey="codigo"
                    uploading={uploading}
                    fileRefs={fileRefs}
                    onFileChange={handleFile}
                  />
                </div>
              ))
        )}
      </div>
    </>
  );
};

// ─────────────── MAIN SISTEMA VIEW ───────────────

const SistemaView = ({ onToast }) => {
  const [tab, setTab] = React.useState('Usuarios');
  const TABS = ['Usuarios', 'Roles', 'Operarios', 'Materiales', 'Proveedores', 'Tareas', 'Tokens', 'Fotos materiales'];

  return (
    <div className="view-wrap">
      <div className="page-h" style={{ paddingBottom: 0 }}>
        <div>
          <h2 style={{ margin: 0, fontSize: 18 }}>Sistema</h2>
          <div className="sub">Configuración del sistema · solo administradores</div>
        </div>
      </div>

      <div style={{ display: 'flex', gap: 0, borderBottom: '2px solid var(--line)', padding: '0 24px', marginTop: 12 }}>
        {TABS.map(t => (
          <button
            key={t}
            onClick={() => setTab(t)}
            style={{
              background: 'none', border: 'none', cursor: 'pointer',
              padding: '8px 16px', fontSize: 13, fontWeight: tab === t ? 600 : 400,
              color: tab === t ? 'var(--accent-ink)' : 'var(--ink-2)',
              borderBottom: tab === t ? '2px solid var(--accent)' : '2px solid transparent',
              marginBottom: -2, transition: 'color .15s',
            }}
          >{t}</button>
        ))}
      </div>

      <div style={{ marginTop: 16 }}>
        {tab === 'Usuarios'    && <UsuariosTab    onToast={onToast} />}
        {tab === 'Roles'       && <RolesTab       onToast={onToast} />}
        {tab === 'Operarios'   && <OperariosTab   onToast={onToast} />}
        {tab === 'Materiales'  && <MaterialesTab  onToast={onToast} />}
        {tab === 'Proveedores' && <ProveedoresTab onToast={onToast} />}
        {tab === 'Tareas'      && <TareasTab      onToast={onToast} />}
        {tab === 'Tokens'      && <TokensTab      onToast={onToast} />}
        {tab === 'Fotos materiales' && <FotosMaterialesTab onToast={onToast} />}
      </div>
    </div>
  );
};
