// Users & permissions + Module manager + Settings.
// Backed by /api/users, /api/roles, /api/modules.

// ─────────────────────────────────────────────────────────────
//  UsersPermissions — internal team with roles + permission matrix
// ─────────────────────────────────────────────────────────────

function initials(name) {
  return (name || '?').split(/\s+/).filter(Boolean).map(p => p[0]).join('').slice(0, 2).toUpperCase();
}

function formatRelative(ts) {
  if (!ts) return 'nikdy';
  const s = Math.floor((Date.now() / 1000) - ts);
  if (s < 60) return 'teraz';
  if (s < 3600) return `pred ${Math.floor(s/60)} min`;
  if (s < 86400) return `pred ${Math.floor(s/3600)} h`;
  return `pred ${Math.floor(s/86400)} dňami`;
}

function Toast({ kind = 'info', children, onClose }) {
  React.useEffect(() => {
    const id = setTimeout(onClose, 6000);
    return () => clearTimeout(id);
  }, [onClose]);
  return (
    <div style={{
      position:'fixed', bottom:20, right:20, zIndex:2000,
      background: kind === 'error' ? 'var(--red-500)' : kind === 'success' ? 'var(--green-500)' : 'var(--navy-800)',
      color:'#fff', padding:'10px 14px', borderRadius:8, boxShadow:'var(--shadow-lg)',
      fontSize:13, fontWeight:500, maxWidth:420,
    }}>{children}</div>
  );
}

// Reusable modal
function Modal({ title, children, onClose, footer, width = 480 }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" style={{width}} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h3>{title}</h3>
          <button className="btn btn-ghost btn-xs" onClick={onClose}><Ico.x/></button>
        </div>
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-foot">{footer}</div>}
      </div>
    </div>
  );
}

