/* ============================================================
   USER ADMINISTRATION
   ============================================================ */
function Users({state, act, toast, currentUser}){
  const [users, setUsers] = useState(null);
  const [dlg, setDlg] = useState(null);
  const [error, setError] = useState('');

  const load = useCallback(async () => {
    try { setUsers((await API.get('/api/users')).users); }
    catch (err) { setError(err.message); }
  }, []);
  useEffect(()=>{ load(); }, [load]);

  const run = async (promise, msg) => {
    try { await promise; await load(); if (msg) toast(msg); }
    catch (err) { toast(err.message, true); }
  };

  if (error) return <div className="err">{error}</div>;
  if (!users) return <div className="row" style={{gap:10,padding:20}}><span className="spinner"/> Loading accounts…</div>;

  return (
    <div className="stack">
      <div className="info-box">
        Access levels: <strong>Administrators</strong> manage accounts and classification lists.
        <strong> Editors</strong> create and edit any component and the directory.
        <strong> Viewers</strong> can read everything, and can additionally edit any component
        where they are named as one of the four owners.
      </div>

      <div className="card">
        <div className="card-head"><h3>User accounts</h3><Badge tone="slate">{users.length}</Badge><div className="spacer"/>
          <button className="btn sm primary" onClick={()=>setDlg({t:'new', v:{
            email:'', name:'', role:'viewer', password:'', personId:''}})}>
            <I.plus size={13}/>Add user</button></div>
        <div className="tbl-wrap">
          <table>
            <thead><tr><th>Name</th><th>Email</th><th>Access level</th><th>Linked person</th>
              <th>Status</th><th>Last sign-in</th><th style={{width:110}}></th></tr></thead>
            <tbody>{users.map(u => {
              const person = state.people.find(p => p.id === u.personId);
              return (
                <tr key={u.id}>
                  <td><div className="row" style={{gap:9}}><Avatar name={u.name} userId={u.id} hasAvatar={u.hasAvatar}/>
                    <span><span className="cell-name">{u.name}</span>
                      {u.id === currentUser.id && <span className="cell-sub">{'That’s you'}</span>}</span></div></td>
                  <td className="small muted">{u.email}</td>
                  <td><Badge tone={u.role==='admin'?'purple':u.role==='editor'?'accent':'slate'}>
                    {ROLE_LABEL[u.role]}</Badge></td>
                  <td className="small muted">{person ? person.name : <span className="dim">Not linked</span>}</td>
                  <td>{u.isActive
                    ? <Badge tone="green" dot>Active</Badge>
                    : <Badge tone="slate" dot>Deactivated</Badge>}
                    {u.totpEnabled && <div className="xsmall" style={{marginTop:3}}><Badge tone="accent">2FA on</Badge></div>}
                    {u.mustChangePassword && <div className="xsmall dim" style={{marginTop:3}}>Must change password</div>}</td>
                  <td className="small muted">{u.lastLoginAt ? relative(u.lastLoginAt) : <span className="dim">Never</span>}</td>
                  <td><div className="row" style={{gap:2}}>
                    <button className="icon-btn" title="Edit" onClick={()=>setDlg({t:'edit', v:{...u, personId:u.personId||''}})}>
                      <I.edit size={14}/></button>
                    <button className="icon-btn" title="Reset password" onClick={()=>setDlg({t:'pw', v:u})}>
                      <I.key size={14}/></button>
                    {u.totpEnabled &&
                      <button className="icon-btn" title="Disable two-factor authentication (account recovery)"
                        onClick={()=>setDlg({t:'2fa', v:u})}><I.shield size={14}/></button>}
                    <button className="icon-btn" title="Delete" disabled={u.id === currentUser.id}
                      onClick={()=>setDlg({t:'del', v:u})}><I.trash size={14}/></button>
                  </div></td>
                </tr>);
            })}</tbody>
          </table>
        </div>
      </div>

      {dlg && (dlg.t === 'new' || dlg.t === 'edit') &&
        <UserForm value={dlg.v} state={state} isNew={dlg.t==='new'} onClose={()=>setDlg(null)}
          onSave={async (v)=>{
            await run(dlg.t==='new' ? API.post('/api/users', v) : API.put(`/api/users/${v.id}`, v), 'Account saved');
            setDlg(null);
          }}/>}
      {dlg && dlg.t === 'pw' &&
        <ResetPasswordModal user={dlg.v} onClose={()=>setDlg(null)}
          onSave={async (password)=>{
            await run(API.post(`/api/users/${dlg.v.id}/password`, {password}), 'Password reset');
            setDlg(null);
          }}/>}
      {dlg && dlg.t === 'del' &&
        <ConfirmDialog title="Delete account" onClose={()=>setDlg(null)}
          message={`Delete the account for ${dlg.v.name} (${dlg.v.email})? Anything they created stays in the register. You'll have a few seconds to undo.`}
          onConfirm={()=>deleteWithUndo(run, toast, `/api/users/${dlg.v.id}`, dlg.v.name)}/>}
      {dlg && dlg.t === '2fa' &&
        <ConfirmDialog title="Disable two-factor authentication" confirmLabel="Disable" onClose={()=>setDlg(null)}
          message={`Turn off two-factor authentication for ${dlg.v.name}? Use this if they have lost their
            authenticator device and their backup codes. They can set it up again afterwards.`}
          onConfirm={()=>run(API.post(`/api/users/${dlg.v.id}/2fa/disable`, {}), 'Two-factor authentication disabled')}/>}
    </div>
  );
}

