/* Routes & exit nodes — a PROJECTION over nodes (v0.26+ model).
   Routes are not first-class objects: each node has availableRoutes (advertised,
   read-only) and approvedRoutes (the admin-approved set, writable via approve_routes,
   which REPLACES the set). "Served/Primary" is derived (online + primary election). */
function Routes() {
  const s = useStore();
  const toast = useToast();

  // build subnet-route rows (exclude exit prefixes) + exit-node rows
  const subnetRows = [];
  s.machines.forEach((m) => {
    s.subnetPrefixes(m).forEach((cidr) => {
      const approved = m.approvedRoutes.includes(cidr);
      subnetRows.push({
        m, cidr, approved,
        served: s.isServed(m, cidr),
        primary: approved && s.isPrimary(m, cidr),
        auto: s.isAutoApproved(m, cidr),
      });
    });
  });
  const exitRows = s.machines.filter((m) => s.isExitNode(m)).map((m) => ({
    m, approved: s.exitApproved(m), auto: s.isAutoApproved(m, s.consts.EXIT4),
  }));
  const pendingSubnets = subnetRows.filter((r) => !r.approved).length;

  // group same-prefix rows to surface HA / primary
  const prefixGroups = {};
  subnetRows.forEach((r) => { (prefixGroups[r.cidr] = prefixGroups[r.cidr] || []).push(r); });

  return (
    <div className="page wide fade-up">
      <div className="section-head">
        <div>
          <h2 style={{ fontSize: 19 }}>Routes &amp; exit nodes</h2>
          <div className="sub">{subnetRows.length} advertised subnet routes · {exitRows.length} exit nodes{pendingSubnets ? ` · ${pendingSubnets} awaiting approval` : ""}</div>
        </div>
        <div className="spacer" />
        <RefreshBtn />
        <span className="badge"><Ic name="info" size={12} />approve_routes replaces the set per node</span>
      </div>

      {/* Subnet routes */}
      <div className="card" style={{ marginBottom: 18 }}>
        <div className="tbl-toolbar"><Ic name="routes" size={16} style={{ color: "var(--accent)" }} /><b style={{ fontSize: 13.5 }}>Subnet routes</b>
          <span className="sub" style={{ color: "var(--text-faint)", fontSize: 12 }}>&nbsp;— LAN prefixes advertised by nodes</span>
          <div style={{ flex: 1 }} />{pendingSubnets > 0 && <span className="badge warn">{pendingSubnets} awaiting approval</span>}</div>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead><tr><th>Prefix</th><th>Advertised by</th><th>Owner</th><th>State</th><th style={{ textAlign: "right" }}>Approved</th></tr></thead>
            <tbody>
              {subnetRows.map((r, i) => {
                const group = prefixGroups[r.cidr];
                const ha = group.length > 1;
                return (
                  <tr key={i} style={{ cursor: "default" }}>
                    <td>
                      <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
                        <span className="ip" style={{ fontSize: 13.5, fontWeight: 600 }}>{r.cidr}</span>
                        {r.served && <span className="badge accent" style={{ height: 18 }} title="Currently serving traffic (primary)">primary</span>}
                        {ha && !r.served && r.approved && <span className="badge" style={{ height: 18 }} title="Approved standby for HA failover">standby</span>}
                      </div>
                    </td>
                    <td><div className="cell-main" style={{ fontWeight: 500 }}><span className={"dot-status " + (r.m.online ? "online" : "offline")} /><Ic name={osIcon(r.m.kind, r.m.os)} size={15} style={{ color: "var(--text-dim)" }} />{r.m.name}</div></td>
                    <td><OwnerCell m={r.m} size={20} /></td>
                    <td>
                      {r.approved
                        ? (r.auto
                          ? <span className="badge accent" title="Approved automatically by an autoApprovers policy rule"><Ic name="acl" size={11} />auto-approved</span>
                          : <span className="badge online"><span className="dot" />approved</span>)
                        : <span className="badge warn">available · pending</span>}
                    </td>
                    <td>
                      <div style={{ display: "flex", justifyContent: "flex-end", alignItems: "center", gap: 8 }}>
                        {!r.approved
                          ? <button className="btn sm primary" onClick={() => { HS.act.approveRoute(r.m.id, r.cidr); toast.push({ kind: "success", title: "Route approved", msg: `${r.cidr} added to ${r.m.name}'s approved set` }); }}>Approve</button>
                          : <button className="btn sm" onClick={() => { HS.act.rejectRoute(r.m.id, r.cidr); toast.push({ kind: "info", title: "Approval removed", msg: `${r.cidr} removed from approved set` }); }}>Remove</button>}
                      </div>
                    </td>
                  </tr>
                );
              })}
              {subnetRows.length === 0 && <tr><td colSpan={5}><div className="empty" style={{ padding: 34 }}><Ic name="routes" size={34} /><h3>No subnet routes advertised</h3><div>Run <span className="code-inline">tailscale up --advertise-routes=…</span> on a node.</div></div></td></tr>}
            </tbody>
          </table>
        </div>
      </div>

      {/* Exit nodes */}
      <div className="card">
        <div className="tbl-toolbar"><Ic name="exit" size={16} style={{ color: "var(--info)" }} /><b style={{ fontSize: 13.5 }}>Exit nodes</b>
          <span className="sub" style={{ color: "var(--text-faint)", fontSize: 12 }}>&nbsp;— nodes advertising {s.consts.EXIT4} + {s.consts.EXIT6}</span></div>
        <div className="tbl-scroll">
          <table className="tbl">
            <thead><tr><th>Exit node</th><th>Owner</th><th>Egress prefixes</th><th>State</th><th style={{ textAlign: "right" }}>Approved</th></tr></thead>
            <tbody>
              {exitRows.map((r) => {
                const m = r.m;
                return (
                  <tr key={m.id} style={{ cursor: "default" }}>
                    <td><div className="cell-main"><span className={"dot-status " + (m.online ? "online" : "offline")} /><Ic name="server" size={15} style={{ color: "var(--text-dim)" }} />{m.name}<span style={{ fontSize: 11.5, color: "var(--text-faint)", fontWeight: 400 }}>· {geoFor(m.name)}</span></div></td>
                    <td><OwnerCell m={m} size={20} /></td>
                    <td><div style={{ display: "flex", gap: 4 }}><span className="tag muted">{s.consts.EXIT4}</span><span className="tag muted">{s.consts.EXIT6}</span></div></td>
                    <td>{r.approved ? (r.auto ? <span className="badge accent"><Ic name="acl" size={11} />auto-approved</span> : <span className="badge online"><span className="dot" />approved</span>) : <span className="badge warn">available · pending</span>}</td>
                    <td>
                      <div style={{ display: "flex", justifyContent: "flex-end" }}>
                        <span className={"toggle" + (r.approved ? " on" : "")} title="Approve / remove both egress prefixes" onClick={() => { HS.act.setExitNode(m.id, !r.approved); toast.push({ kind: r.approved ? "info" : "success", title: r.approved ? "Exit node approval removed" : "Exit node approved", msg: m.name }); }} />
                      </div>
                    </td>
                  </tr>
                );
              })}
              {exitRows.length === 0 && <tr><td colSpan={5}><div className="empty" style={{ padding: 34 }}><Ic name="exit" size={34} /><h3>No exit nodes</h3><div>Advertise with <span className="code-inline">tailscale up --advertise-exit-node</span>.</div></div></td></tr>}
            </tbody>
          </table>
        </div>
      </div>

      <div style={{ marginTop: 14, display: "flex", gap: 9, color: "var(--text-dim)", fontSize: 12.5, padding: "11px 14px", background: "var(--surface-2)", border: "1px solid var(--border)", borderRadius: 10 }}>
        <Ic name="info" size={16} style={{ flexShrink: 0, marginTop: 1, color: "var(--accent)" }} />
        <div>Headscale has no standalone routes API since v0.26. Approval edits each node's <b>approved set</b> (<span className="code-inline">POST /api/v1/node/&#123;id&#125;/approve_routes</span>). <b>Primary</b> is the node currently serving a prefix; others approving the same prefix are HA standbys with automatic failover. <b>auto-approved</b> routes matched an <span className="code-inline">autoApprovers</span> policy rule.</div>
      </div>
    </div>
  );
}
function geoFor(name) {
  if (name.includes("fra")) return "Frankfurt";
  if (name.includes("sin")) return "Singapore";
  if (name.includes("gateway")) return "us-east-1";
  return "—";
}
window.Routes = Routes;
