import { describe, expect, test } from 'vitest';

import { reorderRows } from './gridDragUtil';

interface TestRow {
  id: number;
  name: string;
}

const makeRows = (...ids: number[]): TestRow[] => ids.map((id) => ({ id, name: `row-${id}` }));
const row = (id: number): TestRow => ({ id, name: `row-${id}` });
const ids = (rows: TestRow[]) => rows.map((r) => r.id);

describe('reorderRows', () => {
  const rows = makeRows(1, 2, 3, 4, 5);

  describe('single row move', () => {
    test('move row down (below target)', () => {
      // Drag row 1 below row 3: [2, 3, 1, 4, 5]
      expect(ids(reorderRows(rows, [row(1)], row(3), 1))).toEqual([2, 3, 1, 4, 5]);
    });

    test('move row up (above target)', () => {
      // Drag row 4 above row 2: [1, 4, 2, 3, 5]
      expect(ids(reorderRows(rows, [row(4)], row(2), -1))).toEqual([1, 4, 2, 3, 5]);
    });

    test('move to first position', () => {
      // Drag row 3 above row 1: [3, 1, 2, 4, 5]
      expect(ids(reorderRows(rows, [row(3)], row(1), -1))).toEqual([3, 1, 2, 4, 5]);
    });

    test('move to last position', () => {
      // Drag row 2 below row 5: [1, 3, 4, 5, 2]
      expect(ids(reorderRows(rows, [row(2)], row(5), 1))).toEqual([1, 3, 4, 5, 2]);
    });

    test('move to adjacent position down', () => {
      // Drag row 2 below row 3: [1, 3, 2, 4, 5]
      expect(ids(reorderRows(rows, [row(2)], row(3), 1))).toEqual([1, 3, 2, 4, 5]);
    });

    test('move to adjacent position up', () => {
      // Drag row 3 above row 2: [1, 3, 2, 4, 5]
      expect(ids(reorderRows(rows, [row(3)], row(2), -1))).toEqual([1, 3, 2, 4, 5]);
    });
  });

  describe('multi-row move', () => {
    test('move multiple contiguous rows down', () => {
      // Drag rows 1,2 below row 4: [3, 4, 1, 2, 5]
      expect(ids(reorderRows(rows, [row(1), row(2)], row(4), 1))).toEqual([3, 4, 1, 2, 5]);
    });

    test('move multiple contiguous rows up', () => {
      // Drag rows 4,5 above row 2: [1, 4, 5, 2, 3]
      expect(ids(reorderRows(rows, [row(4), row(5)], row(2), -1))).toEqual([1, 4, 5, 2, 3]);
    });

    test('move rows with gaps — gaps are collapsed', () => {
      // Drag rows 1,3,5 below row 4: [2, 4, 1, 3, 5]
      expect(ids(reorderRows(rows, [row(1), row(3), row(5)], row(4), 1))).toEqual([2, 4, 1, 3, 5]);
    });

    test('move rows with gaps above target', () => {
      // Drag rows 2,4 above row 5: [1, 3, 2, 4, 5]
      expect(ids(reorderRows(rows, [row(2), row(4)], row(5), -1))).toEqual([1, 3, 2, 4, 5]);
    });

    test('move rows with gaps to first position', () => {
      // Drag rows 3,5 above row 1: [3, 5, 1, 2, 4]
      expect(ids(reorderRows(rows, [row(3), row(5)], row(1), -1))).toEqual([3, 5, 1, 2, 4]);
    });

    test('move rows with gaps to last position', () => {
      // Drag rows 1,3 below row 5: [2, 4, 5, 1, 3]
      expect(ids(reorderRows(rows, [row(1), row(3)], row(5), 1))).toEqual([2, 4, 5, 1, 3]);
    });

    test('preserves insertion order of movedRows', () => {
      // movedRows given in display order [2,4] should insert as [2,4] not [4,2]
      const result = reorderRows(rows, [row(2), row(4)], row(1), -1);
      expect(ids(result)).toEqual([2, 4, 1, 3, 5]);
    });
  });

  describe('edge cases', () => {
    test('target is one of the moved rows — returns original array', () => {
      const result = reorderRows(rows, [row(2), row(3)], row(2), 1);
      expect(result).toBe(rows);
    });

    test('empty movedRows — returns original array', () => {
      const result = reorderRows(rows, [], row(3), 1);
      expect(result).toBe(rows);
    });

    test('target not found in rows — returns original array', () => {
      const result = reorderRows(rows, [row(1)], row(99), 1);
      expect(result).toBe(rows);
    });

    test('single row array — move is no-op (target is the moved row)', () => {
      const single = makeRows(1);
      const result = reorderRows(single, [row(1)], row(1), 1);
      expect(result).toBe(single);
    });

    test('two row array — swap positions', () => {
      const two = makeRows(1, 2);
      expect(ids(reorderRows(two, [row(1)], row(2), 1))).toEqual([2, 1]);
      expect(ids(reorderRows(two, [row(2)], row(1), -1))).toEqual([2, 1]);
    });
  });
});
