/* Machines / nodes view */
function Machines({ initialFilter }) {
  const s = useStore();
  const toast = useToast();
  const [q, setQ] = useState("");
  const [sel, setSel] = useState(new Set());
  const [sort, setSort] = useState({ k: "name", dir: 1 });
  const [filterUser, setFilterUser] = useState("all");
  const [filterState, setFilterState] = useState(initialFilter || "all");
  const [modal, setModal] = useState(null); // {type, machine}
  const [drawer, setDrawer] = useState(null);
  const [density, setDensity] = useState(() => localStorage.getItem("hs.mdensity") || "comfortable");
  useEffect(() => { localStorage.setItem("hs.mdensity", density); }, [density]);

  const filtered = useMemo(() => {
    let list = s.machines.slice();
    if (q) { const ql = q.toLowerCase(); list = list.filter((m) => m.name.toLowerCase().includes(ql) || m.ip4.includes(ql) || m.tags.some((t) => t.includes(ql)) || s.userById(m.user)?.name.includes(ql)); }
    if (filterUser !== "all") list = list.filter((m) => String(m.user) === String(filterUser) && m.tags.length === 0);
    if (filterState === "online") list = list.filter((m) => m.online);
    if (filterState === "offline") list = list.filter((m) => !m.online);
    if (filterState === "expiring") list = list.filter((m) => expiryInfo(m.expiry).soon);
    list.sort((a, b) => {
      let av, bv;
      if (sort.k === "name") { av = a.name; bv = b.name; }
      else if (sort.k === "user") { av = s.userById(a.user)?.name; bv = s.userById(b.user)?.name; }
      else if (sort.k === "ip") { av = a.ip4; bv = b.ip4; }
      else if (sort.k === "lastSeen") { av = a.online ? Infinity : a.lastSeen; bv = b.online ? Infinity : b.lastSeen; }
      else { av = a[sort.k]; bv = b[sort.k]; }
      return (av > bv ? 1 : av < bv ? -1 : 0) * sort.dir;
    });
    return list;
  }, [s.machines, s.rev, q, filterUser, filterState, sort]);

  const toggleSort = (k) => setSort((p) => ({ k, dir: p.k === k ? -p.dir : 1 }));
  const allSel = filtered.length > 0 && filtered.every((m) => sel.has(m.id));
  const toggleAll = () => setSel(allSel ? new Set() : new Set(filtered.map((m) => m.id)));
  const toggleOne = (id) => setSel((p) => { const n = new Set(p); n.has(id) ? n.delete(id) : n.add(id); return n; });

  const Arrow = ({ k }) => sort.k === k ? <span className="arrow">{sort.dir > 0 ? "↑" : "↓"}</span> : null;

  return (
    <div className="page wide fade-up">
      <div className="section-head">
        <div>
          <h2 style={{ fontSize: 19 }}>Machines</h2>
          <div className="sub">{s.machines.length} nodes · {s.machines.filter((m) => m.online).length} online</div>
        </div>
        <div className="spacer" />
        <div className="seg" title="Row density">
          <button className={density === "comfortable" ? "on" : ""} onClick={() => setDensity("comfortable")} title="Comfortable"><Ic name="machines" size={14} /></button>
          <button className={density === "compact" ? "on" : ""} onClick={() => setDensity("compact")} title="Compact"><Ic name="layers" size={14} /></button>
        </div>
        <RefreshBtn />
        <button className="btn primary" onClick={() => setModal({ type: "register" })}><Ic name="plus" size={15} />Register machine</button>
      </div>

      {s.pending.length > 0 && <PendingRegistrations openApprove={(p) => setModal({ type: "approvePending", p })} />}

      <div className="tbl-wrap">
        {/* toolbar */}
        <div className="tbl-toolbar">
          <div className="tb-search" style={{ minWidth: 260 }} onClick={(e) => e.currentTarget.querySelector("input").focus()}>
            <Ic name="search" size={15} />
            <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Filter by name, IP, tag, user…" style={{ border: "none", background: "none", outline: "none", flex: 1, color: "var(--text)" }} />
          </div>
          <FilterPill label="User" value={filterUser} onChange={setFilterUser}
            options={[["all", "All users"], ...s.users.map((u) => [String(u.id), u.name])]} />
          <FilterPill label="State" value={filterState} onChange={setFilterState}
            options={[["all", "Any state"], ["online", "Online"], ["offline", "Offline"], ["expiring", "Expiring"]]} />
          <div style={{ flex: 1 }} />
          <span style={{ fontSize: 12.5, color: "var(--text-faint)" }}>{filtered.length} shown</span>
        </div>

        {/* selection bar */}
        {sel.size > 0 && (
          <div className="sel-bar">
            <span><b>{sel.size}</b> selected</span>
            <div style={{ flex: 1 }} />
            <button className="btn sm" onClick={() => { setModal({ type: "bulkTag", ids: [...sel] }); }}><Ic name="tag" size={14} />Tag</button>
            <button className="btn sm" onClick={() => { setModal({ type: "bulkMove", ids: [...sel] }); }}><Ic name="move" size={14} />Assign user</button>
            <button className="btn sm danger" onClick={() => setModal({ type: "bulkExpire", ids: [...sel] })}><Ic name="clock" size={14} />Expire</button>
            <button className="btn sm danger" onClick={() => setModal({ type: "bulkDelete", ids: [...sel] })}><Ic name="trash" size={14} />Delete</button>
            <button className="btn sm ghost" onClick={() => setSel(new Set())}>Clear</button>
          </div>
        )}

        <div className="tbl-scroll">
          <table className={"tbl" + (density === "compact" ? " compact" : "")}>
            <thead>
              <tr>
                <th style={{ width: 38 }}><span className={"checkbox" + (allSel ? " on" : "")} onClick={toggleAll}><Ic name="check" size={11} /></span></th>
                <th className="sortable" onClick={() => toggleSort("name")}>Machine <Arrow k="name" /></th>
                <th className="sortable hide-sm" onClick={() => toggleSort("ip")}>Addresses <Arrow k="ip" /></th>
                <th className="sortable" onClick={() => toggleSort("user")}>Owner <Arrow k="user" /></th>
                <th className="hide-sm">Method</th>
                <th className="hide-sm">Version</th>
                <th className="sortable" onClick={() => toggleSort("lastSeen")}>Last seen <Arrow k="lastSeen" /></th>
                <th style={{ width: 50 }}></th>
              </tr>
            </thead>
            <tbody>
              {filtered.map((m) => {
                const u = s.userById(m.user);
                const exp = expiryInfo(m.expiry);
                return (
                  <tr key={m.id} className={sel.has(m.id) ? "sel" : ""} onClick={() => setDrawer(m.id)}>
                    <td onClick={(e) => { e.stopPropagation(); toggleOne(m.id); }}><span className={"checkbox" + (sel.has(m.id) ? " on" : "")}><Ic name="check" size={11} /></span></td>
                    <td>
                      <div className="cell-main">
                        <span className={"dot-status " + (m.online ? "online" : "offline")} />
                        <Ic name={osIcon(m.kind, m.os)} size={17} style={{ color: "var(--text-dim)" }} />
                        <span>{m.name}</span>
                        {m.ephemeral && <span className="badge" title="Ephemeral node" style={{ height: 18 }}>ephemeral</span>}
                        {exp.soon && <span className={"badge " + exp.cls} style={{ height: 18 }}>{exp.label}</span>}
                      </div>
                    </td>
                    <td className="hide-sm">
                      <div style={{ display: "flex", alignItems: "center", gap: 7 }}>
                        <span className="ip">{m.ip4}</span><CopyBtn text={m.ip4} label={m.ip4} />
                      </div>
                      <div className="cell-sub" style={{ fontSize: 11 }}>{m.ip6}</div>
                    </td>
                    <td><OwnerCell m={m} /></td>
                    <td className="hide-sm"><span className="badge" title="How this node registered">{regMethodShort(m.registerMethod)}</span></td>
                    <td className="hide-sm"><span className="mono" style={{ fontSize: 12, color: "var(--text-dim)" }}>{m.client}</span></td>
                    <td>
                      {m.online ? <span className="badge online"><span className="dot" />Connected</span>
                        : <span style={{ fontSize: 12.5, color: "var(--text-faint)" }}>{relTime(m.lastSeen)}</span>}
                    </td>
                    <td>
                      <div className="row-actions" onClick={(e) => e.stopPropagation()}>
                        <Menu trigger={<button className="btn icon sm ghost"><Ic name="dots" size={16} /></button>}>
                          <MenuItem icon="eye" onClick={() => setDrawer(m.id)}>View details</MenuItem>
                          <MenuItem icon="edit" onClick={() => setModal({ type: "rename", machine: m })}>Rename</MenuItem>
                          <MenuItem icon="tag" onClick={() => setModal({ type: "tag", machine: m })}>{m.tags.length ? "Edit tags" : "Set tags"}</MenuItem>
                          <MenuItem icon="move" onClick={() => setModal({ type: "move", machine: m })}>Assign to user</MenuItem>
                          <div className="menu-sep" />
                          <MenuItem icon="clock" danger onClick={() => setModal({ type: "expire", machine: m })}>Expire key</MenuItem>
                          <MenuItem icon="trash" danger onClick={() => setModal({ type: "delete", machine: m })}>Delete node</MenuItem>
                        </Menu>
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          {filtered.length === 0 && (
            <div className="empty"><Ic name="machines" size={38} /><h3>No machines match</h3><div>Try clearing filters or registering a new node.</div></div>
          )}
        </div>
      </div>

      {modal && <MachineModals modal={modal} close={() => setModal(null)} clearSel={() => setSel(new Set())} />}
      {drawer && <MachineDrawer id={drawer} close={() => setDrawer(null)} openModal={setModal} />}
    </div>
  );
}

function FilterPill({ label, value, onChange, options }) {
  const cur = options.find((o) => o[0] === value);
  return (
    <Menu trigger={
      <button className="btn sm"><Ic name="filter" size={14} /><span style={{ color: "var(--text-faint)" }}>{label}:</span>&nbsp;{cur ? cur[1] : "All"}<Ic name="chevDown" size={13} /></button>
    } align="left">
      {options.map((o) => (
        <MenuItem key={o[0]} icon={value === o[0] ? "check" : undefined} onClick={() => onChange(o[0])}>{o[1]}</MenuItem>
      ))}
    </Menu>
  );
}

window.Machines = Machines;

/* ---- Pending registration queue (auth/approve, auth/reject) ---- */
function PendingRegistrations({ openApprove }) {
  const s = useStore();
  const toast = useToast();
  return (
    <div className="card" style={{ marginBottom: 14, borderColor: "var(--accent-line)" }}>
      <div className="tbl-toolbar" style={{ background: "var(--accent-soft)" }}>
        <Ic name="clock" size={16} style={{ color: "var(--accent)" }} />
        <b style={{ fontSize: 13.5, color: "var(--accent)" }}>{s.pending.length} node{s.pending.length > 1 ? "s" : ""} awaiting approval</b>
        <span className="sub" style={{ color: "var(--text-dim)", fontSize: 12 }}>&nbsp;— from OIDC / interactive login</span>
      </div>
      <div className="tbl-scroll">
        <table className="tbl">
          <tbody>
            {s.pending.map((p) => (
              <tr key={p.authId} style={{ cursor: "default" }}>
                <td style={{ width: 30 }}><Ic name={osIcon(p.os === "macos" ? "laptop" : "server", p.os)} size={16} style={{ color: "var(--text-dim)" }} /></td>
                <td><div className="cell-main">{p.node}</div><div className="cell-sub">{p.key}</div></td>
                <td className="hide-sm"><span className="badge">{p.method === "oidc" ? "OIDC" : "CLI"}</span></td>
                <td className="hide-sm">wants user <span className="mono" style={{ color: "var(--text-dim)" }}>{p.requestedUser}</span></td>
                <td style={{ color: "var(--text-faint)", fontSize: 12 }}>{relTime(p.t)}</td>
                <td>
                  <div style={{ display: "flex", gap: 6, justifyContent: "flex-end" }}>
                    <button className="btn sm primary" onClick={() => openApprove(p)}><Ic name="check" size={14} />Approve</button>
                    <button className="btn sm danger" onClick={() => { HS.act.rejectPending(p.authId); toast.push({ kind: "info", title: "Registration rejected", msg: p.node }); }}>Reject</button>
                  </div>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}
window.PendingRegistrations = PendingRegistrations;