// ── Create role modal ──
function CreateRoleModal({ roles, modules, onClose, onCreated, notify }) {
  const [slug, setSlug] = React.useState('');
  const [label, setLabel] = React.useState('');
  const [description, setDescription] = React.useState('');
  const [preset, setPreset] = React.useState('empty');   // empty | viewer | obchodnik | manager
  const [busy, setBusy] = React.useState(false);

  // Auto-generate slug from label
  React.useEffect(() => {
    if (slug) return;
    const s = label.toLowerCase()
      .normalize('NFD').replace(/[̀-ͯ]/g, '')
      .replace(/[^a-z0-9]+/g, '_')
      .replace(/^_+|_+$/g, '').slice(0, 24);
    setSlug(s);
    // eslint-disable-next-line
  }, [label]);

  const presets = {
    empty:      { label: 'Prázdna rola', permissions: {} },
    viewer:     { label: 'Kopírovať z: Iba čítanie',   permissions: (roles.find(r => r.slug === 'viewer')?.permissions)     || {} },
    obchodnik:  { label: 'Kopírovať z: Obchodník',     permissions: (roles.find(r => r.slug === 'obchodnik')?.permissions)  || {} },
    manager:    { label: 'Kopírovať z: Manažér',       permissions: (roles.find(r => r.slug === 'manager')?.permissions)    || {} },
  };

  async function submit() {
    if (!slug || !/^[a-z0-9_-]{2,32}$/.test(slug)) return notify('error', 'Slug: 2–32 znakov, a–z / 0–9 / _ / -');
    if (!label.trim() || label.length < 2) return notify('error', 'Vyplňte názov.');
    setBusy(true);
    const r = await API.createRole({
      slug, label: label.trim(), description,
      permissions: presets[preset].permissions,
    });
    setBusy(false);
    if (r.status === 201 || r.ok) {
      notify('success', 'Rola vytvorená.');
      onCreated(r.body.role);
      onClose();
    } else {
      notify('error',
        r.body.error === 'slug_taken' ? 'Slug už existuje.' :
        r.body.message || 'Nepodarilo sa vytvoriť.');
    }
  }

  const count = Object.values(presets[preset].permissions).filter(Boolean).length;

  return (
    <Modal
      title="Nová rola"
      width={520}
      onClose={onClose}
      footer={
        <>
          <button className="btn" onClick={onClose} disabled={busy}>Zrušiť</button>
          <button className="btn btn-primary" onClick={submit} disabled={busy || !label.trim() || !slug}>
            {busy ? 'Vytváram…' : 'Vytvoriť'}
          </button>
        </>
      }
    >
      <div className="vstack" style={{gap:12}}>
        <div className="field">
          <label>Názov roly *</label>
          <input className="input" value={label} onChange={e => setLabel(e.target.value)} placeholder="Napr. Junior obchodník" autoFocus/>
        </div>
        <div className="field">
          <label>Slug (identifikátor) *</label>
          <input className="input mono" value={slug} onChange={e => setSlug(e.target.value.toLowerCase().replace(/[^a-z0-9_-]/g, ''))} placeholder="junior_obchodnik" maxLength={32}/>
          <div className="small muted">Technické meno (a–z, 0–9, _, -). Nedá sa neskôr zmeniť.</div>
        </div>
        <div className="field">
          <label>Popis</label>
          <textarea className="textarea" value={description} onChange={e => setDescription(e.target.value)} rows={2} placeholder="Na čo sa používa, aká pozícia…"/>
        </div>
        <div className="field">
          <label>Počiatočné práva</label>
          <select className="select" value={preset} onChange={e => setPreset(e.target.value)}>
            {Object.entries(presets).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
          </select>
          <div className="small muted">Po vytvorení budete môcť jemne doladiť v matici práv. Počiatočných povolení: <b>{count}</b></div>
        </div>
      </div>
    </Modal>
  );
}

// ── Edit role metadata (label + description) ──
function EditRoleModal({ role, onClose, onSaved, notify }) {
  const [label, setLabel] = React.useState(role.label || '');
  const [description, setDescription] = React.useState(role.description || '');
  const [busy, setBusy] = React.useState(false);

  async function save() {
    if (!label.trim() || label.length < 2) return notify('error', 'Vyplňte názov (min. 2 znaky).');
    setBusy(true);
    const r = await API.updateRole(role.slug, { label: label.trim(), description });
    setBusy(false);
    if (r.ok) { notify('success', 'Uložené.'); onSaved(); onClose(); }
    else notify('error', r.body.message || 'Nepodarilo sa uložiť.');
  }

  return (
    <Modal
      title={`Upraviť rolu: ${role.label}`}
      width={480}
      onClose={onClose}
      footer={
        <>
          <button className="btn" onClick={onClose} disabled={busy}>Zrušiť</button>
          <button className="btn btn-primary" onClick={save} disabled={busy || !label.trim()}>
            {busy ? 'Ukladám…' : 'Uložiť'}
          </button>
        </>
      }
    >
      <div className="vstack" style={{gap:12}}>
        <div className="field">
          <label>Slug (nezmeniteľný)</label>
          <input className="input mono" value={role.slug} readOnly style={{background:'var(--bg)'}}/>
        </div>
        <div className="field">
          <label>Názov *</label>
          <input className="input" value={label} onChange={e => setLabel(e.target.value)} autoFocus/>
        </div>
        <div className="field">
          <label>Popis</label>
          <textarea className="textarea" value={description} onChange={e => setDescription(e.target.value)} rows={3}/>
        </div>
        <div className="small muted">
          Zmena <b>slug</b>-u nie je podporovaná — ak chcete iný identifikátor, vytvorte novú rolu a starú zmažte (platí iba pre non-system role).
        </div>
      </div>
    </Modal>
  );
}

function InviteUserModal({ roles, onClose, onCreated }) {
  const [email, setEmail] = React.useState('');
  const [name, setName] = React.useState('');
  const [role, setRole] = React.useState('viewer');
  const [submitting, setSubmitting] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [tempPwd, setTempPwd] = React.useState(null);

  async function submit(e) {
    e.preventDefault();
    setSubmitting(true); setErr('');
    const r = await API.createUser({ email: email.trim(), name: name.trim(), role });
    setSubmitting(false);
    if (r.status === 201 || r.ok) {
      setTempPwd(r.body.tempPassword || '—');
      onCreated && onCreated(r.body.user);
    } else {
      const map = {
        email_taken: 'Tento e-mail už je zaregistrovaný.',
        invalid_email: 'Neplatný e-mail.',
        invalid_name: 'Meno musí mať aspoň 2 znaky.',
        invalid_role: 'Neplatná rola.',
        forbidden: 'Nemáte oprávnenie pridávať používateľov.',
      };
      setErr(map[r.body.error] || 'Nepodarilo sa vytvoriť používateľa.');
    }
  }

  return (
    <Modal
      title="Pozvať používateľa"
      onClose={onClose}
      footer={tempPwd ? (
        <button className="btn btn-primary" onClick={onClose}>Zavrieť</button>
      ) : (
        <>
          <button className="btn" onClick={onClose} disabled={submitting}>Zrušiť</button>
          <button className="btn btn-primary" onClick={submit} disabled={submitting}>
            {submitting ? 'Vytváram…' : 'Vytvoriť'}
          </button>
        </>
      )}>
      {tempPwd ? (
        <div className="vstack" style={{gap:10}}>
          <div className="auth-alert" style={{background:'var(--green-100)', color:'#135e37', borderColor:'rgba(42,157,95,0.3)'}}>
            <Ico.check/> <span>Používateľ <b>{email}</b> bol vytvorený.</span>
          </div>
          <div className="field">
            <label>Dočasné heslo (odovzdajte ho používateľovi)</label>
            <div className="hstack">
              <input className="input mono" value={tempPwd} readOnly style={{flex:1, fontSize:13}}/>
              <button className="btn btn-sm" onClick={() => navigator.clipboard && navigator.clipboard.writeText(tempPwd)}>Kopírovať</button>
            </div>
            <div className="small muted" style={{marginTop:4}}>Heslo sa tu zobrazí naposledy — v logoch sa neuchováva. Používateľ ho musí pri prvom prihlásení zmeniť.</div>
          </div>
        </div>
      ) : (
        <form className="vstack" style={{gap:12}} onSubmit={submit}>
          {err && <div className="auth-alert" role="alert"><Ico.dots/> <span>{err}</span></div>}
          <div className="field">
            <label>Meno a priezvisko</label>
            <input className="input" value={name} onChange={e => setName(e.target.value)} placeholder="Napr. Mária Nováková" required autoFocus/>
          </div>
          <div className="field">
            <label>E-mail</label>
            <input className="input" type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="meno@ddenergy.sk" required/>
          </div>
          <div className="field">
            <label>Rola</label>
            <select className="select" value={role} onChange={e => setRole(e.target.value)}>
              {roles.map(r => <option key={r.slug} value={r.slug}>{r.label}</option>)}
            </select>
          </div>
          <div className="small muted">Vygenerujeme dočasné heslo, ktoré odovzdáte používateľovi. Pri prvom prihlásení si ho musí zmeniť.</div>
        </form>
      )}
    </Modal>
  );
}

function UsersPermissions() {
  const [users, setUsers] = React.useState([]);
  const [roles, setRoles] = React.useState([]);
  const [modules, setModules] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [selectedRole, setSelectedRole] = React.useState(null);
  const [showInvite, setShowInvite] = React.useState(false);
  const [showNewRole, setShowNewRole] = React.useState(false);
  const [editingRole, setEditingRole] = React.useState(null);
  const [toast, setToast] = React.useState(null);
  const [busyId, setBusyId] = React.useState(null);
  const [resetResult, setResetResult] = React.useState(null); // { user, password }
  const [skillAccessUser, setSkillAccessUser] = React.useState(null);
  const canEdit = window.hasPerm ? window.hasPerm('users.edit') : true;
  const canCreate = window.hasPerm ? window.hasPerm('users.create') : true;
  const canDelete = window.hasPerm ? window.hasPerm('users.delete') : true;

  async function reload() {
    setLoading(true);
    const [u, r, m] = await Promise.all([API.listUsers(), API.listRoles(), API.listModules()]);
    setUsers(u); setRoles(r); setModules(m);
    if (!selectedRole && r.length) setSelectedRole(r.find(x => x.slug === 'manager') || r[0]);
    setLoading(false);
  }
  React.useEffect(() => { reload(); }, []);

  async function changeRole(user, newRole) {
    setBusyId(user.id);
    const r = await API.updateUser(user.id, { role: newRole });
    setBusyId(null);
    if (r.ok) { setToast({ kind: 'success', text: `Rola aktualizovaná: ${newRole}` }); reload(); }
    else setToast({ kind: 'error', text: r.body.error === 'last_admin' ? 'Nedá sa zmeniť — zostal by len jeden admin.' : 'Nepodarilo sa uložiť.' });
  }
  async function toggleActive(user) {
    setBusyId(user.id);
    const r = await API.updateUser(user.id, { is_active: !user.is_active });
    setBusyId(null);
    if (r.ok) { setToast({ kind: 'success', text: user.is_active ? 'Používateľ deaktivovaný.' : 'Používateľ aktivovaný.' }); reload(); }
    else setToast({ kind: 'error', text: r.body.error === 'cannot_disable_self' ? 'Nemôžete deaktivovať seba.' : r.body.error === 'last_admin' ? 'Zostal by len jeden admin.' : 'Chyba.' });
  }
  async function resetPassword(user) {
    if (!confirm(`Vygenerovať nové dočasné heslo pre ${user.email}? Všetky jeho relácie sa odhlásia.`)) return;
    const r = await API.resetPassword(user.id);
    if (r.ok) setResetResult({ user, password: r.body.tempPassword });
    else setToast({ kind: 'error', text: 'Nepodarilo sa resetnúť heslo.' });
  }
  async function removeUser(user) {
    if (!confirm(`Naozaj chcete odstrániť ${user.name} (${user.email})?`)) return;
    setBusyId(user.id);
    const r = await API.deleteUser(user.id);
    setBusyId(null);
    if (r.ok) { setToast({ kind: 'success', text: 'Používateľ odstránený.' }); reload(); }
    else setToast({ kind: 'error', text: r.body.error === 'last_admin' ? 'Zostal by len jeden admin.' : r.body.error === 'cannot_delete_self' ? 'Nemôžete odstrániť seba.' : 'Chyba.' });
  }
  const [savingCells, setSavingCells] = React.useState(new Set()); // `${slug}:${perm}`
  function markSaving(slug, perm, on) {
    setSavingCells(prev => {
      const n = new Set(prev);
      const k = `${slug}:${perm}`;
      on ? n.add(k) : n.delete(k);
      return n;
    });
  }
  async function togglePerm(roleSlug, perm) {
    if (!canEdit) return;
    const role = roles.find(r => r.slug === roleSlug);
    if (!role) return;

    // Optimistic update
    const next = { ...role.permissions };
    next[perm] = !next[perm];   // flips true ↔ false/undefined
    setRoles(rs => rs.map(r => r.slug === roleSlug ? { ...r, permissions: next } : r));
    if (selectedRole && selectedRole.slug === roleSlug) setSelectedRole({ ...selectedRole, permissions: next });
    markSaving(roleSlug, perm, true);

    const r = await API.updateRole(roleSlug, { permissions: next });
    markSaving(roleSlug, perm, false);
    if (!r.ok) {
      // revert on failure
      setRoles(rs => rs.map(r => r.slug === roleSlug ? role : r));
      if (selectedRole && selectedRole.slug === roleSlug) setSelectedRole(role);
      setToast({
        kind: 'error',
        text: r.body.error === 'admin_requires_wildcard' ? 'Admin musí mať všetky práva (wildcard).' : 'Nepodarilo sa uložiť.'
      });
    }
  }

  // Batch: toggle all actions for one module in a role
  async function setModuleAll(roleSlug, moduleSlug, value) {
    if (!canEdit) return;
    const role = roles.find(r => r.slug === roleSlug);
    if (!role) return;
    const next = { ...role.permissions };
    for (const a of ['view', 'edit', 'create', 'delete']) {
      next[`${moduleSlug}.${a}`] = value;
    }
    setRoles(rs => rs.map(r => r.slug === roleSlug ? { ...r, permissions: next } : r));
    if (selectedRole && selectedRole.slug === roleSlug) setSelectedRole({ ...selectedRole, permissions: next });
    const r = await API.updateRole(roleSlug, { permissions: next });
    if (!r.ok) { setRoles(rs => rs.map(r => r.slug === roleSlug ? role : r)); setToast({ kind: 'error', text: 'Chyba.' }); }
  }

  const actions = [
    { k: 'view',   label: 'Zobr.',  title: 'Zobrazenie' },
    { k: 'edit',   label: 'Upr.',   title: 'Úprava' },
    { k: 'create', label: 'Vytv.',  title: 'Vytváranie' },
    { k: 'delete', label: 'Zmaz.',  title: 'Mazanie' },
  ];
  const visibleModules = modules.filter(m => m.slug !== 'settings');

  function renderCell(role, module, action) {
    const perm = `${module.slug}.${action}`;
    const isWild = role.permissions['*'] === true;
    const val = isWild ? true : role.permissions[perm] === true;
    const isAdmin = role.slug === 'admin';
    const disabled = isWild || !canEdit || isAdmin;
    const saving = savingCells.has(`${role.slug}:${perm}`);
    return (
      <td key={action} style={{padding:'4px 6px'}}>
        <button
          type="button"
          onClick={() => !disabled && togglePerm(role.slug, perm)}
          disabled={disabled}
          className={`perm-cell ${val ? 'on' : 'off'}${disabled ? ' disabled' : ''}`}
          title={
            isAdmin ? 'Admin rola má všetky práva automaticky' :
            disabled ? '' :
            (val ? 'Kliknite pre odobratie' : 'Kliknite pre povolenie')
          }
        >
          {saving ? <span className="spin" style={{display:'inline-block'}}>⋯</span>
                  : (val ? '✓ Áno' : '✗ Nie')}
        </button>
      </td>
    );
  }

  if (loading) {
    return <AppShell active="users" crumbs={['Administrácia', 'Používatelia a práva']}><div className="page-head"><div><h1>Načítavam…</h1></div></div></AppShell>;
  }

  const activeCount = users.filter(u => u.is_active).length;

  return (
    <AppShell active="users" crumbs={['Administrácia', 'Používatelia a práva']}>
      {toast && <Toast kind={toast.kind} onClose={() => setToast(null)}>{toast.text}</Toast>}
      {showInvite && (
        <InviteUserModal
          roles={roles}
          onClose={() => setShowInvite(false)}
          onCreated={() => reload()}
        />
      )}
      {showNewRole && (
        <CreateRoleModal
          roles={roles}
          modules={modules}
          onClose={() => setShowNewRole(false)}
          onCreated={(newRole) => {
            reload();
            setSelectedRole(newRole);
          }}
          notify={(k, t) => setToast({ kind: k, text: t })}
        />
      )}
      {editingRole && (
        <EditRoleModal
          role={editingRole}
          onClose={() => setEditingRole(null)}
          onSaved={async () => {
            await reload();
            // re-resolve selectedRole to fresh label/description
            setSelectedRole(r => r && r.slug === editingRole.slug ? { ...r } : r);
          }}
          notify={(k, t) => setToast({ kind: k, text: t })}
        />
      )}
{skillAccessUser && (
        <SkillAccessModal
          user={skillAccessUser}
          onClose={() => setSkillAccessUser(null)}
          notify={(k, t) => setToast({ kind: k, text: t })}
        />
      )}
      {resetResult && (
        <Modal
          title={`Nové heslo pre ${resetResult.user.email}`}
          onClose={() => setResetResult(null)}
          footer={<button className="btn btn-primary" onClick={() => setResetResult(null)}>Zavrieť</button>}
          width={480}
        >
          <div className="auth-alert" style={{background:'var(--amber-100)', color:'#7d5500', borderColor:'rgba(243,167,18,0.3)'}}>
            <Ico.shield/> <span>Používateľ bude pri ďalšom prihlásení vyzvaný zmeniť heslo. Odovzdajte heslo cez bezpečný kanál.</span>
          </div>
          <div className="field" style={{marginTop:12}}>
            <label>Dočasné heslo</label>
            <div className="hstack">
              <input className="input mono" value={resetResult.password} readOnly style={{flex:1, fontSize:14}}/>
              <button className="btn btn-sm" onClick={() => navigator.clipboard && navigator.clipboard.writeText(resetResult.password)}>Kopírovať</button>
            </div>
            <div className="small muted" style={{marginTop:4}}>Všetky aktívne relácie boli odhlásené. V logoch ERP sa heslo neukladá.</div>
          </div>
        </Modal>
      )}

      <div className="page-head">
        <div>
          <h1>Používatelia a práva</h1>
          <p>{activeCount} aktívnych · {roles.length} rolí · granular permissions s override na používateľa</p>
        </div>
        <div className="page-head-actions">
          <button className="btn" onClick={reload}><Ico.refresh/>Obnoviť</button>
          {canEdit && <button className="btn" onClick={() => setShowNewRole(true)}><Ico.plus/>Nová rola</button>}
          {canCreate && <button className="btn btn-primary" onClick={() => setShowInvite(true)}><Ico.plus/>Pozvať používateľa</button>}
        </div>
      </div>

      <div style={{display:'grid', gridTemplateColumns:'1.4fr 1fr', gap:14}}>
        <div className="card" style={{overflow:'hidden'}}>
          <div className="card-head">
            <h3>Používatelia ({users.length})</h3>
          </div>
          <div style={{overflow:'auto'}}>
            <table className="tbl">
              <thead><tr><th>Meno / e-mail</th><th>Rola</th><th>Posledné prihl.</th><th>2FA</th><th>Stav</th><th></th></tr></thead>
              <tbody>
                {users.map(u => (
                  <tr key={u.id} style={busyId === u.id ? { opacity: 0.5 } : {}}>
                    <td>
                      <div className="hstack">
                        <div className="sb-avatar" style={{width:28, height:28, fontSize:11}}>{initials(u.name)}</div>
                        <div>
                          <div style={{fontWeight:500}}>{u.name}</div>
                          <div className="small muted">{u.email}</div>
                        </div>
                      </div>
                    </td>
                    <td>
                      {canEdit ? (
                        <select
                          className="select"
                          value={u.role}
                          style={{height:26, fontSize:11.5, padding:'0 6px'}}
                          onChange={e => changeRole(u, e.target.value)}
                          disabled={busyId === u.id}
                        >
                          {roles.map(r => <option key={r.slug} value={r.slug}>{r.label}</option>)}
                        </select>
                      ) : <span className="chip">{roles.find(r => r.slug === u.role)?.label || u.role}</span>}
                    </td>
                    <td className="small muted">{formatRelative(u.last_login_at)}</td>
                    <td>{u.has_2fa ? <Ico.check style={{color:'var(--green-500)'}}/> : <span className="chip amber">vyžiadať</span>}</td>
                    <td>{u.is_active ? <span className="chip green dot">Aktívny</span> : <span className="chip gray dot">Deaktivovaný</span>}</td>
                    <td style={{textAlign:'right', whiteSpace:'nowrap'}}>
                      {canEdit && (
                        <button className="btn btn-xs" onClick={() => setSkillAccessUser(u)} title="Prístupy k AI skillom"><Ico.sparkles/></button>
                      )}
                      {canEdit && (
                        <button className="btn btn-xs" style={{marginLeft:4}} onClick={() => resetPassword(u)} title="Resetovať heslo"><Ico.lock/></button>
                      )}
                      {canEdit && (
                        <button className="btn btn-xs" style={{marginLeft:4}} onClick={() => toggleActive(u)} disabled={busyId === u.id}>
                          {u.is_active ? 'Deaktivovať' : 'Aktivovať'}
                        </button>
                      )}
                      {canDelete && (
                        <button className="btn btn-xs btn-danger" style={{marginLeft:4}} onClick={() => removeUser(u)} disabled={busyId === u.id}><Ico.x/></button>
                      )}
                    </td>
                  </tr>
                ))}
                {users.length === 0 && (
                  <tr><td colSpan={6} style={{textAlign:'center', padding:30, color:'var(--ink-500)'}}>Zatiaľ žiadni používatelia.</td></tr>
                )}
              </tbody>
            </table>
          </div>
        </div>

        <div className="card">
          <div className="card-head">
            <h3>Matica práv</h3>
            <div className="hstack" style={{gap:6}}>
              <select
                className="select"
                value={selectedRole ? selectedRole.slug : ''}
                onChange={e => setSelectedRole(roles.find(r => r.slug === e.target.value))}
                style={{height:26, fontSize:11.5}}
              >
                {roles.map(r => <option key={r.slug} value={r.slug}>{r.label}</option>)}
              </select>
              {canEdit && selectedRole && (
                <button
                  className="btn btn-xs"
                  style={{height:26, padding:'0 8px'}}
                  onClick={() => setEditingRole(selectedRole)}
                  title="Upraviť názov a popis roly"
                >Upraviť</button>
              )}
              {canEdit && selectedRole && !selectedRole.is_system && (
                <button
                  className="btn btn-xs btn-danger"
                  style={{height:26, padding:'0 8px'}}
                  disabled={(selectedRole.users_count || 0) > 0}
                  title={(selectedRole.users_count || 0) > 0
                    ? 'Rolu nemožno zmazať — používa ju ' + selectedRole.users_count + ' používateľ(ov)'
                    : 'Zmazať rolu'}
                  onClick={async () => {
                    if (!confirm(`Naozaj zmazať rolu "${selectedRole.label}"?`)) return;
                    const r = await API.deleteRole(selectedRole.slug);
                    if (r.ok) {
                      setToast({ kind:'success', text:'Rola zmazaná.' });
                      reload();
                      setSelectedRole(roles.find(rr => rr.slug === 'admin') || null);
                    } else {
                      setToast({ kind:'error', text:
                        r.body.error === 'role_in_use' ? `Rolu používa ${r.body.count} používateľov.` :
                        r.body.error === 'system_role' ? 'Systémovú rolu nemožno zmazať.' :
                        r.body.message || 'Chyba.'
                      });
                    }
                  }}
                >Zmazať</button>
              )}
            </div>
          </div>
          {selectedRole && (
            <div style={{padding:'10px 14px'}}>
              <div className="small muted" style={{marginBottom:8}}>
                <b style={{color:'var(--ink-900)'}}>{selectedRole.label}</b> — {selectedRole.description || '—'}
                {selectedRole.is_system && <span className="chip navy" style={{marginLeft:6, padding:'1px 6px', fontSize:10}}>system</span>}
                {selectedRole.permissions['*'] === true && <span className="chip amber" style={{marginLeft:6, padding:'1px 6px', fontSize:10}}>wildcard</span>}
                <div style={{marginTop:4}}>Používatelia s touto rolou: <b>{selectedRole.users_count}</b></div>
              </div>
              <div style={{overflow:'auto'}}>
                <table className="perm-matrix">
                  <thead>
                    <tr>
                      <th className="lbl">Modul</th>
                      {actions.map(a => <th key={a.k} title={a.title}>{a.label}</th>)}
                      <th style={{textAlign:'right'}}>Skratky</th>
                    </tr>
                  </thead>
                  <tbody>
                    {visibleModules.map(m => {
                      const allOn = !selectedRole.permissions['*']
                        ? actions.every(a => selectedRole.permissions[`${m.slug}.${a.k}`] === true)
                        : true;
                      const anyOn = !selectedRole.permissions['*']
                        ? actions.some(a => selectedRole.permissions[`${m.slug}.${a.k}`] === true)
                        : true;
                      return (
                        <tr key={m.slug}>
                          <td className="lbl" style={{fontWeight:500, opacity: m.is_enabled ? 1 : 0.4}}>
                            {m.label}
                            {!m.is_enabled && <span className="small muted"> (vypnutý modul)</span>}
                          </td>
                          {actions.map(a => renderCell(selectedRole, m, a.k))}
                          <td style={{textAlign:'right', whiteSpace:'nowrap'}}>
                            {!selectedRole.permissions['*'] && canEdit && selectedRole.slug !== 'admin' && (
                              <>
                                <button
                                  type="button"
                                  className="btn btn-xs"
                                  style={{padding:'0 6px', height:22}}
                                  onClick={() => setModuleAll(selectedRole.slug, m.slug, true)}
                                  title="Povoliť všetky akcie tohto modulu"
                                  disabled={allOn}
                                >Všetko</button>
                                <button
                                  type="button"
                                  className="btn btn-xs"
                                  style={{marginLeft:4, padding:'0 6px', height:22}}
                                  onClick={() => setModuleAll(selectedRole.slug, m.slug, false)}
                                  title="Odobrať všetky akcie"
                                  disabled={!anyOn}
                                >Nič</button>
                              </>
                            )}
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
              <div className="divider"/>
              <div className="small muted" style={{textAlign:'center'}}>
                Zmeny sa ukladajú okamžite · <b>Skratky</b> nastavia všetky 4 akcie v module naraz
              </div>
            </div>
          )}
        </div>
      </div>
    </AppShell>
  );
}

// ─────────────────────────────────────────────────────────────
//  ModuleManager — on/off + description + config
// ─────────────────────────────────────────────────────────────
function ModuleManager() {
  const [modules, setModules] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [toast, setToast] = React.useState(null);
  const [busy, setBusy] = React.useState(null);
  const canEdit = window.hasPerm ? window.hasPerm('modules.edit') : true;

  const iconFor = {
    dashboard: <Ico.dashboard/>, orders: <Ico.orders/>, products: <Ico.products/>,
    pricing: <Ico.pricing/>, b2b: <Ico.b2b/>, datasheets: <Ico.datasheet/>,
    gallery: <Ico.gallery/>, ai: <Ico.ai/>, calendar: <Ico.construction/>,
    monitoring: <Ico.construction/>, users_customers: <Ico.users/>,
    users: <Ico.users/>, modules: <Ico.modules/>, settings: <Ico.settings/>,
  };

  async function reload() {
    setLoading(true);
    setModules(await API.listModules());
    setLoading(false);
  }
  React.useEffect(() => { reload(); }, []);

  async function toggle(m) {
    if (!canEdit) return setToast({ kind:'error', text:'Nemáte oprávnenie.' });
    if (m.is_core && m.is_enabled) return setToast({ kind:'error', text:'Jadrový modul nemožno vypnúť.' });
    setBusy(m.slug);
    const r = await API.toggleModule(m.slug, !m.is_enabled);
    setBusy(null);
    if (r.ok) {
      setToast({ kind:'success', text: `${m.label}: ${m.is_enabled ? 'vypnutý' : 'zapnutý'}` });
      // Reload the modules list for this screen + notify App to refresh the sidebar.
      await reload();
      // Push fresh modules into global session so Sidebar hides/shows immediately.
      window.dispatchEvent(new CustomEvent('dd-modules-changed'));
    }
    else setToast({ kind:'error', text: r.body.error === 'core_module' ? 'Jadrový modul.' : 'Chyba.' });
  }

  const enabled = modules.filter(m => m.is_enabled).length;

  return (
    <AppShell active="modules" crumbs={['Administrácia', 'Moduly']}>
      {toast && <Toast kind={toast.kind} onClose={() => setToast(null)}>{toast.text}</Toast>}

      <div className="page-head">
        <div>
          <h1>Moduly</h1>
          <p>Aktivujte alebo deaktivujte moduly pre celú firmu · {enabled} aktívnych z {modules.length}</p>
        </div>
      </div>

      {loading ? <div>Načítavam…</div> : (
        <div style={{display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(260px, 1fr))', gap:12}}>
          {modules.map(m => (
            <div key={m.slug} className={`mod-tile ${m.is_enabled ? '' : 'off'}`}>
              <div className="hstack">
                <div className="mod-ico">{iconFor[m.slug] || <Ico.modules/>}</div>
                <div style={{flex:1, minWidth:0}}>
                  <h4>{m.label}</h4>
                  <div className="small muted" style={{marginTop:2}}>
                    {m.is_core && <span className="chip navy" style={{marginRight:4, padding:'0 6px', fontSize:10}}>core</span>}
                    {m.is_enabled ? 'Aktívny modul' : 'Neaktívny'}
                  </div>
                </div>
                <div
                  className={`toggle ${m.is_enabled ? 'on' : ''}`}
                  onClick={() => !busy && toggle(m)}
                  style={{cursor: (canEdit && !m.is_core) || !m.is_enabled ? 'pointer' : 'not-allowed', opacity: busy === m.slug ? 0.5 : 1}}
                  title={m.is_core ? 'Jadrový modul — nemožno vypnúť' : ''}
                />
              </div>
              <p>{m.description}</p>
            </div>
          ))}
        </div>
      )}
    </AppShell>
  );
}

// ─────────────────────────────────────────────────────────────
//  Settings — Profile / Security / Notifications / Integrations
// ─────────────────────────────────────────────────────────────
function Settings2FA() {
  const [tab, setTab] = React.useState('profile');
  const [toast, setToast] = React.useState(null);
  const s = window.currentSession || {};
  const user = s.user || {};

  const canEditBranding = window.hasPerm ? window.hasPerm('integrations.edit') : (user.role === 'admin');
  const tabs = [
    { id: 'profile',       label: 'Profil' },
    { id: 'security',      label: 'Bezpečnosť' },
    { id: 'notifications', label: 'Notifikácie' },
    ...(canEditBranding ? [{ id: 'branding', label: 'Vzhľad' }] : []),
    { id: 'integrations',  label: 'Integrácie' },
  ];

  return (
    <AppShell active="settings" crumbs={['Nastavenia', tabs.find(t => t.id === tab)?.label || '']}>
      {toast && <Toast kind={toast.kind} onClose={() => setToast(null)}>{toast.text}</Toast>}

      <div className="page-head">
        <div><h1>Nastavenia účtu</h1><p>Profil, bezpečnosť, notifikácie, prepojenia</p></div>
      </div>

      <div style={{display:'grid', gridTemplateColumns:'200px 1fr', gap:14}}>
        <div className="card" style={{padding:'8px 6px', height:'fit-content'}}>
          <div className="sb-nav" style={{padding:0}}>
            {tabs.map(t => (
              <a
                key={t.id}
                onClick={() => setTab(t.id)}
                className={tab === t.id ? 'active' : ''}
                style={{
                  cursor:'pointer',
                  color:     tab === t.id ? 'var(--navy-900)' : 'var(--ink-700)',
                  background:tab === t.id ? 'var(--blue-100)' : 'transparent',
                  boxShadow: tab === t.id ? 'inset 2px 0 0 var(--amber-500)' : 'none',
                  borderColor:'transparent',
                }}
              ><span>{t.label}</span></a>
            ))}
          </div>
        </div>

        <div className="vstack" style={{gap:14}}>
          {tab === 'profile'       && <SettingsProfile user={user} notify={(k,t) => setToast({kind:k, text:t})}/>}
          {tab === 'security'      && <SettingsSecurity notify={(k,t) => setToast({kind:k, text:t})}/>}
          {tab === 'notifications' && <SettingsNotifications notify={(k,t) => setToast({kind:k, text:t})}/>}
          {tab === 'branding'      && <SettingsBranding notify={(k,t) => setToast({kind:k, text:t})}/>}
          {tab === 'integrations'  && <SettingsIntegrations/>}
        </div>
      </div>
    </AppShell>
  );
}

// ── Profile tab ─────────────────────────────────────────────
function SettingsProfile({ user, notify }) {
  const [name, setName]   = React.useState(user.name || '');
  const [email, setEmail] = React.useState(user.email || '');
  const [busy, setBusy]   = React.useState(false);

  async function save(e) {
    e.preventDefault();
    setBusy(true);
    const r = await API.updateProfile({ name, email });
    setBusy(false);
    if (r.ok) {
      notify('success', 'Profil uložený.');
      window.dispatchEvent(new CustomEvent('dd-session-changed'));
    } else {
      notify('error',
        r.body.error === 'email_taken' ? 'E-mail používa iný používateľ.' :
        (r.body.message || 'Chyba.'));
    }
  }

  return (
    <>
      <div className="card">
        <div className="card-head"><h3>Môj účet</h3></div>
        <div className="card-body">
          <div className="hstack" style={{gap:12}}>
            <div className="sb-avatar" style={{width:48, height:48, fontSize:18}}>{initials(name)}</div>
            <div>
              <div style={{fontSize:15, fontWeight:600}}>{name || '—'}</div>
              <div className="small muted">{email || '—'} · rola <b>{user.role_label || user.role || '—'}</b></div>
            </div>
          </div>
        </div>
      </div>

      <div className="card">
        <div className="card-head"><h3>Upraviť profil</h3></div>
        <form className="card-body" onSubmit={save}>
          <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:10}}>
            <div className="field">
              <label>Meno a priezvisko</label>
              <input className="input" value={name} onChange={e => setName(e.target.value)} required minLength={2}/>
            </div>
            <div className="field">
              <label>E-mail (prihlasovací)</label>
              <input className="input" type="email" value={email} onChange={e => setEmail(e.target.value)} required/>
            </div>
          </div>
          <div className="hstack" style={{marginTop:10}}>
            <div className="small muted">Zmeny sa prejavia okamžite v sidebar-i a na všetkých obrazovkách.</div>
            <button className="btn btn-primary btn-sm" style={{marginLeft:'auto'}} type="submit" disabled={busy || !name.trim() || !email.trim()}>
              {busy ? 'Ukladám…' : 'Uložiť profil'}
            </button>
          </div>
        </form>
      </div>
    </>
  );
}

// ── Security tab — password + 2FA ──────────────────────────
function SettingsSecurity({ notify }) {
  const [current, setCurrent] = React.useState('');
  const [next, setNext] = React.useState('');
  const [confirm, setConfirm] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [msg, setMsg] = React.useState(null);

  const [twofaEnabled, setTwofaEnabled]   = React.useState(null); // null = loading
  const [setupData, setSetupData]         = React.useState(null); // { secret, qr_data_url }
  const [setupCode, setSetupCode]         = React.useState('');
  const [disablePwd, setDisablePwd]       = React.useState('');
  const [disableCode, setDisableCode]     = React.useState('');

  async function loadStatus() {
    const r = await API.twofaStatus();
    setTwofaEnabled(r.ok ? !!r.body.enabled : false);
  }
  React.useEffect(() => { loadStatus(); }, []);

  async function changePwd(e) {
    e.preventDefault();
    setMsg(null);
    if (next.length < 12) return setMsg({ kind:'error', text:'Nové heslo musí mať aspoň 12 znakov.' });
    if (next !== confirm) return setMsg({ kind:'error', text:'Potvrdenie sa nezhoduje.' });
    setBusy(true);
    const r = await API.changePassword(current, next);
    setBusy(false);
    if (r.ok) { setMsg({ kind:'success', text:'Heslo zmenené. Ostatné relácie boli odhlásené.' }); setCurrent(''); setNext(''); setConfirm(''); }
    else setMsg({ kind:'error', text: r.body.error === 'wrong_password' ? 'Aktuálne heslo je nesprávne.' : r.body.message || 'Chyba.' });
  }

  async function startSetup() {
    setBusy(true);
    const r = await API.twofaSetup();
    setBusy(false);
    if (r.ok) setSetupData(r.body);
    else notify('error', r.body.message || 'Chyba.');
  }
  async function confirmEnable() {
    setBusy(true);
    const r = await API.twofaEnable(setupCode);
    setBusy(false);
    if (r.ok) {
      notify('success', '2FA zapnuté. Pri ďalšom prihlásení budete zadávať kód.');
      setSetupData(null); setSetupCode('');
      setTwofaEnabled(true);
    } else {
      notify('error', r.body.error === 'invalid_code' ? 'Zlý kód — skontrolujte aplikáciu a skúste znova.' : (r.body.message || 'Chyba.'));
    }
  }
  async function disable() {
    setBusy(true);
    const r = await API.twofaDisable(disablePwd, disableCode);
    setBusy(false);
    if (r.ok) {
      notify('success', '2FA vypnuté.');
      setDisablePwd(''); setDisableCode('');
      setTwofaEnabled(false);
    } else {
      notify('error',
        r.body.error === 'wrong_password' ? 'Zlé heslo.' :
        r.body.error === 'invalid_code'   ? 'Zlý kód.' :
        (r.body.message || 'Chyba.'));
    }
  }

  return (
    <>
      <div className="card">
        <div className="card-head"><h3>Zmena hesla</h3></div>
        <form className="card-body" onSubmit={changePwd}>
          {msg && (
            <div className="auth-alert"
              style={msg.kind === 'success' ? {background:'var(--green-100)', color:'#135e37', borderColor:'rgba(42,157,95,0.3)'} : {}}
              role="alert">
              {msg.kind === 'success' ? <Ico.check/> : <Ico.dots/>} <span>{msg.text}</span>
            </div>
          )}
          <div style={{display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:10, alignItems:'end'}}>
            <div className="field"><label>Aktuálne heslo</label><input className="input" type="password" value={current} onChange={e => setCurrent(e.target.value)} autoComplete="current-password" required/></div>
            <div className="field"><label>Nové heslo</label><input className="input" type="password" value={next} onChange={e => setNext(e.target.value)} autoComplete="new-password" minLength={12} required/></div>
            <div className="field"><label>Potvrdiť nové</label><input className="input" type="password" value={confirm} onChange={e => setConfirm(e.target.value)} autoComplete="new-password" required/></div>
            <div style={{gridColumn:'span 3'}} className="hstack">
              <div className="small muted">Min. 12 znakov. Po zmene sa odhlásia všetky ostatné relácie.</div>
              <button className="btn btn-primary btn-sm" style={{marginLeft:'auto'}} type="submit" disabled={busy}>
                {busy ? 'Ukladám…' : 'Zmeniť heslo'}
              </button>
            </div>
          </div>
        </form>
      </div>

      <div className="card">
        <div className="card-head">
          <h3>Dvojfaktorové overenie (2FA)</h3>
          {twofaEnabled === true && <span className="chip green dot">Zapnuté</span>}
          {twofaEnabled === false && <span className="chip gray dot">Vypnuté</span>}
        </div>
        <div className="card-body">
          {twofaEnabled === null && <div className="muted">Načítavam…</div>}

          {twofaEnabled === false && !setupData && (
            <>
              <p className="muted" style={{margin:'0 0 10px'}}>
                Zapnutím 2FA bude pri každom prihlásení vyžiadaný 6-ciferný kód z autentifikátora (Google Authenticator, Authy, 1Password, …). Výrazne znižuje riziko kompromitácie účtu.
              </p>
              <button className="btn btn-primary" onClick={startSetup} disabled={busy}>
                {busy ? 'Pripravujem…' : 'Zapnúť 2FA'}
              </button>
            </>
          )}

          {twofaEnabled === false && setupData && (
            <div>
              <div className="small muted" style={{marginBottom:10}}>
                1) Naskenujte QR kód v aplikácii Authy / Google Authenticator / 1Password.<br/>
                2) Zadajte 6-ciferný kód, ktorý aplikácia zobrazí.
              </div>
              <div className="hstack" style={{gap:20, alignItems:'flex-start', flexWrap:'wrap'}}>
                <div style={{flex:'0 0 auto', border:'1px solid var(--border)', borderRadius:8, padding:6, background:'#fff'}}>
                  <img src={setupData.qr_data_url} alt="2FA QR code" style={{display:'block', width:220, height:220}}/>
                </div>
                <div style={{flex:1, minWidth:240}}>
                  <div className="field">
                    <label>Secret (ak nefunguje sken — zadajte ručne)</label>
                    <div className="hstack">
                      <input className="input mono" value={setupData.secret} readOnly style={{flex:1}}/>
                      <button className="btn btn-sm" type="button" onClick={() => navigator.clipboard && navigator.clipboard.writeText(setupData.secret)}>Kopírovať</button>
                    </div>
                  </div>
                  <div className="field" style={{marginTop:10}}>
                    <label>Potvrdiť 6-ciferný kód</label>
                    <input
                      className="input mono"
                      inputMode="numeric"
                      maxLength={6}
                      placeholder="123456"
                      value={setupCode}
                      onChange={e => setSetupCode(e.target.value.replace(/\D/g, '').slice(0, 6))}
                      style={{fontSize:18, letterSpacing:4, textAlign:'center'}}
                      autoFocus
                    />
                  </div>
                  <div className="hstack" style={{marginTop:10}}>
                    <button className="btn" type="button" onClick={() => { setSetupData(null); setSetupCode(''); }} disabled={busy}>Zrušiť</button>
                    <button className="btn btn-primary" type="button" onClick={confirmEnable} disabled={busy || setupCode.length !== 6}>
                      {busy ? 'Overujem…' : 'Zapnúť 2FA'}
                    </button>
                  </div>
                </div>
              </div>
            </div>
          )}

          {twofaEnabled === true && (
            <div>
              <p className="muted" style={{margin:'0 0 10px'}}>
                Na vypnutie 2FA potvrďte aktuálnym heslom + 6-ciferným kódom z autentifikátora.
              </p>
              <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:10, maxWidth:500}}>
                <div className="field"><label>Heslo</label><input className="input" type="password" value={disablePwd} onChange={e => setDisablePwd(e.target.value)} autoComplete="current-password"/></div>
                <div className="field"><label>6-ciferný kód</label><input className="input mono" inputMode="numeric" maxLength={6} value={disableCode} onChange={e => setDisableCode(e.target.value.replace(/\D/g, '').slice(0, 6))} style={{letterSpacing:3, textAlign:'center'}}/></div>
              </div>
              <button className="btn btn-danger btn-sm" style={{marginTop:10}} onClick={disable} disabled={busy || !disablePwd || disableCode.length !== 6}>
                {busy ? 'Vypínam…' : 'Vypnúť 2FA'}
              </button>
            </div>
          )}
        </div>
      </div>
    </>
  );
}

