import { describe, it, expect } from 'vitest';
import { createRng } from '@/lib/mock-data/seed';

describe('createRng (mulberry32 PRNG)', () => {
  it('produces deterministic output for the same seed', () => {
    const rng1 = createRng(42);
    const rng2 = createRng(42);

    const seq1 = Array.from({ length: 10 }, () => rng1.next());
    const seq2 = Array.from({ length: 10 }, () => rng2.next());

    expect(seq1).toEqual(seq2);
  });

  it('produces different output for different seeds', () => {
    const rng1 = createRng(42);
    const rng2 = createRng(99);

    const val1 = rng1.next();
    const val2 = rng2.next();

    expect(val1).not.toEqual(val2);
  });

  it('next() returns values in [0, 1)', () => {
    const rng = createRng(42);
    for (let i = 0; i < 100; i++) {
      const val = rng.next();
      expect(val).toBeGreaterThanOrEqual(0);
      expect(val).toBeLessThan(1);
    }
  });

  describe('randomInt', () => {
    it('returns integers within [min, max]', () => {
      const rng = createRng(42);
      for (let i = 0; i < 100; i++) {
        const val = rng.randomInt(5, 15);
        expect(val).toBeGreaterThanOrEqual(5);
        expect(val).toBeLessThanOrEqual(15);
        expect(Number.isInteger(val)).toBe(true);
      }
    });

    it('returns min when min === max', () => {
      const rng = createRng(42);
      expect(rng.randomInt(7, 7)).toBe(7);
    });
  });

  describe('randomFloat', () => {
    it('returns floats within [min, max)', () => {
      const rng = createRng(42);
      for (let i = 0; i < 100; i++) {
        const val = rng.randomFloat(1.5, 9.5);
        expect(val).toBeGreaterThanOrEqual(1.5);
        expect(val).toBeLessThan(9.5);
      }
    });
  });

  describe('pick', () => {
    it('returns an element from the array', () => {
      const rng = createRng(42);
      const items = ['a', 'b', 'c', 'd'] as const;
      for (let i = 0; i < 50; i++) {
        const val = rng.pick(items);
        expect(items).toContain(val);
      }
    });
  });

  describe('shuffle', () => {
    it('returns a new array with the same elements', () => {
      const rng = createRng(42);
      const original = [1, 2, 3, 4, 5];
      const shuffled = rng.shuffle(original);

      expect(shuffled).toHaveLength(original.length);
      expect(shuffled.sort()).toEqual([...original].sort());
    });

    it('does not mutate the original array', () => {
      const rng = createRng(42);
      const original = [1, 2, 3, 4, 5];
      const copy = [...original];
      rng.shuffle(original);

      expect(original).toEqual(copy);
    });

    it('produces deterministic results for the same seed', () => {
      const rng1 = createRng(42);
      const rng2 = createRng(42);
      const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

      expect(rng1.shuffle(arr)).toEqual(rng2.shuffle(arr));
    });
  });

  describe('randomDate', () => {
    it('returns a date between start and end', () => {
      const rng = createRng(42);
      const start = new Date('2023-01-01');
      const end = new Date('2024-12-31');

      for (let i = 0; i < 50; i++) {
        const date = rng.randomDate(start, end);
        expect(date.getTime()).toBeGreaterThanOrEqual(start.getTime());
        expect(date.getTime()).toBeLessThanOrEqual(end.getTime());
      }
    });

    it('returns the same date when start === end', () => {
      const rng = createRng(42);
      const date = new Date('2024-06-15');
      const result = rng.randomDate(date, date);
      expect(result.getTime()).toBe(date.getTime());
    });
  });
});
