import { describe, it, expect } from 'vitest';
import { preloadDicts } from './preload';
import type { Variant } from '../model';
import type { Dawg } from './dawg';

// A non-null stand-in for a loaded reader — preloadDicts only checks getDawg's result for null.
const DAWG = {} as Dawg;
const noSleep = (): Promise<void> => Promise.resolve();

describe('preloadDicts', () => {
  it('fetches every enabled variant that has a known version', async () => {
    const calls: string[] = [];
    const res = await preloadDicts({ scrabble_en: 'v1', scrabble_ru: 'v2', erudit_ru: 'v3' }, ['scrabble_en', 'erudit_ru'], {
      getDawg: async (v: Variant, ver: string) => {
        calls.push(`${v}@${ver}`);
        return DAWG;
      },
      disabled: () => false,
      sleep: noSleep,
    });
    expect(res.ok).toEqual(['scrabble_en', 'erudit_ru']);
    expect(res.failed).toEqual([]);
    expect(calls).toEqual(['scrabble_en@v1', 'erudit_ru@v3']);
  });

  it('marks a variant with no known version as failed without fetching it', async () => {
    const calls: string[] = [];
    const res = await preloadDicts({ scrabble_en: 'v1' }, ['scrabble_en', 'scrabble_ru'], {
      getDawg: async (v: Variant) => {
        calls.push(v);
        return DAWG;
      },
      disabled: () => false,
      sleep: noSleep,
    });
    expect(res.ok).toEqual(['scrabble_en']);
    expect(res.failed).toEqual(['scrabble_ru']);
    expect(calls).toEqual(['scrabble_en']);
  });

  it('retries a transient failure with linear backoff, then succeeds', async () => {
    let attempts = 0;
    const waits: number[] = [];
    const res = await preloadDicts({ scrabble_en: 'v1' }, ['scrabble_en'], {
      getDawg: async () => (++attempts >= 3 ? DAWG : null),
      disabled: () => false,
      sleep: async (ms: number) => void waits.push(ms),
      retries: 3,
      backoffMs: 100,
    });
    expect(res.ok).toEqual(['scrabble_en']);
    expect(attempts).toBe(3);
    expect(waits).toEqual([100, 200]);
  });

  it('gives up a persistent failure after the retry budget', async () => {
    let attempts = 0;
    const res = await preloadDicts({ scrabble_en: 'v1' }, ['scrabble_en'], {
      getDawg: async () => {
        attempts++;
        return null;
      },
      disabled: () => false,
      sleep: noSleep,
      retries: 2,
    });
    expect(res.failed).toEqual(['scrabble_en']);
    expect(res.ok).toEqual([]);
    expect(attempts).toBe(3);
  });

  it('stops retrying once the session miss-breaker trips', async () => {
    let attempts = 0;
    const res = await preloadDicts({ scrabble_en: 'v1' }, ['scrabble_en'], {
      getDawg: async () => {
        attempts++;
        return null;
      },
      disabled: () => true,
      sleep: noSleep,
      retries: 5,
    });
    expect(res.failed).toEqual(['scrabble_en']);
    expect(attempts).toBe(1);
  });
});