// ── Notifications tab — WP options (pickup / company reg / pallet emails) ───
function SettingsNotifications({ notify }) {
  const [fields, setFields] = React.useState(null);   // { key: { value, label, type } }
  const [draft, setDraft]   = React.useState({});
  const [loading, setLoading] = React.useState(true);
  const [error, setError]   = React.useState(null);
  const [busy, setBusy]     = React.useState(false);
  const canEdit = window.hasPerm ? window.hasPerm('settings.edit') : false;

  async function load() {
    setLoading(true);
    const r = await API.getWpSettings();
    setLoading(false);
    if (r.status === 503) { setError('not_configured'); return; }
    if (r.status === 404 || r.body.error === 'bridge_not_installed') { setError('bridge_missing'); return; }
    if (!r.ok) { setError(r.body.message || 'Chyba.'); return; }
    setError(null);
    setFields(r.body.settings || {});
    const d = {};
    for (const [k, v] of Object.entries(r.body.settings || {})) d[k] = v.value || '';
    setDraft(d);
  }
  React.useEffect(() => { load(); }, []);

  async function save() {
    setBusy(true);
    const r = await API.saveWpSettings(draft);
    setBusy(false);
    if (r.ok) { notify('success', `Uložené (${r.body.updated?.length || 0} polí).`); load(); }
    else      { notify('error', r.body.message || 'Chyba.'); }
  }

  if (error === 'not_configured') {
    return <div className="card" style={{padding:20}}>Pripojte WordPress v <a href="#/integrations" style={{color:'var(--navy-700)'}}>Integráciách</a>.</div>;
  }
  if (error === 'bridge_missing') {
    return (
      <div className="card" style={{padding:20}}>
        <b>WP bridge plugin chýba alebo je starý.</b>
        <div className="small muted" style={{marginTop:4}}>Endpoint <code>/dd/v1/settings</code> neexistuje. Re-uploadnite aktuálny plugin.</div>
      </div>
    );
  }
  if (loading || !fields) return <div className="muted" style={{padding:20}}>Načítavam…</div>;

  // Group by prefix for nicer UX
  const groups = [
    { title: 'Osobný odber — notifikácie', keys: ['pickup_notification_email', 'pickup_notification_emails_extra'] },
    { title: 'Osobný odber — kontakt v e-mailoch', keys: ['pickup_contact_phone', 'pickup_contact_email', 'pickup_address', 'pickup_hours'] },
    { title: 'Firemné registrácie',        keys: ['company_registration_email'] },
    { title: 'Paletové dopyty',            keys: ['my_pallet_emails'] },
  ];

  return (
    <>
      <div className="card" style={{padding:14, background:'var(--blue-50)'}}>
        <div className="small muted">
          Tieto nastavenia sa ukladajú priamo do WordPress opcí cez bridge plugin — používajú sa v <code>functions.php</code> pre odosielanie notifikácií. Admin e-maily môžete oddeliť čiarkou, prípadne do textareí jeden na riadok.
        </div>
      </div>

      {groups.map(g => (
        <div key={g.title} className="card">
          <div className="card-head"><h3>{g.title}</h3></div>
          <div className="card-body vstack" style={{gap:10}}>
            {g.keys.map(k => {
              const f = fields[k];
              if (!f) return null;
              const isTextarea = f.type === 'text';
              return (
                <div key={k} className="field">
                  <label>{f.label}</label>
                  {isTextarea
                    ? <textarea className="textarea" rows={3} value={draft[k] ?? ''} onChange={e => setDraft(d => ({ ...d, [k]: e.target.value }))} disabled={!canEdit}/>
                    : <input className="input" type={f.type === 'email' ? 'email' : 'text'} value={draft[k] ?? ''} onChange={e => setDraft(d => ({ ...d, [k]: e.target.value }))} disabled={!canEdit}/>
                  }
                  <div className="small muted">WP option: <code>{k}</code></div>
                </div>
              );
            })}
          </div>
        </div>
      ))}

      {canEdit && (
        <div className="hstack" style={{justifyContent:'flex-end'}}>
          <button className="btn btn-primary" onClick={save} disabled={busy}>
            {busy ? 'Ukladám…' : 'Uložiť všetky nastavenia'}
          </button>
        </div>
      )}
    </>
  );
}

