import { describe, expect, it } from 'vitest';
import { LOBBY_DUE_GRACE_MS, LOBBY_TITLE_MIN, LOBBY_TITLE_ONE_LINE_MIN, lobbyStart, lobbyTitleFit, textEm, wrappedLines } from './lobby';

const MIN = 60_000;
const now = Date.UTC(2026, 9, 29, 23, 0);

describe('lobbyStart', () => {
  it('counts down only within the hour', () => {
    expect(lobbyStart(now + 12 * MIN, now)).toEqual({ kind: 'countdown', startsAt: now + 12 * MIN });
    expect(lobbyStart(now + 60 * MIN, now)).toEqual({ kind: 'countdown', startsAt: now + 60 * MIN });
    expect(lobbyStart(now + 61 * MIN, now)).toEqual({ kind: 'host' });
  });

  it('never announces a show scheduled for tomorrow as starting soon', () => {
    // Lobby opened on Thursday for Friday 9:00 PM.
    expect(lobbyStart(now + 22 * 60 * MIN, now)).toEqual({ kind: 'host' });
  });

  it('says "any moment" once the time has come, until the host is clearly late', () => {
    expect(lobbyStart(now, now)).toEqual({ kind: 'due' });
    expect(lobbyStart(now - LOBBY_DUE_GRACE_MS, now)).toEqual({ kind: 'due' });
    expect(lobbyStart(now - LOBBY_DUE_GRACE_MS - 1, now)).toEqual({ kind: 'host' });
  });

  it('falls back to the host when the event has no start time', () => {
    expect(lobbyStart(null, now)).toEqual({ kind: 'host' });
    expect(lobbyStart(undefined, now)).toEqual({ kind: 'host' });
    expect(lobbyStart(Number.NaN, now)).toEqual({ kind: 'host' });
  });
});

describe('title width estimate', () => {
  it('never comes out narrower than the face really renders', () => {
    // Widths in em measured in the browser (Unbounded 800, -0.03em tracking) on the TV page.
    const measured: [string, number][] = [
      ['Friday Night Championship', 16.68],
      ['The Harbor Tap Thursday Night Championship', 28.18],
      ['Halloween Spooktacular Trivia Championship', 27.76],
      ['Big Monday Music Bingo Showdown', 22.07],
      ['ABCDEFGHIJKLMNOPQRSTUVWXYZ', 22.65],
      ['abcdefghijklmnopqrstuvwxyz', 18.59],
    ];
    for (const [text, em] of measured) {
      expect(textEm(text)).toBeGreaterThanOrEqual(em);
      // ...but stays close, so names aren't shrunk for nothing.
      expect(textEm(text)).toBeLessThanOrEqual(em * 1.1);
    }
  });
});

describe('wrappedLines', () => {
  it('wraps greedily on spaces', () => {
    expect(wrappedLines('Friday Night Championship', 17.2)).toBe(1);
    expect(wrappedLines('Friday Night Championship', 16)).toBe(2);
    expect(wrappedLines('Pub Quiz Night Live', 6)).toBe(3);
    expect(wrappedLines('  ', 10)).toBe(1);
  });

  it('reports a word wider than a line as not fitting', () => {
    expect(wrappedLines('Supercalifragilistic', 10)).toBe(Number.POSITIVE_INFINITY);
  });
});

describe('lobbyTitleFit', () => {
  const width = 1160;

  it('keeps short names on one line at full size', () => {
    expect(lobbyTitleFit('Quiz Night', width)).toEqual({ size: 76, lines: 1 });
  });

  it('fits the seeded "Friday Night Championship" on one line instead of truncating it', () => {
    const fit = lobbyTitleFit('Friday Night Championship', width);
    expect(fit.lines).toBe(1);
    expect(fit.size).toBeGreaterThanOrEqual(LOBBY_TITLE_ONE_LINE_MIN);
    expect(fit.size).toBeLessThan(76);
    expect(fit.size * 16.68).toBeLessThanOrEqual(width);
  });

  it('wraps names of 40+ characters onto two lines at a readable size, ALL CAPS included', () => {
    for (const name of ['The Harbor Tap Thursday Night Championship', 'Halloween Spooktacular Trivia Championship', 'HALLOWEEN SPOOKTACULAR TRIVIA CHAMPIONSHIP']) {
      const fit = lobbyTitleFit(name, width);
      expect(fit.lines).toBe(2);
      expect(fit.size).toBeGreaterThanOrEqual(56);
      expect(wrappedLines(name, width / fit.size)).toBeLessThanOrEqual(2);
    }
  });

  it('bottoms out at the floor for 80-character names', () => {
    expect(lobbyTitleFit('W'.repeat(39) + ' ' + 'M'.repeat(40), width).size).toBe(LOBBY_TITLE_MIN);
  });
});
