import { SortingService } from './sorting.service';
import { SortData, SortSnapshot } from '../sorting.models';

function toSortData(values: number[]): SortData[] {
  return values.map((value) => ({ value, state: 'unsorted' }));
}

function finalValues(snapshots: SortSnapshot[]): number[] {
  const lastSnapshot = snapshots[snapshots.length - 1];
  return lastSnapshot.array.map((item) => item.value);
}

function isAscending(values: number[]): boolean {
  for (let i = 1; i < values.length; i++) {
    if (values[i - 1] > values[i]) {
      return false;
    }
  }
  return true;
}

describe('SortingService', () => {
  let service: SortingService;
  const unsorted = [5, 2, 9, 1, 5, 6, 3, 8, 7, 4];
  const expectedSorted = [...unsorted].sort((a, b) => a - b);

  beforeEach(() => {
    service = new SortingService();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  for (const algorithm of [
    'bubbleSort',
    'cocktailSort',
    'quickSort',
    'heapSort',
    'timSort',
  ] as const) {
    it(`${algorithm} should produce a fully sorted final snapshot`, () => {
      const snapshots = service[algorithm](toSortData(unsorted));
      const result = finalValues(snapshots);

      expect(result).toEqual(expectedSorted);
      expect(isAscending(result)).toBeTrue();
    });

    it(`${algorithm} should not mutate the input array`, () => {
      const input = toSortData(unsorted);
      service[algorithm](input);

      expect(input.map((item) => item.value)).toEqual(unsorted);
    });
  }
});
