// Users management — appended to the Admin view.
//
// Rules-of-Hooks: every useState / useEffect / useMemo is declared
// BEFORE any early return. See components/admin.jsx:180-210 for the
// canonical pattern.

const USER_INPUT = {
  width: '100%', padding: '5px 9px', background: 'var(--surface)',
  border: '1px solid var(--border-2)', borderRadius: 2,
  color: 'var(--text)', fontFamily: 'var(--mono)', fontSize: 12, outline: 'none',
};

const ALL_VIEWS = ['overview','threed','topology','nodes','alerts','logs','admin','notifications'];

function UserEditor({ user, currentUser, onClose, onSaved, onResetPassword }) {
  const open = !!user;
  const [username, setUsername] = React.useState('');
  const [isAdmin, setIsAdmin] = React.useState(false);
  const [enabled, setEnabled] = React.useState(true);
  const [views, setViews] = React.useState([]);
  const [newPw, setNewPw] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);

  React.useEffect(() => {
    if (!user) return;
    setUsername(user.username || '');
    setIsAdmin(!!user.isAdmin);
    setEnabled(user.enabled !== false);
    setViews(user.isAdmin ? [...ALL_VIEWS] : (user.views || []));
    setNewPw('');
    setErr(null);
  }, [user?.id]);

  if (!open) return <div className="drawer" />;

  function toggleView(v) {
    setViews(cur => cur.includes(v) ? cur.filter(x => x !== v) : [...cur, v]);
  }

  async function save() {
    setBusy(true); setErr(null);
    try {
      const patch = {
        username: username.trim(),
        isAdmin,
        enabled,
        views: isAdmin ? ALL_VIEWS : views,
      };
      const updated = await window.apiFetch(`/api/admin/users/${user.id}`, {
        method: 'PATCH',
        body: JSON.stringify(patch),
      });
      if (newPw.trim()) {
        await window.apiFetch(`/api/admin/users/${user.id}/password`, {
          method: 'POST',
          body: JSON.stringify({ password: newPw }),
        });
      }
      onSaved(updated);
    } catch (e) { setErr(e.message || 'save failed'); }
    finally { setBusy(false); }
  }

  const self = user && currentUser && user.id === currentUser.id;

  return (
    <div className="drawer open">
      <div className="drawer-head">
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="drawer-title">Edit user</div>
          <div className="drawer-sub">{user.id}{self ? ' · you' : ''}</div>
        </div>
        <button className="close" onClick={onClose}>×</button>
      </div>
      <div className="drawer-body">
        <div className="tweak-row">
          <label>Username</label>
          <input style={USER_INPUT} value={username} onChange={e => setUsername(e.target.value)} />
        </div>
        <div className="tweak-row">
          <label>Role</label>
          <label style={{ fontSize: 11, display: 'flex', gap: 6, alignItems: 'center', color: 'var(--text-dim)' }}>
            <input type="checkbox" checked={isAdmin} onChange={e => setIsAdmin(e.target.checked)} />
            admin (full access — implicitly every view)
          </label>
        </div>
        <div className="tweak-row">
          <label>Enabled</label>
          <label style={{ fontSize: 11, display: 'flex', gap: 6, alignItems: 'center', color: 'var(--text-dim)' }}>
            <input type="checkbox" checked={enabled} onChange={e => setEnabled(e.target.checked)} />
            signed-in users with disabled accounts are booted immediately
          </label>
        </div>
        <div className="tweak-row">
          <label>Views {isAdmin && <span style={{ textTransform: 'none', color: 'var(--text-mute)' }}>· admin overrides this</span>}</label>
          <div className="seg" style={{ flexWrap: 'wrap' }}>
            {ALL_VIEWS.map(v => (
              <button key={v} type="button" disabled={isAdmin}
                      className={(isAdmin || views.includes(v)) ? 'on' : ''}
                      onClick={() => toggleView(v)}
                      style={isAdmin ? { opacity: 0.55, cursor: 'not-allowed' } : {}}>
                {v}
              </button>
            ))}
          </div>
        </div>
        <div className="tweak-row">
          <label>New password <span style={{ textTransform: 'none', color: 'var(--text-mute)' }}>(leave blank to keep)</span></label>
          <input style={USER_INPUT} type="password" value={newPw} onChange={e => setNewPw(e.target.value)}
                 placeholder="at least 6 chars" />
        </div>
        {err && <div style={{ color: 'var(--crit)', fontSize: 11 }}>{err}</div>}
      </div>
      <div className="drawer-actions" style={{ gridTemplateColumns: '1fr 1fr' }}>
        <button className="action-btn" onClick={onClose} disabled={busy}>cancel</button>
        <button className="action-btn" onClick={save} disabled={busy || !username.trim()}
          style={{ color: 'var(--accent)', borderColor: 'var(--accent-dim)', background: 'var(--accent-bg)' }}>
          {busy ? 'saving…' : 'save'}
        </button>
      </div>
    </div>
  );
}

