// Reactive PWA install runtime: captures the Chromium install prompt, tracks the installed
// state, and registers the install-only service worker. The pure platform branching lives in
// pwa.ts. Structured after the other reactive lib modules (app.svelte.ts, router.svelte.ts).

import { isIosSafari, isStandalone, resolveInstallMode, type InstallMode } from './pwa';
import { insideTelegram } from './telegram';
import { insideVK } from './vk';
import { clientChannel } from './channel';

/** inMiniApp reports whether the app runs inside a Telegram or VK Mini App webview, where a web
 *  install makes no sense (the platform manages its own launcher). Kept here, out of the pure
 *  pwa.ts, so that module stays free of app imports for the node unit tests. */
function inMiniApp(): boolean {
  return insideTelegram() || insideVK();
}

// beforeinstallprompt is a Chromium-only event, absent from the DOM lib types.
interface BeforeInstallPromptEvent extends Event {
  readonly userChoice: Promise<{ outcome: 'accepted' | 'dismissed'; platform: string }>;
  prompt(): Promise<void>;
}
declare global {
  interface WindowEventMap {
    beforeinstallprompt: BeforeInstallPromptEvent;
    appinstalled: Event;
  }
}

// Not named `state` (a Svelte-check hazard: `$state` would then read as a store subscription).
const install = $state<{ deferred: BeforeInstallPromptEvent | null; installed: boolean }>({
  deferred: null,
  installed: isStandalone(),
});

/**
 * installMode is the reactive install-CTA mode for the current platform and any captured prompt.
 * Read inside a component's markup / $derived so the CTA updates when a Chromium prompt arrives
 * (beforeinstallprompt fires shortly after load) or the app is installed.
 */
export function installMode(): InstallMode {
  return resolveInstallMode({
    deferredAvailable: install.deferred !== null,
    standalone: install.installed,
    iosSafari: isIosSafari(),
    inMiniApp: inMiniApp(),
  });
}

/**
 * initInstallListeners wires the Chromium install prompt and the installed event. Harmless on
 * any platform — the events fire only in an installable browser — so it may run unconditionally
 * at startup, early enough to catch a prompt that fires before the CTA mounts.
 */
export function initInstallListeners(): void {
  if (typeof window === 'undefined') return;
  window.addEventListener('beforeinstallprompt', (e) => {
    // Suppress Chromium's mini-infobar; the app presents its own CTA instead.
    e.preventDefault();
    install.deferred = e;
  });
  window.addEventListener('appinstalled', () => {
    install.deferred = null;
    install.installed = true;
  });
}

/**
 * promptInstall shows the native Chromium install dialog. It MUST be called directly from a user
 * gesture (the deferred prompt is gesture-gated — no await before it). A deferred prompt is
 * single-use, so it is cleared here; a no-op when nothing was captured.
 */
export async function promptInstall(): Promise<void> {
  const deferred = install.deferred;
  if (!deferred) return;
  install.deferred = null;
  await deferred.prompt();
}

/**
 * registerServiceWorker registers the app-shell service worker (built from ui/src/sw.ts to
 * dist/sw.js by vite-plugin-pwa): it satisfies Chromium's installability requirement and precaches
 * the shell + assets so an installed PWA cold-launches offline. Web-only: skipped in the mock build
 * (a real worker would perturb the Playwright run), inside a Telegram/VK Mini App, and inside the
 * Capacitor native shell (the bundle is already local, and a worker would risk serving stale assets
 * across store updates). Best-effort — a failed registration only means the app is not installable
 * and cannot launch offline.
 */
export function registerServiceWorker(): void {
  if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) return;
  if (import.meta.env.MODE === 'mock') return;
  if (inMiniApp()) return;
  const ch = clientChannel();
  if (ch === 'android' || ch === 'ios') return;
  void navigator.serviceWorker.register('sw.js').catch(() => {});
}
