/* The More menu's in-place dialogs (pitchiq#257) — the React half of more-app-actions.js.

   Holds only what React must hold: which app's dialog is open, and whether its write is in flight.
   Every decision and every answer's handling lives in more-app-actions.js (tested without a DOM). The
   dialogs are the shell's own (`window.EvolveShell.LegalDialog`, `.RequestAccessDialog`), the same
   components the Suite and Coach render, so the three products show one dialog.

   Mounted once, next to the SupportDialog, by nav.jsx's Shell; the rail's `onSelectApp` calls
   `window.selectMoreApp`. */

/**
 * The Suite's public Legal Documents view for a document path (`/legal/coach-terms`), or null when no
 * Suite URL is known. Edge has no Legal Documents view of its own, and `productLegalDocuments` hands
 * back a path Edge does not serve — so the row opens the Suite's page in a NEW tab, keeping this dialog
 * where it is. An absolute path resolves against the Suite's origin, so `ctx.suiteHref`'s own path and
 * `?wsid=` cannot leak into it.
 */
function suiteDocumentHref(ctx, path) {
  if (!ctx.suiteHref) return null;
  try { return new URL(path, ctx.suiteHref).toString(); } catch (e) { return null; }
}

function MoreAppDialogs({ ctx }) {
  const toast = useToast();
  const [pending, setPending] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  // A ref, not `busy`, guards the write: two clicks inside one render share its closure, so the state
  // would read `false` twice and send twice (the Suite's review-R4 lesson). `busy` only drives the UI.
  const busyRef = React.useRef(false);
  const shell = window.EvolveShell;
  const actions = window.MoreAppActions;

  // Flow 14 (evolve-suite#1302): entering an app shows that app's Product loader first, then navigates. An app the shell
  // has no colour for has no loader and is entered at once, as before.
  const enterApp = (app) =>
    window.SwitchLoader.enter(window.SwitchLoader.productSpec(shell, app), () => { window.location.href = app.href; });

  // The rail's `onSelectApp` (nav.jsx) lands here. Re-assigned on every render so it always closes over
  // the CURRENT ctx and toast; exactly one Shell is mounted at a time.
  window.selectMoreApp = (app) => {
    const decision = actions.dialogFor(app, ctx, shell);
    if (decision.kind === "none") return;
    if (decision.kind === "follow") { enterApp(app); return; }
    if (decision.kind === "error") { toast.error(actions.ERROR_TEXT); return; }
    setPending({ app, decision });
  };

  const deps = {
    psa: window.PSA,
    toast,
    enter: enterApp,
    // A stale answer: re-read /api/shell (nav.jsx's useShellContext listens for this).
    refresh: () => window.dispatchEvent(new Event("mp-state")),
  };

  async function run(handler) {
    if (pending === null || busyRef.current) return;
    busyRef.current = true;
    setBusy(true);
    try {
      if ((await handler(pending, ctx, deps)) === "close") setPending(null);
    } finally {
      busyRef.current = false;
      setBusy(false);
    }
  }

  // Cancel keeps the user in Edge (L197, L275, L301): drop the pending app, send nothing. Not while a
  // write is in flight — its answer would still act after the dialog had gone.
  const close = () => { if (!busyRef.current) setPending(null); };

  const legal = pending && pending.decision.kind === "legal" ? pending : null;
  const request = pending && pending.decision.kind === "request" ? pending : null;
  const documents = legal
    ? shell.productLegalDocuments(legal.app.key, () => () => {})
        .map((doc) => {
          const href = suiteDocumentHref(ctx, doc.href);
          return href === null ? null : { ...doc, href, onOpen: () => { window.open(href, "_blank", "noopener"); } };
        })
        .filter((doc) => doc !== null)
    : [];

  return (
    <>
      <shell.LegalDialog
        open={legal !== null}
        appName={legal ? legal.app.name : ""}
        variant={legal ? legal.decision.variant : "use"}
        // The team notice names the workspace (L293); the personal dialog has none (L188).
        workspaceName={legal && legal.decision.variant === "enable" ? ctx.workspace.name : undefined}
        documents={documents}
        busy={busy}
        onAccept={() => { void run(actions.acceptLegal); }}
        onClose={close}
      />
      <shell.RequestAccessDialog
        open={request !== null}
        appName={request ? request.app.name : ""}
        workspaceName={ctx.workspace.name}
        // More offers only not-enabled apps; an app this member may not use is hidden (#1297).
        reason="not-enabled"
        busy={busy}
        onRequest={() => { void run(actions.sendRequest); }}
        onClose={close}
      />
    </>
  );
}

window.MoreAppDialogs = MoreAppDialogs;