function AddUserForm({ onAdded }) {
  const [username, setUsername] = React.useState('');
  const [password, setPassword] = React.useState('');
  const [isAdmin, setIsAdmin] = React.useState(false);
  const [views, setViews] = React.useState(['overview']);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);

  function toggleView(v) {
    setViews(cur => cur.includes(v) ? cur.filter(x => x !== v) : [...cur, v]);
  }

  async function submit(e) {
    e.preventDefault();
    setBusy(true); setErr(null);
    try {
      const created = await window.apiFetch('/api/admin/users', {
        method: 'POST',
        body: JSON.stringify({
          username: username.trim(), password,
          isAdmin, enabled: true,
          views: isAdmin ? ALL_VIEWS : views,
        }),
      });
      onAdded(created);
      setUsername(''); setPassword(''); setIsAdmin(false); setViews(['overview']);
    } catch (e2) { setErr(e2.message || 'add failed'); }
    finally { setBusy(false); }
  }

  return (
    <form onSubmit={submit} className="card-body"
          style={{ display: 'grid', gridTemplateColumns: '1fr 1fr auto 2fr auto', gap: 10, alignItems: 'end' }}>
      <div className="tweak-row">
        <label>username</label>
        <input style={USER_INPUT} required value={username} onChange={e => setUsername(e.target.value)} />
      </div>
      <div className="tweak-row">
        <label>password</label>
        <input style={USER_INPUT} type="password" required minLength={6}
               value={password} onChange={e => setPassword(e.target.value)} />
      </div>
      <div className="tweak-row">
        <label>admin</label>
        <label style={{ fontSize: 11, color: 'var(--text-dim)', display: 'flex', gap: 4, alignItems: 'center' }}>
          <input type="checkbox" checked={isAdmin} onChange={e => setIsAdmin(e.target.checked)} />
          full access
        </label>
      </div>
      <div className="tweak-row">
        <label>views</label>
        <div className="seg" style={{ flexWrap: 'wrap' }}>
          {ALL_VIEWS.map(v => (
            <button key={v} type="button" disabled={isAdmin}
                    className={(isAdmin || views.includes(v)) ? 'on' : ''}
                    onClick={() => toggleView(v)}
                    style={isAdmin ? { opacity: 0.55, cursor: 'not-allowed' } : {}}>
              {v}
            </button>
          ))}
        </div>
      </div>
      <button type="submit" className="btn" disabled={busy || !username.trim() || password.length < 6}
        style={{ color: 'var(--accent)', borderColor: 'var(--accent-dim)',
                 background: 'var(--accent-bg)', padding: '6px 14px' }}>
        {busy ? 'adding…' : 'add user'}
      </button>
      {err && <div style={{ gridColumn: '1 / -1', color: 'var(--crit)', fontSize: 11 }}>{err}</div>}
    </form>
  );
}

