import { describe, expect, it } from 'vitest';
import { SeededRNG } from '../src/core/SeededRNG';

describe('SeededRNG', () => {
  it('same seed + stream name → identical sequence', () => {
    const a = new SeededRNG(42).stream('patrons');
    const b = new SeededRNG(42).stream('patrons');
    for (let i = 0; i < 100; i++) expect(a.next()).toBe(b.next());
  });

  it('different seeds diverge', () => {
    const a = new SeededRNG(1).stream('x');
    const b = new SeededRNG(2).stream('x');
    const seqA = Array.from({ length: 10 }, () => a.next());
    const seqB = Array.from({ length: 10 }, () => b.next());
    expect(seqA).not.toEqual(seqB);
  });

  it('streams are independent — draining one does not shift another', () => {
    const rng1 = new SeededRNG(7);
    const rng2 = new SeededRNG(7);
    rng1.stream('a');
    for (let i = 0; i < 500; i++) rng1.stream('a').next(); // drain a
    const fromDrained = rng1.stream('b').next();
    const fromFresh = rng2.stream('b').next();
    expect(fromDrained).toBe(fromFresh);
  });

  it('stream() returns the same stream instance per name (stateful)', () => {
    const rng = new SeededRNG(9);
    const first = rng.stream('q').next();
    const second = rng.stream('q').next();
    expect(first).not.toBe(second); // continued, not restarted
  });

  it('int() covers bounds inclusively and stays in range', () => {
    const s = new SeededRNG(3).stream('ints');
    const seen = new Set<number>();
    for (let i = 0; i < 2000; i++) {
      const v = s.int(1, 6);
      expect(v).toBeGreaterThanOrEqual(1);
      expect(v).toBeLessThanOrEqual(6);
      seen.add(v);
    }
    expect(seen.size).toBe(6);
  });

  it('chance() distribution sanity', () => {
    const s = new SeededRNG(11).stream('coin');
    let hits = 0;
    for (let i = 0; i < 10_000; i++) if (s.chance(0.3)) hits++;
    expect(hits / 10_000).toBeGreaterThan(0.27);
    expect(hits / 10_000).toBeLessThan(0.33);
  });

  it('weighted() respects weights and never returns zero-weight entries', () => {
    const s = new SeededRNG(13).stream('w');
    const counts = { a: 0, b: 0, c: 0 };
    for (let i = 0; i < 10_000; i++) {
      counts[s.weighted([['a', 9], ['b', 1], ['c', 0]] as const)]++;
    }
    expect(counts.c).toBe(0);
    expect(counts.a).toBeGreaterThan(counts.b * 5);
  });

  it('pick/weighted throw on empty/invalid input', () => {
    const s = new SeededRNG(1).stream('e');
    expect(() => s.pick([])).toThrow();
    expect(() => s.weighted([])).toThrow();
  });
});
