import { describe, expect, it } from 'vitest';
import {
  ERUDIT_KEYS,
  ERUDIT_VALUES,
  PAUSE_MS,
  SPLASH_TILES,
  WORD_MS,
  splashSchedule,
  type SplashStep,
} from './splash';

const at = (word: 'erudit' | 'zagruzka' | 'ozhidanie') =>
  SPLASH_TILES.filter((t) => t.word === word);
const text = (word: 'erudit' | 'zagruzka' | 'ozhidanie') => at(word).map((t) => t.letter).join('');
const reveals = (steps: SplashStep[]) => steps.filter((s) => s.kind === 'reveal');

describe('splash layout', () => {
  it('uses the Эрудит point values for every splash letter', () => {
    // Spot-check the distinctive weights against scrabble-solver rules.Erudit.
    expect(ERUDIT_VALUES['Э']).toBe(10);
    expect(ERUDIT_VALUES['Ж']).toBe(5);
    expect(ERUDIT_VALUES['З']).toBe(5);
    expect(ERUDIT_VALUES['У']).toBe(3);
    expect(ERUDIT_VALUES['Г']).toBe(3);
    expect(ERUDIT_VALUES['Р']).toBe(2);
    expect(ERUDIT_VALUES['И']).toBe(1);
  });

  it('places ЭРУДИТ horizontally on row 3, left to right, with its values', () => {
    const e = at('erudit');
    expect(text('erudit')).toBe('ЭРУДИТ');
    expect(e.map((t) => t.col)).toEqual([0, 1, 2, 3, 4, 5]);
    expect(e.every((t) => t.row === 3)).toBe(true);
    expect(e.map((t) => t.value)).toEqual([10, 2, 3, 2, 1, 2]);
  });

  it('drops the shared crossing tiles (Р, Д) from the vertical words', () => {
    // ЗАГРУЗКА in column 1 minus the shared Р at row 3; ОЖИДАНИЕ in column 3 minus Д.
    expect(at('zagruzka').map((t) => t.row)).toEqual([0, 1, 2, 4, 5, 6, 7]);
    expect(text('zagruzka')).toBe('ЗАГУЗКА'); // ЗАГ(Р)УЗКА without the shared Р
    expect(at('zagruzka').every((t) => t.col === 1)).toBe(true);
    expect(at('ozhidanie').map((t) => t.row)).toEqual([0, 1, 2, 4, 5, 6, 7]);
    expect(text('ozhidanie')).toBe('ОЖИАНИЕ'); // ОЖИ(_)АНИЕ without Д
    expect(at('ozhidanie').every((t) => t.col === 3)).toBe(true);
  });

  it('keeps only the ЭРУДИТ tiles as the persistent set', () => {
    expect(ERUDIT_KEYS.size).toBe(6);
    expect(ERUDIT_KEYS.has('3-1')).toBe(true); // Р crossing
    expect(ERUDIT_KEYS.has('3-3')).toBe(true); // Д crossing
    expect(SPLASH_TILES).toHaveLength(20); // 6 + 7 + 7 unique cells
  });
});

describe('splash schedule', () => {
  const s = splashSchedule();
  const HOLD = WORD_MS + PAUSE_MS;

  it('lays ЭРУДИТ first, then holds it for PAUSE_MS before the check', () => {
    const r = reveals(s.prefix);
    expect(r).toHaveLength(6);
    expect(r.map((x) => x.atMs)).toEqual([...r.map((x) => x.atMs)].sort((a, b) => a - b));
    expect(r[0].atMs).toBe(0);
    expect(r[r.length - 1].atMs).toBeLessThan(WORD_MS);
    // The check waits out the whole word plus the readability hold — it never fires the instant
    // the last tile lands.
    expect(s.prefix.at(-1)).toEqual({ atMs: HOLD, kind: 'check' });
    expect(HOLD - WORD_MS).toBe(PAUSE_MS);
    expect(s.prefixMs).toBe(HOLD);
  });

  it('reveals both vertical words per cycle and never re-lays a shared tile', () => {
    const r = reveals(s.cycle);
    expect(r).toHaveLength(14); // 7 + 7
    expect(r.some((x) => x.key === '3-1' || x.key === '3-3')).toBe(false);
    // ЗАГРУЗКА starts at the cycle start (no leading gap); the hold lives after the word.
    expect(r[0].atMs).toBe(0);
  });

  it('checks one hold after each word and clears at the end of the cycle', () => {
    const checks = s.cycle.filter((x) => x.kind === 'check').map((x) => x.atMs);
    expect(checks).toEqual([HOLD, 2 * HOLD]);
    const last = s.cycle.at(-1)!;
    expect(last.kind).toBe('clear');
    expect(last.atMs).toBe(s.cycleMs);
    expect(s.cycleMs).toBe(2 * HOLD);
  });

  it('honours overridden timings', () => {
    const fast = splashSchedule({ wordMs: 700, pauseMs: 100 });
    expect(fast.prefixMs).toBe(700 + 100);
    expect(fast.cycleMs).toBe(2 * (700 + 100));
  });
});