function UsersAdmin({ currentUser }) {
  const [users, setUsers] = React.useState(null);
  const [editing, setEditing] = React.useState(null);
  const [confirmDelete, setConfirmDelete] = React.useState(null);
  const [err, setErr] = React.useState(null);

  async function load() {
    setErr(null);
    try {
      const list = await window.apiFetch('/api/admin/users');
      setUsers(list);
    } catch (e) {
      if (e.message !== 'unauthorized') setErr(e.message);
    }
  }
  React.useEffect(() => { load(); }, []);
  React.useEffect(() => {
    if (!confirmDelete) return;
    const t = setTimeout(() => setConfirmDelete(null), 4000);
    return () => clearTimeout(t);
  }, [confirmDelete]);

  async function onAdded(_u) { await load(); }
  async function onSaved(_u) { setEditing(null); await load(); }
  async function onToggleEnabled(u) {
    try {
      await window.apiFetch(`/api/admin/users/${u.id}`, {
        method: 'PATCH', body: JSON.stringify({ enabled: !u.enabled }),
      });
      await load();
    } catch (e) { setErr(e.message); }
  }
  async function onDelete(u) {
    try {
      await window.apiFetch(`/api/admin/users/${u.id}`, { method: 'DELETE' });
      await load();
    } catch (e) { setErr(e.message); }
  }
  async function onResetPassword(u) {
    const pw = prompt(`New password for "${u.username}" (min 6 chars, admin reset will sign them out everywhere):`);
    if (!pw || pw.length < 6) return;
    try {
      await window.apiFetch(`/api/admin/users/${u.id}/password`, {
        method: 'POST', body: JSON.stringify({ password: pw }),
      });
    } catch (e) { setErr(e.message); }
  }

  return (
    <div className="card" style={{ padding: 0 }}>
      <div className="card-head">
        <h3>Users</h3>
        <span className="sub">{users ? `${users.length} account${users.length === 1 ? '' : 's'}` : 'loading…'}</span>
      </div>
      <AddUserForm onAdded={onAdded} />
      {err && <div style={{ padding: '0 12px 8px', color: 'var(--crit)', fontSize: 11 }}>{err}</div>}
      <div style={{ overflow: 'auto' }}>
        <table className="tbl">
          <thead>
            <tr>
              <th>username</th><th>role</th><th>views</th><th>status</th><th>created</th>
              <th style={{ textAlign: 'right' }}>actions</th>
            </tr>
          </thead>
          <tbody>
            {(users || []).map(u => {
              const self = currentUser && u.id === currentUser.id;
              return (
                <tr key={u.id}>
                  <td style={{ fontWeight: 500 }}>{u.username}{self && <span className="chip accent" style={{ marginLeft: 6, fontSize: 9 }}>you</span>}</td>
                  <td>
                    {u.isAdmin
                      ? <span className="chip crit" style={{ background: 'var(--accent-bg)', color: 'var(--accent)', borderColor: 'oklch(0.78 0.14 195 / 0.3)' }}>admin</span>
                      : <span className="chip" style={{ background: 'var(--surface-3)', color: 'var(--text-dim)' }}>user</span>}
                  </td>
                  <td>
                    {u.isAdmin
                      ? <span style={{ color: 'var(--text-mute)', fontSize: 10 }}>all 8</span>
                      : (u.views || []).map(v => (
                          <span key={v} className="chip" style={{ marginRight: 4,
                            background: 'var(--surface-3)', color: 'var(--text-dim)', fontSize: 9 }}>{v}</span>
                        ))}
                  </td>
                  <td>
                    <span className={`chip ${u.enabled ? 'ok' : 'off'}`}>{u.enabled ? 'enabled' : 'disabled'}</span>
                  </td>
                  <td className="mute">{window.fmtRel(u.createdAt)}</td>
                  <td style={{ textAlign: 'right' }}>
                    <div style={{ display: 'inline-flex', gap: 4 }}>
                      <button className="btn" onClick={() => setEditing(u)}>edit</button>
                      <button className="btn" onClick={() => onResetPassword(u)}>reset pw</button>
                      <button className="btn" onClick={() => onToggleEnabled(u)} disabled={self && u.enabled}>
                        {u.enabled ? 'disable' : 'enable'}
                      </button>
                      {confirmDelete === u.id ? (
                        <button className="btn" onClick={() => { setConfirmDelete(null); onDelete(u); }}
                          style={{ color: 'var(--crit)', borderColor: 'oklch(0.66 0.22 27 / 0.5)', background: 'var(--crit-bg)' }}>
                          confirm?
                        </button>
                      ) : (
                        <button className="btn" onClick={() => setConfirmDelete(u.id)} disabled={self}>delete</button>
                      )}
                    </div>
                  </td>
                </tr>
              );
            })}
            {(!users || users.length === 0) && (
              <tr><td colSpan="6" style={{ padding: 20, textAlign: 'center', color: 'var(--text-mute)' }}>
                {users === null ? 'Loading…' : 'No users yet.'}
              </td></tr>
            )}
          </tbody>
        </table>
      </div>
      <UserEditor user={editing} currentUser={currentUser}
                  onClose={() => setEditing(null)} onSaved={onSaved} />
    </div>
  );
}

