import { commit, emptyHistory, redo, undo } from './history.util';

describe('history reducer', () => {
  it('starts empty', () => {
    const history = emptyHistory<number>();
    expect(history.past).toEqual([]);
    expect(history.future).toEqual([]);
  });

  it('commit records an undo point and clears the redo stack', () => {
    let history = emptyHistory<number>();
    history = commit(history, 1);
    history = commit({ ...history, future: [9] }, 2);
    expect(history.past).toEqual([1, 2]);
    expect(history.future).toEqual([]);
  });

  it('caps history depth at the limit', () => {
    let history = emptyHistory<number>();
    for (let i = 1; i <= 5; i++) {
      history = commit(history, i, 3);
    }
    expect(history.past).toEqual([3, 4, 5]);
  });

  it('undo steps back and moves current onto the redo stack', () => {
    const history = { past: [1, 2], future: [] as number[] };
    const step = undo(history, 3);
    expect(step).not.toBeNull();
    expect(step!.value).toBe(2);
    expect(step!.history.past).toEqual([1]);
    expect(step!.history.future).toEqual([3]);
  });

  it('undo returns null when there is nothing to undo', () => {
    expect(undo(emptyHistory<number>(), 1)).toBeNull();
  });

  it('redo steps forward and moves current back onto the undo stack', () => {
    const history = { past: [1], future: [3] };
    const step = redo(history, 2);
    expect(step).not.toBeNull();
    expect(step!.value).toBe(3);
    expect(step!.history.past).toEqual([1, 2]);
    expect(step!.history.future).toEqual([]);
  });

  it('redo returns null when there is nothing to redo', () => {
    expect(redo(emptyHistory<number>(), 1)).toBeNull();
  });

  it('undo then redo round-trips to the same value', () => {
    const start = { past: [10, 20], future: [] as number[] };
    const undone = undo(start, 30)!;
    const redone = redo(undone.history, undone.value)!;
    expect(redone.value).toBe(30);
    expect(redone.history.past).toEqual([10, 20]);
    expect(redone.history.future).toEqual([]);
  });
});
