import { describe, it, expect } from 'vitest';
import { mapFilesToTasks } from './fileToTask';
import type { StagedFile } from '../types';

/** Build a StagedFile from a relative path (name = last segment). */
function sf(relativePath: string, size = 1): StagedFile {
  return { name: relativePath.slice(relativePath.lastIndexOf('/') + 1), relativePath, size };
}

describe('mapFilesToTasks §5', () => {
  it('returns no tasks for no files', () => {
    expect(mapFilesToTasks('dicom', false, [])).toEqual([]);
  });

  it('2D image: each image -> one task', () => {
    const tasks = mapFilesToTasks('image2d', false, [sf('a.png'), sf('b.png')]);
    expect(tasks).toHaveLength(2);
    expect(tasks.map((t) => t.name)).toEqual(['a', 'b']);
    expect(tasks.every((t) => t.grouping === 'image')).toBe(true);
  });

  it('video: each video -> one task', () => {
    const tasks = mapFilesToTasks('video', false, [sf('clip1.mp4'), sf('clip2.mov')]);
    expect(tasks).toHaveLength(2);
    expect(tasks.every((t) => t.grouping === 'video')).toBe(true);
  });

  it('video frames: a folder -> one task, frames ordered by filename (MAP2)', () => {
    const tasks = mapFilesToTasks('videoFrames', false, [
      sf('clipA/frame10.png'),
      sf('clipA/frame2.png'),
      sf('clipA/frame1.png'),
    ]);
    expect(tasks).toHaveLength(1);
    expect(tasks[0].name).toBe('clipA');
    expect(tasks[0].grouping).toBe('frames');
    // natural sort: frame1, frame2, frame10 (not lexicographic frame1, frame10, frame2)
    expect(tasks[0].files.map((f) => f.name)).toEqual(['frame1.png', 'frame2.png', 'frame10.png']);
  });

  it('DICOM (no group-by-study): a series in a folder -> one task (MAP1)', () => {
    const tasks = mapFilesToTasks('dicom', false, [
      sf('seriesA/i1.dcm'),
      sf('seriesA/i2.dcm'),
      sf('seriesB/i1.dcm'),
    ]);
    expect(tasks).toHaveLength(2);
    expect(tasks.map((t) => t.name).sort()).toEqual(['seriesA', 'seriesB']);
    expect(tasks.every((t) => t.grouping === 'series')).toBe(true);
  });

  it('DICOM (no group-by-study): a single series without a folder -> one task', () => {
    const tasks = mapFilesToTasks('dicom', false, [sf('i1.dcm'), sf('i2.dcm')]);
    expect(tasks).toHaveLength(1);
    expect(tasks[0].files).toHaveLength(2);
  });

  it('DICOM (group-by-study): top-level study folder merges its series -> one task', () => {
    const tasks = mapFilesToTasks('dicom', true, [
      sf('study1/seriesA/i1.dcm'),
      sf('study1/seriesB/i1.dcm'),
      sf('study2/seriesA/i1.dcm'),
    ]);
    expect(tasks).toHaveLength(2);
    expect(tasks.map((t) => t.name).sort()).toEqual(['study1', 'study2']);
    expect(tasks.find((t) => t.name === 'study1')!.files).toHaveLength(2);
    expect(tasks.every((t) => t.grouping === 'study')).toBe(true);
  });

  it('NIfTI (no group-by-study): each file -> a separate task', () => {
    const tasks = mapFilesToTasks('nifti', false, [sf('a.nii.gz'), sf('b.nii.gz')]);
    expect(tasks).toHaveLength(2);
    expect(tasks.map((t) => t.name)).toEqual(['a', 'b']);
    expect(tasks.every((t) => t.grouping === 'volume')).toBe(true);
  });

  it('NIfTI (group-by-study): files in the same folder -> one task', () => {
    const tasks = mapFilesToTasks('nifti', true, [
      sf('studyX/scan1.nii.gz'),
      sf('studyX/scan2.nii.gz'),
      sf('studyY/scan1.nii.gz'),
    ]);
    expect(tasks).toHaveLength(2);
    expect(tasks.find((t) => t.name === 'studyX')!.files).toHaveLength(2);
    expect(tasks.every((t) => t.grouping === 'study')).toBe(true);
  });

  it('is deterministic: group order is stable regardless of input order', () => {
    const a = mapFilesToTasks('dicom', false, [sf('z/i.dcm'), sf('a/i.dcm')]);
    const b = mapFilesToTasks('dicom', false, [sf('a/i.dcm'), sf('z/i.dcm')]);
    expect(a.map((t) => t.name)).toEqual(b.map((t) => t.name));
    expect(a.map((t) => t.name)).toEqual(['a', 'z']);
  });
});
