import { describe, it, expect } from 'vitest';
import { initialSrs, review } from './srs';

const MS_PER_DAY = 86_400_000;
const NOW = 1_700_000_000_000;

describe('initialSrs', () => {
  it('creates a neutral, immediately-due card', () => {
    const s = initialSrs(NOW);
    expect(s).toEqual({
      ease: 2.5,
      intervalDays: 0,
      reps: 0,
      lapses: 0,
      due: NOW,
    });
  });
});

describe('review', () => {
  it('does not mutate the input state', () => {
    const s = initialSrs(NOW);
    const frozen = { ...s };
    review(s, 2, NOW);
    expect(s).toEqual(frozen);
  });

  it('produces monotonically increasing intervals on repeated "good"', () => {
    let s = initialSrs(NOW);
    const intervals: number[] = [];
    let t = NOW;
    for (let i = 0; i < 5; i++) {
      s = review(s, 2, t);
      intervals.push(s.intervalDays);
      t += s.intervalDays * MS_PER_DAY;
    }
    // 1 day, 6 days, then *ease each time -> strictly increasing.
    for (let i = 1; i < intervals.length; i++) {
      expect(intervals[i]!).toBeGreaterThan(intervals[i - 1]!);
    }
    expect(intervals[0]).toBe(1);
    expect(intervals[1]).toBe(6);
  });

  it('increments reps on passing grades and resets on "again"', () => {
    let s = initialSrs(NOW);
    s = review(s, 2, NOW); // good
    s = review(s, 2, NOW); // good
    expect(s.reps).toBe(2);
    s = review(s, 0, NOW); // again
    expect(s.reps).toBe(0);
    expect(s.lapses).toBe(1);
  });

  it('floors ease at 1.3 even after many "again" reviews', () => {
    let s = initialSrs(NOW);
    for (let i = 0; i < 20; i++) s = review(s, 0, NOW);
    expect(s.ease).toBe(1.3);
  });

  it('lowers ease on hard, raises it on easy', () => {
    const good = review(initialSrs(NOW), 2, NOW);
    expect(good.ease).toBe(2.5);
    const hard = review(initialSrs(NOW), 1, NOW);
    expect(hard.ease).toBeCloseTo(2.35, 5);
    const easy = review(initialSrs(NOW), 3, NOW);
    expect(easy.ease).toBeCloseTo(2.65, 5);
  });

  it('computes due = now + intervalDays * 86400000 and sets lastReviewed', () => {
    const s = review(initialSrs(NOW), 2, NOW);
    expect(s.intervalDays).toBe(1);
    expect(s.due).toBe(NOW + 1 * MS_PER_DAY);
    expect(s.lastReviewed).toBe(NOW);
  });

  it('gives "easy" a longer interval than "good" at the same step', () => {
    const good = review(initialSrs(NOW), 2, NOW);
    const easy = review(initialSrs(NOW), 3, NOW);
    expect(easy.intervalDays).toBeGreaterThan(good.intervalDays);
  });
});
