/* install.jsx — "Add to Home Screen" nudge.

   Why this exists beyond convenience: on iPhone, Web Push does NOT work in a
   Safari tab at all — the app must be installed to the Home Screen first. So for
   a large share of members this prompt is the precondition for notifications
   working, not a nice-to-have.

   What can actually be detected (and what can't):
   - `display-mode: standalone` / `navigator.standalone` tell us how THIS session
     was launched — not whether an install exists. Someone who installed the app
     and is also browsing in a tab looks identical to someone who never did. We
     accept that: the nudge is snoozeable and capped, so a false positive costs
     one dismissal.
   - Android/desktop Chromium fire `beforeinstallprompt`, which we stash and
     replay from a real button. Its firing is itself decent evidence of "not
     installed" (Chromium suppresses it once installed).
   - iOS/iPadOS Safari has NO install API, ever. All we can do is teach the
     Share → Add to Home Screen gesture.
   - Inside Discord's in-app browser there is no Add-to-Home-Screen option AT ALL,
     so telling that user to "tap Share" is a dead end — they must open in Safari
     first, and the copy says exactly that.
*/

// Launched from an installed icon? True for Android/desktop PWAs and iOS Home Screen.
function isStandalone() {
  try {
    return ["standalone", "fullscreen", "minimal-ui"].some(m => window.matchMedia(`(display-mode: ${m})`).matches)
      || window.navigator.standalone === true;
  } catch (_) { return false; }
}
const _ua = () => (typeof navigator !== "undefined" ? String(navigator.userAgent || "") : "");
function isIOS() {
  // iPadOS 13+ reports a desktop UA, so the touch check is the one that catches it.
  return /iPad|iPhone|iPod/.test(_ua())
    || (/Macintosh/.test(_ua()) && typeof document !== "undefined" && "ontouchend" in document);
}
// Discord's webview (and other in-app browsers) can't install anything.
function isInAppBrowser() {
  return /(Discord|FBAN|FBAV|Instagram|Twitter|Line\/|MicroMessenger)/i.test(_ua());
}

const INSTALL_SNOOZE_DAYS = 14;   // re-offer cadence
const INSTALL_MAX_SHOWS = 4;      // then stop asking forever

function installState(uid) {
  const key = "install:nudge:" + (uid || "anon");
  let s = { shows: 0, until: 0, done: false };
  try { s = { ...s, ...(JSON.parse(localStorage.getItem(key) || "null") || {}) }; } catch (_) {}
  return [s, (next) => { try { localStorage.setItem(key, JSON.stringify(next)); } catch (_) {} }];
}

function InstallPrompt({ userId, ready }) {
  const [show, setShow] = useState(false);
  const [bip, setBip] = useState(null);          // stashed beforeinstallprompt
  const [state, setState] = useState(() => installState(userId)[0]);

  // Stash Chromium's install event. Must preventDefault to stop the mini-infobar,
  // and it only fires when the app is genuinely installable.
  useEffect(() => {
    const onBip = (e) => { e.preventDefault(); setBip(e); };
    const onInstalled = () => {
      setShow(false);
      const [, write] = installState(userId);
      write({ shows: 0, until: 0, done: true });
      toast.success("Installed — open Sinux Signals from your home screen.");
    };
    window.addEventListener("beforeinstallprompt", onBip);
    window.addEventListener("appinstalled", onInstalled);
    return () => { window.removeEventListener("beforeinstallprompt", onBip); window.removeEventListener("appinstalled", onInstalled); };
  }, [userId]);

  useEffect(() => {
    if (!ready || !userId) return;
    if (isStandalone()) return;                       // already running installed
    const [s] = installState(userId);
    setState(s);
    if (s.done || s.shows >= INSTALL_MAX_SHOWS) return;
    if (s.until && Date.now() < s.until) return;
    // Only nudge where there's a real path: an installable Chromium, or iOS Safari
    // (manual gesture), or an in-app browser we can redirect out of.
    const usable = !!bip || isIOS() || isInAppBrowser();
    if (!usable) return;
    const t = setTimeout(() => {
      setShow(true);
      // Count the SHOW here. Counting it only on dismissal meant a user who ignored
      // the nudge (closed the tab, navigated away) never advanced the counter — so
      // the 14-day snooze and the 4-show cap could never be reached and it would
      // reappear every session forever.
      const [s2, write] = installState(userId);
      write({ ...s2, shows: (s2.shows || 0) + 1, until: Date.now() + INSTALL_SNOOZE_DAYS * 86400e3 });
    }, 4000);                                          // never race the first paint
    return () => clearTimeout(t);
  }, [ready, userId, bip]);

  // The show was already counted and snoozed when it appeared; dismissing just hides it.
  function close() { setShow(false); }
  function never() {
    const [s, write] = installState(userId);
    write({ ...s, done: true });
    setShow(false);
  }
  async function install() {
    if (!bip) return;
    try {
      bip.prompt();
      const res = await bip.userChoice;
      setBip(null);
      if (res && res.outcome === "accepted") { never(); return; }   // appinstalled also fires
    } catch (_) {}
    close();
  }

  if (!show) return null;
  const inApp = isInAppBrowser();
  const ios = isIOS();
  return (
    <div className="install-nudge" role="dialog" aria-labelledby="install-h">
      <button className="icon-btn install-x" title="Dismiss" onClick={() => close()}>{I("close", { size: 14 })}</button>
      <div className="install-body">
        <div className="install-icon">{I("bell", { size: 18 })}</div>
        <div className="install-txt">
          <div id="install-h" className="install-title">Add Sinux Signals to your home screen</div>
          {inApp ? (
            <div className="install-sub">
              You're in Discord's built-in browser, which can't install apps. Tap the <strong>⋯</strong> menu
              and choose <strong>Open in browser</strong> first — then add it from there.
            </div>
          ) : ios ? (
            <div className="install-sub">
              Tap <strong>Share</strong> {I("upload", { size: 12 })} at the bottom of Safari, then
              <strong> Add to Home Screen</strong>. On iPhone this is also the only way alerts can reach you
              when the app is closed. You'll sign in once more inside the app.
            </div>
          ) : (
            <div className="install-sub">
              Opens like a real app, full screen, and lets alerts reach you when it's closed.
            </div>
          )}
        </div>
      </div>
      <div className="install-actions">
        <button className="btn btn-sm ghost" onClick={never}>Don't ask again</button>
        {bip
          ? <button className="btn btn-sm primary" onClick={install}>Install</button>
          : <button className="btn btn-sm primary" onClick={() => close()}>Got it</button>}
      </div>
    </div>
  );
}

Object.assign(window, { InstallPrompt, isStandalone });
