// Dispatch a game id to its source: the local engine (an offline vs_ai game) or the network
// gateway. The same game screen (game/Game.svelte) drives both through the shared game-loop subset
// (GameLoopSource); a game id decides which. A network game's calls are unchanged — gameSource
// returns the gateway itself for it — so the seam adds the local path without touching online play.
//
// The offline engine is kept OUT of the app entry bundle: this module statically imports only the
// gateway, the tiny id helper and types; the LocalSource is dynamically imported on first use of a
// local game, so it loads as a separate chunk only when someone plays offline.

import { gateway } from './gateway';
import { isLocalGameId } from './localgame/id';
import type { GameLoopSource, LocalSource } from './localgame/source';

let loaded: Promise<LocalSource> | null = null;
function load(): Promise<LocalSource> {
  if (!loaded) loaded = import('./localgame/source').then((m) => new m.LocalSource());
  return loaded;
}

/**
 * localSource is a lazy handle on the single offline LocalSource. It is obtainable synchronously,
 * but each method dynamically imports the engine on first use, so the offline code stays out of the
 * app entry bundle until a local game is actually played. It exposes the game-loop methods plus the
 * local-only create() and the robot-reply event subscription events().
 */
export const localSource = {
  // Offline scoring is self-contained, so the local source ignores includeAlphabet and the evaluate
  // abort signal; the proxy accepts the full gateway signatures but forwards only what the local
  // source uses.
  gameState: (id, _includeAlphabet) => load().then((s) => s.gameState(id)),
  gameHistory: (id) => load().then((s) => s.gameHistory(id)),
  submitPlay: (id, tiles, variant) => load().then((s) => s.submitPlay(id, tiles, variant)),
  pass: (id) => load().then((s) => s.pass(id)),
  exchange: (id, tiles, variant) => load().then((s) => s.exchange(id, tiles, variant)),
  resign: (id) => load().then((s) => s.resign(id)),
  hint: (id) => load().then((s) => s.hint(id)),
  evaluate: (id, tiles, variant, _signal) => load().then((s) => s.evaluate(id, tiles, variant)),
  checkWord: (id, word, variant) => load().then((s) => s.checkWord(id, word, variant)),
  draftGet: (id) => load().then((s) => s.draftGet(id)),
  draftSave: (id, json) => load().then((s) => s.draftSave(id, json)),
  create: (opts) => load().then((s) => s.create(opts)),
  list: () => load().then((s) => s.list()),
  delete: (id) => load().then((s) => s.delete(id)),
  // Offline hotseat controls (no network equivalent): reveal a PIN-locked seat, and the host
  // (referee) overrides gated by the master PIN.
  unlockSeat: (id, pin) => load().then((s) => s.unlockSeat(id, pin)),
  verifyHostPin: (id, pin) => load().then((s) => s.verifyHostPin(id, pin)),
  hostAction: (id, pin, action, targetSeat) => load().then((s) => s.hostAction(id, pin, action, targetSeat)),
  // relock is fire-and-forget: the engine is already loaded (the game was open), and re-locking a
  // cached game before the screen is left needs no result.
  relock: (id) => void load().then((s) => s.relock(id)),
  // events must return the unsubscribe synchronously (the screen subscribes in onMount), so it wires
  // the real subscription once the engine loads and, until then, cancels a pending subscribe.
  events: (id, onEvent) => {
    let unsub = (): void => {};
    let cancelled = false;
    void load().then((s) => {
      if (!cancelled) unsub = s.events(id, onEvent);
    });
    return () => {
      cancelled = true;
      unsub();
    };
  },
} satisfies GameLoopSource &
  Pick<LocalSource, 'events' | 'create' | 'list' | 'delete' | 'unlockSeat' | 'verifyHostPin' | 'hostAction' | 'relock'>;

/**
 * gameSource returns the source that runs the game with the given id: the local engine for a local
 * id, otherwise the network gateway (which satisfies the same game-loop interface).
 */
export function gameSource(id: string): GameLoopSource {
  return isLocalGameId(id) ? localSource : gateway;
}

export { isLocalGameId } from './localgame/id';