// ── Branding tab — favicon + logo light + logo dark ─────────
function SettingsBranding({ notify }) {
  const [branding, setBranding] = React.useState({ favicon_url: null, logo_light_url: null, logo_dark_url: null, app_name: null });
  const [loading, setLoading] = React.useState(true);
  const [appName, setAppName] = React.useState('');
  const [busy, setBusy] = React.useState(null); // slot currently uploading

  async function reload() {
    setLoading(true);
    const r = await fetch('/api/crm-settings', { credentials: 'same-origin' });
    const j = await r.json().catch(() => ({}));
    setLoading(false);
    if (j.ok) { setBranding(j.branding); setAppName(j.branding.app_name || ''); }
  }
  React.useEffect(() => { reload(); }, []);

  async function upload(slot, file) {
    if (!file) return;
    setBusy(slot);
    const fd = new FormData();
    fd.append('slot', slot);
    fd.append('file', file);
    const r = await fetch('/api/crm-settings/branding/upload', { method: 'POST', credentials: 'same-origin', body: fd });
    const j = await r.json().catch(() => ({}));
    setBusy(null);
    if (!r.ok || !j.ok) { notify('error', j.message || j.error || 'Upload zlyhal.'); return; }
    setBranding(j.branding);
    notify('success', 'Uložené.');
    // Broadcast so app + sidebar re-render with new logo/favicon
    window.dispatchEvent(new CustomEvent('branding:changed', { detail: j.branding }));
  }

  async function remove(slot) {
    if (!confirm('Odstrániť tento obrázok?')) return;
    setBusy(slot);
    const r = await fetch(`/api/crm-settings/branding/${slot}`, { method: 'DELETE', credentials: 'same-origin' });
    const j = await r.json().catch(() => ({}));
    setBusy(null);
    if (!r.ok || !j.ok) { notify('error', 'Nepodarilo sa odstrániť.'); return; }
    setBranding(j.branding);
    window.dispatchEvent(new CustomEvent('branding:changed', { detail: j.branding }));
  }

  async function saveName() {
    setBusy('name');
    const r = await fetch('/api/crm-settings', {
      method: 'PATCH', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ app_name: appName.trim() }),
    });
    const j = await r.json().catch(() => ({}));
    setBusy(null);
    if (!r.ok || !j.ok) return notify('error', 'Nepodarilo sa uložiť.');
    setBranding(j.branding);
    notify('success', 'Uložené.');
    window.dispatchEvent(new CustomEvent('branding:changed', { detail: j.branding }));
  }

  const slot = (id, title, hint, accept, previewStyle) => {
    const key = id === 'favicon' ? 'favicon_url' : id + '_url';
    const url = branding[key];
    return (
      <div className="card" style={{padding:14}}>
        <div className="hstack" style={{marginBottom:8}}>
          <div style={{flex:1}}>
            <h4 style={{margin:0}}>{title}</h4>
            <div className="small muted">{hint}</div>
          </div>
          {url && (
            <button type="button" className="btn btn-sm btn-danger" disabled={busy === id} onClick={() => remove(id)}>
              <Ico.x/>Odstrániť
            </button>
          )}
        </div>
        <div className="hstack" style={{gap:14, alignItems:'center'}}>
          <div style={{
            width:96, height:96, display:'grid', placeItems:'center',
            border:'1px dashed var(--border-strong)', borderRadius:8,
            ...previewStyle
          }}>
            {url ? <img src={url} alt="" style={{maxWidth:'85%', maxHeight:'85%', objectFit:'contain'}}/> :
              <span className="small muted">bez obrázka</span>}
          </div>
          <label className="btn" style={{cursor:'pointer'}}>
            <Ico.upload/> {busy === id ? 'Nahrávam…' : (url ? 'Nahradiť' : 'Nahrať')}
            <input type="file" accept={accept} hidden onChange={e => upload(id, e.target.files?.[0])}/>
          </label>
        </div>
      </div>
    );
  };

  if (loading) return <div className="card" style={{padding:30, textAlign:'center'}}>Načítavam…</div>;

  return (
    <>
      <div className="card">
        <div className="card-head"><h3>Názov aplikácie</h3></div>
        <form className="card-body vstack" style={{gap:10, padding:14}} onSubmit={e => { e.preventDefault(); saveName(); }}>
          <div className="field">
            <label>Zobrazované meno (sidebar + záložka prehliadača)</label>
            <input className="input" value={appName} onChange={e => setAppName(e.target.value)} placeholder="D&D Energy" maxLength={80}/>
          </div>
          <button className="btn btn-primary btn-sm" style={{alignSelf:'flex-start'}} disabled={busy === 'name'}>
            {busy === 'name' ? 'Ukladám…' : 'Uložiť názov'}
          </button>
        </form>
      </div>

      <div className="card">
        <div className="card-head"><h3>Ikona a logá</h3></div>
        <div className="card-body vstack" style={{gap:12, padding:14}}>
          {slot('favicon',    'Favicon',       'Ikona v záložke prehliadača. Odporúčané PNG 32×32 alebo .ico.', 'image/png,image/x-icon,image/svg+xml,image/vnd.microsoft.icon', { background:'#fff' })}
          {slot('logo_light', 'Logo — svetlé', 'Zobrazuje sa na svetlom pozadí (hlavné logo v bočnom paneli). SVG alebo PNG s priehľadným pozadím.', 'image/png,image/svg+xml,image/webp,image/jpeg', { background:'#fff' })}
          {slot('logo_dark',  'Logo — tmavé',  'Variant pre tmavé pozadie (napr. prihlasovacia obrazovka). SVG alebo PNG s priehľadným pozadím.', 'image/png,image/svg+xml,image/webp,image/jpeg', { background:'#0a3d62' })}
        </div>
      </div>
    </>
  );
}

