// connection.jsx — topbar status chip showing online / reconnecting /
// offline state, plus a click-popover listing pending outbox entries.
//
// State sources (combined into a single derived state):
//   - SSE state: realtime.js dispatches `flowboard:conn` with detail
//     { sse: "open" | "connecting" | "closed" }
//   - Outbox: any failed write makes the queue non-empty
//   - navigator.onLine: the browser's own offline detection
//
// Derived buckets (worst-case wins):
//   - "online"     : SSE open + outbox empty + navigator.onLine
//   - "reconnecting" : SSE connecting OR outbox has pending writes (server briefly unreachable)
//   - "offline"    : navigator.onLine === false  OR  SSE closed AND queue is non-empty
//
// The chip itself is intentionally minimal — a coloured dot + a 1-line
// label. Click expands a popover where the user can see what's pending,
// hit "Retry now", or "Discard" individual entries.

function ConnectionChip() {
  const [sseState, setSseState] = React.useState("open");   // open | connecting | closed
  const [navOnline, setNavOnline] = React.useState(
    typeof navigator !== "undefined" ? navigator.onLine !== false : true
  );
  const [outbox, setOutbox] = React.useState({ length: 0, items: [], flushing: false, lastFailedKind: null });
  const [open, setOpen] = React.useState(false);
  const anchorRef = React.useRef(null);

  // SSE connection state — realtime.js fires this whenever it transitions.
  React.useEffect(() => {
    function onConn(e) {
      if (e && e.detail && e.detail.sse) setSseState(e.detail.sse);
    }
    window.addEventListener("flowboard:conn", onConn);
    return () => window.removeEventListener("flowboard:conn", onConn);
  }, []);

  // Browser online/offline events.
  React.useEffect(() => {
    function on()  { setNavOnline(true); }
    function off() { setNavOnline(false); }
    window.addEventListener("online",  on);
    window.addEventListener("offline", off);
    return () => {
      window.removeEventListener("online",  on);
      window.removeEventListener("offline", off);
    };
  }, []);

  // Outbox subscription. window.flowboardOutbox is created by outbox.js
  // before this component mounts (script tag order in index.html).
  React.useEffect(() => {
    if (!window.flowboardOutbox) return;
    return window.flowboardOutbox.subscribe(setOutbox);
  }, []);

  // Derive the single status everyone can read.
  const status = (() => {
    if (!navOnline) return "offline";
    if (sseState === "closed" && outbox.length > 0) return "offline";
    if (sseState === "connecting") return "reconnecting";
    if (outbox.length > 0)       return "reconnecting";
    return "online";
  })();

  const label =
    status === "offline"      ? (outbox.length ? `${outbox.length} pending · offline` : "Offline")
    : status === "reconnecting" ? (outbox.length ? `${outbox.length} pending` : "Reconnecting…")
    : "Online";

  // Auto-hide the chip when everything's healthy — no point cluttering
  // the topbar. It pops back the moment something needs attention.
  if (status === "online") return null;

  return (
    <div ref={anchorRef} className={`conn-chip is-${status}`}
         onClick={() => setOpen(o => !o)}
         role="button" tabIndex={0}
         title="Connection status">
      <span className="conn-chip-dot"/>
      <span className="conn-chip-label">{label}</span>
      {open && (
        <Popover anchor={anchorRef.current} onClose={() => setOpen(false)}>
          <div className="conn-popover" onClick={(e) => e.stopPropagation()}>
            <div className="conn-popover-head">
              <div className="conn-popover-title">{label}</div>
              <div className="conn-popover-sub">
                {status === "offline"
                  ? "Your edits are saved on this device. They'll sync the moment you're back online."
                  : status === "reconnecting"
                  ? "Reconnecting to the server — your changes will retry automatically."
                  : "Everything in sync."}
              </div>
            </div>

            {outbox.length > 0 && (
              <>
                <div className="conn-popover-section">
                  Pending changes ({outbox.length})
                </div>
                <div className="conn-popover-list">
                  {outbox.items.map(it => (
                    <div key={it.id} className="conn-popover-item">
                      <div className="conn-popover-item-main">
                        <div className="conn-popover-item-label">{it.label || _kindLabel(it.kind)}</div>
                        <div className="conn-popover-item-sub">
                          {it.attempts > 0
                            ? `${it.attempts} attempt${it.attempts === 1 ? "" : "s"} — ${it.lastError || "failed"}`
                            : "Waiting…"}
                        </div>
                      </div>
                      <button className="conn-popover-discard"
                              onClick={() => window.flowboardOutbox.discard(it.id)}
                              title="Drop this change without saving">
                        ✕
                      </button>
                    </div>
                  ))}
                </div>
              </>
            )}

            <div className="conn-popover-actions">
              <button className="btn"
                      disabled={outbox.flushing || outbox.length === 0}
                      onClick={() => window.flowboardOutbox.flush()}>
                {outbox.flushing ? "Retrying…" : "Retry now"}
              </button>
              {outbox.length > 0 && (
                <button className="btn conn-popover-clear"
                        onClick={() => {
                          if (confirm(`Discard ${outbox.length} pending change${outbox.length===1?"":"s"}? This can't be undone.`)) {
                            window.flowboardOutbox.clear();
                          }
                        }}>
                  Discard all
                </button>
              )}
            </div>
          </div>
        </Popover>
      )}
    </div>
  );
}

function _kindLabel(kind) {
  switch (kind) {
    case "tasks.patch":     return "Task edit";
    case "tasks.move":      return "Task moved";
    case "tasks.assignees": return "Owner change";
    case "tasks.remove":    return "Task delete";
    default:                return kind;
  }
}

Object.assign(window, { ConnectionChip });
