// offline.svelte.ts: the offline-mode read surface (a thin shim over the net-state store,
// netstate.svelte.ts) plus the orthogonal dictionary-preload warning + background preload. Offline is
// implicit — detected by the net-state machine — so offlineMode.active just derives from netState; there
// is no deliberate toggle. The dict-preload pieces are unrelated to connectivity and stay live here.

import { netState } from './netstate.svelte';
import { offlinePreloadEligible } from './offline';
import { isStandalone } from './pwa';
import { insideTelegram } from './telegram';
import { insideVK } from './vk';
import type { Profile } from './model';

/**
 * offlineCapable reports whether this channel supports the offline model — implicit offline mode,
 * device-local games (vs_ai and hotseat) and the transport kill switch. Native builds and the plain web
 * app do; the Telegram and VK mini-apps are online-only wrappers, so the whole offline model stays inert
 * there (offlineMode.active is forced false regardless of the detected net state, and the create flow
 * hides its device-local segment).
 */
export function offlineCapable(): boolean {
  return !insideTelegram() && !insideVK();
}

/** offlineMode exposes the reactive offline flags; read them in markup / $derived. */
export const offlineMode = {
  /** active is true while the app is offline (the machine's offlineNoNetwork / offlineVersionLocked) on a
   *  channel that supports offline play; it is always false in the online-only Telegram/VK mini-apps, so
   *  every offline-model consumer (chrome, lobby, kill switch, device-local create) stays inert there. */
  get active(): boolean {
    return netState.offline && offlineCapable();
  },
};

// The dict-preload warning: true when a first-lobby background preload could not fetch every enabled
// variant's dictionary (typically a poor connection), so offline play may be incomplete. The lobby
// shows a notice in place of the ad banner while it holds.
let preloadWarn = $state(false);

/** dictPreloadWarning exposes the reactive preload-failure flag; read it in markup / $derived. */
export const dictPreloadWarning = {
  /** active is true while a first-lobby dictionary preload has left a variant unavailable. */
  get active(): boolean {
    return preloadWarn;
  },
};

/** setDictPreloadWarning raises or clears the in-lobby preload-failure notice. */
export function setDictPreloadWarning(on: boolean): void {
  preloadWarn = on;
}

// A preload runs at most once at a time; it finishes before a fresh trigger (a repeated lobby mount or
// a variant-preference change) can start another. Module-scoped so mounts do not stack.
let preloadInFlight = false;

/**
 * kickDictPreload starts a background preload of the enabled variants' dictionaries for an
 * offline-capable install (a standalone web PWA with a confirmed email) while online, so a later switch
 * to offline mode already has the data. It is a no-op in a Telegram/VK mini-app, in a plain browser tab,
 * without a confirmed email, while offline, or when a preload is already running; getDawg's caching
 * makes a repeat run cheap. When warnOnFail is set (the first lobby entry), a fetch failure raises the
 * in-lobby notice, and a later successful run clears it. The dict loader and generator are imported
 * dynamically, so neither is pulled into the main bundle.
 */
export function kickDictPreload(prof: Profile | null, warnOnFail = false): void {
  if (preloadInFlight || !prof) return;
  const eligible = offlinePreloadEligible({
    hasEmail: !!prof.email,
    standalone: isStandalone(),
    inTelegram: insideTelegram(),
    inVK: insideVK(),
    online: typeof navigator === 'undefined' || navigator.onLine !== false,
  });
  if (!eligible) return;
  preloadInFlight = true;
  void import('./dict/preloadrun')
    .then((m) => m.runPreload(prof, warnOnFail))
    .catch(() => {
      /* best-effort warmup — a failed dynamic import just leaves offline data unprimed */
    })
    .finally(() => {
      preloadInFlight = false;
    });
}