// ── Integrations tab — shortcut to /#/integrations ──
function SettingsIntegrations() {
  React.useEffect(() => {
    // Redirect after a short delay so user can see what happened
    const id = setTimeout(() => { window.location.hash = '#/integrations'; }, 250);
    return () => clearTimeout(id);
  }, []);
  return (
    <div className="card" style={{padding:30, textAlign:'center'}}>
      <div style={{fontSize:32}}>🔌</div>
      <h3>Presmerujem do Integrácií…</h3>
      <p className="muted">Alebo kliknite <a href="#/integrations" style={{color:'var(--navy-700)', fontWeight:600}}>sem</a>.</p>
    </div>
  );
}

// ── Skill Access Modal ─────────────────────────────────────────────────────
// Shows all AI skills; admin checks which ones this user can use.
// Unrestricted skills (restricted=0) are available to everyone and show only for context.
function SkillAccessModal({ user, onClose, notify }) {
  const [skills, setSkills] = React.useState([]);
  const [allowed, setAllowed] = React.useState(new Set());
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving] = React.useState(false);

  async function reload() {
    setLoading(true);
    const r = await fetch('/api/ai/users-access', { credentials: 'same-origin' });
    const j = await r.json().catch(() => ({}));
    setLoading(false);
    if (!r.ok || !j.ok) { notify('error', 'Nepodarilo sa načítať.'); onClose(); return; }
    setSkills(j.skills || []);
    setAllowed(new Set(j.access[user.id] || []));
  }
  React.useEffect(() => { reload(); }, []);

  function toggle(id) {
    setAllowed(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  }
  async function save() {
    setSaving(true);
    const r = await fetch('/api/ai/users-access/' + user.id, {
      method: 'PATCH', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ skill_ids: [...allowed] }),
    });
    setSaving(false);
    if (r.ok) { notify('success', 'Prístupy uložené.'); onClose(); }
    else notify('error', 'Chyba.');
  }

  const restricted   = skills.filter(s => s.restricted);
  const unrestricted = skills.filter(s => !s.restricted);
  const isAdmin = user.role === 'admin';

  return (
    <Modal
      title={`AI skills — ${user.name}`}
      width={560}
      onClose={onClose}
      footer={<>
        <button className="btn" onClick={onClose}>Zavrieť</button>
        <button className="btn btn-primary btn-sm" onClick={save} disabled={saving || isAdmin}>
          {saving ? 'Ukladám…' : 'Uložiť prístupy'}
        </button>
      </>}
    >
      <div className="small muted" style={{marginBottom:10}}>
        {isAdmin
          ? <>Administrátor má automatický prístup ku všetkým skillom — tento zoznam je len informatívny.</>
          : <>Zaškrtnite skilly, ku ktorým má mať <b>{user.name}</b> prístup. „Per-user" skilly sú viditeľné iba pre explicitne priradených používateľov.</>}
      </div>

      {loading ? <div className="muted" style={{padding:20, textAlign:'center'}}>Načítavam…</div> : (
        <div className="vstack" style={{gap:14}}>
          <div>
            <div className="small muted" style={{textTransform:'uppercase', letterSpacing:'0.08em', fontWeight:600, marginBottom:6}}>
              Obmedzené (per-user) · {restricted.length}
            </div>
            {restricted.length === 0 ? <div className="small muted" style={{padding:8, border:'1px dashed var(--border-strong)', borderRadius:6}}>Žiadne „per-user" skilly. Označte skill v AI → Skills ako „Per-user" ak chcete kontrolovať prístup.</div> :
              <div className="vstack" style={{gap:4}}>
                {restricted.map(s => (
                  <label key={s.id} className="checkbox" style={{padding:'6px 10px', border:'1px solid var(--border)', borderRadius:6, background:'#fff'}}>
                    <input type="checkbox" checked={allowed.has(s.id)} onChange={() => toggle(s.id)} disabled={isAdmin}/>
                    <span style={{flex:1, minWidth:0}}>
                      <div className="mono small"><b>{s.name}</b></div>
                      <div className="small muted" style={{overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>{s.description || '—'}</div>
                    </span>
                  </label>
                ))}
              </div>}
          </div>
          <div>
            <div className="small muted" style={{textTransform:'uppercase', letterSpacing:'0.08em', fontWeight:600, marginBottom:6}}>
              Dostupné všetkým · {unrestricted.length}
            </div>
            {unrestricted.length === 0 ? <div className="small muted">—</div> :
              <div className="vstack" style={{gap:4}}>
                {unrestricted.map(s => (
                  <div key={s.id} style={{padding:'4px 10px', opacity:0.65}}>
                    <span className="mono small"><b>{s.name}</b></span>
                    <span className="small muted"> — {s.description || '—'}</span>
                  </div>
                ))}
              </div>}
          </div>
        </div>
      )}
    </Modal>
  );
}

// Export shared UI primitives so integrations.jsx (loaded later) can reuse them.
Object.assign(window, { UsersPermissions, ModuleManager, Settings2FA, Modal, Toast, initials });
