// Lobby preload: warm the per-game cache for the player's ongoing games ahead of time, so
// opening one from the lobby renders instantly — no "loading" flash and no rack→board jump
// (the saved draft is fetched and cached alongside state and history). It is best-effort and
// bounded; finished games are skipped (rarely reopened, no draft), as are games already cached
// (the live stream keeps those fresh) or currently being warmed.

import { gateway } from './gateway';
import { getCachedGame, setCachedGame } from './gamecache';
import { hasAlphabet } from './alphabet';
import type { GameView } from './model';

// Cap on games warmed at once: enough to cover a typical lobby quickly without a request burst.
const MAX_CONCURRENT = 4;
// Games being warmed right now, so overlapping calls (the lobby refreshes on every live event)
// never fetch the same game twice.
const inflight = new Set<string>();

/**
 * preloadGames warms the cache for the given ongoing games: it fetches each game's state,
 * history and saved draft in parallel and stores them, skipping finished games and any already
 * cached or in flight. Concurrency is bounded and failures are swallowed (the game just
 * cold-loads on open). It resolves once every queued game has settled.
 */
export async function preloadGames(games: GameView[]): Promise<void> {
  const queue = games.filter(
    (g) => g.status !== 'finished' && !getCachedGame(g.id) && !inflight.has(g.id),
  );
  if (queue.length === 0) return;
  for (const g of queue) inflight.add(g.id);
  let next = 0;
  const worker = async (): Promise<void> => {
    while (next < queue.length) {
      const g = queue[next++];
      try {
        const [st, hist, draft] = await Promise.all([
          gateway.gameState(g.id, !hasAlphabet(g.variant)),
          gateway.gameHistory(g.id),
          gateway.draftGet(g.id).catch(() => ''),
        ]);
        // A live event may have seeded or advanced this entry while we fetched; don't clobber it.
        if (!getCachedGame(g.id)) setCachedGame(g.id, st, hist.moves, draft);
      } catch {
        /* best-effort: leave the game to cold-load on open */
      } finally {
        inflight.delete(g.id);
      }
    }
  };
  await Promise.all(Array.from({ length: Math.min(MAX_CONCURRENT, queue.length) }, worker));
}