function UserForm({value, state, isNew, onClose, onSave}){
  const [u, setU] = useState(()=>clone(value));
  const [busy, setBusy] = useState(false);
  const set = (k,v)=>setU(x=>({...x,[k]:v}));
  const valid = u.name.trim() && (!isNew || (/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(u.email) && (u.password||'').length >= 8));
  return (
    <Modal title={isNew ? 'Add user' : 'Edit ' + value.name} onClose={onClose}
      footer={<>
        <button className="btn" onClick={onClose} disabled={busy}>Cancel</button>
        <button className="btn primary" disabled={!valid || busy}
          onClick={async ()=>{ setBusy(true); try { await onSave(u); } finally { setBusy(false); } }}>
          {busy ? 'Saving…' : 'Save'}</button>
      </>}>
      <div className="form-grid">
        <Field label="Full name"><input type="text" value={u.name} onChange={e=>set('name',e.target.value)} autoFocus/></Field>
        <Field label="Email" hint={isNew ? '' : 'Email addresses cannot be changed after creation.'}>
          <input type="email" value={u.email} disabled={!isNew} onChange={e=>set('email',e.target.value)}/></Field>
      </div>
      <div className="form-grid">
        <Field label="Access level">
          <select value={u.role} onChange={e=>set('role',e.target.value)}>
            <option value="viewer">Viewer — read everything, edit components they own</option>
            <option value="editor">Editor — create and edit any component</option>
            <option value="admin">Administrator — everything, plus accounts and lists</option>
          </select></Field>
        <Field label="Linked directory person"
          hint="Required for owner-based editing. Links the account to a person in the directory.">
          <select value={u.personId||''} onChange={e=>set('personId', e.target.value)}>
            <option value="">Not linked</option>
            {state.people.map(p => <option key={p.id} value={p.id}>{p.name} — {p.jobTitle}</option>)}
          </select></Field>
      </div>
      {isNew &&
        <Field label="Initial password" hint="At least 8 characters. The user is asked to change it at first sign-in.">
          <input type="text" value={u.password||''} onChange={e=>set('password',e.target.value)}/>
        </Field>}
      {!isNew &&
        <label className="row" style={{gap:8,cursor:'pointer'}}>
          <input type="checkbox" style={{width:'auto'}} checked={!!u.isActive}
            onChange={e=>set('isActive', e.target.checked)}/>
          <span className="small">Account is active. Deactivating signs the user out immediately.</span>
        </label>}
    </Modal>
  );
}

function ResetPasswordModal({user, onClose, onSave}){
  const [pw, setPw] = useState('');
  return (
    <Modal title={'Reset password for ' + user.name} onClose={onClose}
      footer={<>
        <button className="btn" onClick={onClose}>Cancel</button>
        <button className="btn primary" disabled={pw.length < 8} onClick={()=>onSave(pw)}>Reset password</button>
      </>}>
      <div className="warn-box" style={{marginBottom:16}}>
        This signs {user.name} out of every session and forces a password change at next sign-in.
        Share the new password through a secure channel.
      </div>
      <Field label="New password" hint="At least 8 characters.">
        <input type="text" value={pw} onChange={e=>setPw(e.target.value)} autoFocus/>
      </Field>
    </Modal>
  );
}


/* ============================================================
   AUDIT LOG
   ============================================================ */
function AuditLog({toast}){
  const [entries, setEntries] = useState(null);
  const [error, setError] = useState('');
  const [q, setQ] = useState('');

  const load = useCallback(async () => {
    try { setEntries((await API.get('/api/audit?limit=500')).entries); }
    catch (err) { setError(err.message); }
  }, []);
  useEffect(()=>{ load(); }, [load]);

  if (error) return <div className="err">{error}</div>;
  if (!entries) return <div className="row" style={{gap:10,padding:20}}><span className="spinner"/> Loading…</div>;

  const term = q.trim().toLowerCase();
  const rows = term
    ? entries.filter(e => [e.user_name, e.action, e.entity, e.summary].join(' ').toLowerCase().includes(term))
    : entries;

  const tone = (action) =>
    action === 'delete' ? 'red' : action === 'create' ? 'green' :
    action.includes('password') ? 'amber' : 'slate';

  return (
    <div className="stack">
      <div className="row wrap">
        <div className="search-box"><I.search size={15}/>
          <input type="text" placeholder="Filter by user, action or entity…" value={q} onChange={e=>setQ(e.target.value)}/>
        </div>
        <div className="spacer"/>
        <button className="btn sm" onClick={load}><I.refresh size={13}/>Refresh</button>
      </div>
      <div className="card">
        <div className="card-head"><h3>Recent activity</h3><Badge tone="slate">{rows.length}</Badge>
          <span className="xsmall dim">Most recent 500 entries</span></div>
        <div className="tbl-wrap" style={{maxHeight:'calc(100vh - 250px)'}}>
          <table>
            <thead><tr><th style={{width:170}}>When</th><th>User</th><th>Action</th><th>Entity</th><th>Detail</th></tr></thead>
            <tbody>{rows.map(e =>
              <tr key={e.id}>
                <td className="small muted" title={e.at}>{fmtDateTime(e.at)}</td>
                <td className="small">{e.user_name}</td>
                <td><Badge tone={tone(e.action)}>{e.action}</Badge></td>
                <td className="small muted">{e.entity}</td>
                <td className="small muted">{e.summary || <span className="dim">—</span>}</td>
              </tr>)}</tbody>
          </table>
          {!rows.length && <EmptyState icon={<I.history size={28}/>} title="Nothing matches"/>}
        </div>
      </div>
    </div>
  );
}