function AuditLog() {
  // All hooks above any early return.
  const [entries, setEntries] = React.useState(null);
  const [userFilter, setUserFilter] = React.useState('');
  const [limit, setLimit] = React.useState(200);
  const [err, setErr] = React.useState(null);

  async function load() {
    setErr(null);
    try {
      const q = new URLSearchParams({ limit: String(limit), days: '14' });
      if (userFilter.trim()) q.set('username', userFilter.trim());
      const data = await window.apiFetch(`/api/admin/audit?${q.toString()}`);
      setEntries(data.entries || []);
    } catch (e) {
      if (e.message !== 'unauthorized') setErr(e.message);
    }
  }

  React.useEffect(() => { load(); }, []);
  React.useEffect(() => {
    // Auto-refresh every 30s while panel is mounted.
    const t = setInterval(load, 30_000);
    return () => clearInterval(t);
  }, [userFilter, limit]);

  function statusChip(code) {
    if (code == null) return <span className="chip" style={{background:'var(--surface-3)', color:'var(--text-mute)'}}>—</span>;
    if (code >= 500) return <span className="chip crit">{code}</span>;
    if (code >= 400) return <span className="chip warn">{code}</span>;
    return <span className="chip ok">{code}</span>;
  }

  return (
    <div className="card" style={{ padding: 0 }}>
      <div className="card-head" style={{ gap: 8 }}>
        <h3>Audit log</h3>
        <span className="sub">{entries ? `${entries.length} entries · last 14d` : 'loading…'}</span>
        <span className="spacer" style={{ flex: 1 }} />
        <input style={{
          background: 'var(--surface)', border: '1px solid var(--border-2)',
          borderRadius: 2, color: 'var(--text)', padding: '4px 8px',
          fontFamily: 'var(--mono)', fontSize: 11, width: 160,
        }} placeholder="filter by username"
               value={userFilter}
               onChange={e => setUserFilter(e.target.value)}
               onKeyDown={e => { if (e.key === 'Enter') load(); }} />
        <button className="btn" onClick={load}>reload</button>
      </div>
      {err && <div style={{ padding: '0 12px 8px', color: 'var(--crit)', fontSize: 11 }}>{err}</div>}
      <div style={{ overflow: 'auto', maxHeight: 360 }}>
        <table className="tbl">
          <thead>
            <tr>
              <th>when</th><th>user</th><th>method</th><th>path</th>
              <th>target</th><th>status</th><th>dur</th><th>ip</th>
            </tr>
          </thead>
          <tbody>
            {(entries || []).map(e => (
              <tr key={e.id}>
                <td className="mute">{window.fmtRel(e.ts)}</td>
                <td style={{ fontWeight: 500 }}>
                  {e.username || <span style={{ color: 'var(--text-mute)' }}>—</span>}
                  {e.isAdmin && <span className="chip accent" style={{ marginLeft: 4, fontSize: 9 }}>admin</span>}
                </td>
                <td><span className="chip" style={{
                  background: e.method === 'DELETE' ? 'var(--crit-bg)'
                              : e.method === 'POST' ? 'var(--accent-bg)'
                              : 'var(--surface-3)',
                  color: e.method === 'DELETE' ? 'var(--crit)'
                         : e.method === 'POST' ? 'var(--accent)'
                         : 'var(--text-dim)',
                  fontSize: 9,
                }}>{e.method}</span></td>
                <td className="mute" style={{ maxWidth: 280, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {e.path}
                </td>
                <td className="mute" style={{ maxWidth: 100, overflow: 'hidden', textOverflow: 'ellipsis' }}>
                  {e.target_id || <span style={{ color: 'var(--text-mute)' }}>—</span>}
                </td>
                <td>{statusChip(e.status_code)}{e.error && <span title={e.error} style={{ marginLeft: 4, color: 'var(--crit)', fontSize: 10 }}>✗</span>}</td>
                <td className="num mute">{e.duration_ms != null ? `${e.duration_ms}ms` : '—'}</td>
                <td className="mute" style={{ fontSize: 10 }}>{e.client_ip || '—'}</td>
              </tr>
            ))}
            {(entries || []).length === 0 && (
              <tr><td colSpan="8" style={{ padding: 20, textAlign: 'center', color: 'var(--text-mute)' }}>
                {entries === null ? 'Loading…' : 'No audit entries yet — take an admin action to see one here.'}
              </td></tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
}

Object.assign(window, { UsersAdmin, AuditLog });
