import { describe, expect, it } from 'vitest';
import type { LocalFileEntry } from '../api';
import { DND_FILES_MIME, dragSources, readDragSources } from './fileDnd';

function entry(p: Partial<LocalFileEntry> & { path: string }): LocalFileEntry {
  return {
    name: p.path.split('/').pop() ?? p.path,
    kind: 'file',
    size: 0,
    modifiedAt: '2026-01-01T00:00:00Z',
    ...p,
  };
}

describe('fileDnd', () => {
  describe('DND_FILES_MIME', () => {
    it('is the maestro-specific custom MIME type', () => {
      expect(DND_FILES_MIME).toBe('application/x-maestro-files');
    });
  });

  describe('dragSources', () => {
    it('returns just the entry when nothing is selected', () => {
      expect(dragSources(entry({ path: 'output/a.txt' }), new Set())).toEqual(['output/a.txt']);
    });

    it('returns the whole selection when dragging a selected file', () => {
      const sel = new Set(['output/a.txt', 'output/b.txt']);
      const result = dragSources(entry({ path: 'output/a.txt' }), sel);
      expect(new Set(result)).toEqual(sel);
      expect(result).toHaveLength(2);
    });

    it('returns only the dragged file when it is NOT part of the selection', () => {
      const sel = new Set(['output/a.txt', 'output/b.txt']);
      expect(dragSources(entry({ path: 'output/c.txt' }), sel)).toEqual(['output/c.txt']);
    });

    it('returns only the directory itself even if it is "selected" (folders never carry the selection)', () => {
      const sel = new Set(['output/dir', 'output/a.txt']);
      const result = dragSources(entry({ path: 'output/dir', kind: 'directory' }), sel);
      expect(result).toEqual(['output/dir']);
    });

    it('returns the directory alone when nothing is selected', () => {
      expect(dragSources(entry({ path: 'output/dir', kind: 'directory' }), new Set())).toEqual([
        'output/dir',
      ]);
    });
  });

  describe('readDragSources', () => {
    function fakeDt(data: Record<string, string>): DataTransfer {
      return {
        getData: (type: string) => data[type] ?? '',
      } as unknown as DataTransfer;
    }

    it('returns [] for a null dataTransfer', () => {
      expect(readDragSources(null)).toEqual([]);
    });

    it('returns [] when the MIME slot is empty', () => {
      expect(readDragSources(fakeDt({}))).toEqual([]);
    });

    it('parses a JSON array of paths', () => {
      const dt = fakeDt({ [DND_FILES_MIME]: JSON.stringify(['output/a.txt', 'output/b.txt']) });
      expect(readDragSources(dt)).toEqual(['output/a.txt', 'output/b.txt']);
    });

    it('round-trips with dragSources output', () => {
      const sources = dragSources(entry({ path: 'output/a.txt' }), new Set(['output/a.txt', 'output/b.txt']));
      const dt = fakeDt({ [DND_FILES_MIME]: JSON.stringify(sources) });
      expect(new Set(readDragSources(dt))).toEqual(new Set(sources));
    });

    it('filters out non-string array entries', () => {
      const dt = fakeDt({ [DND_FILES_MIME]: JSON.stringify(['ok', 42, null, { x: 1 }, 'fine']) });
      expect(readDragSources(dt)).toEqual(['ok', 'fine']);
    });

    it('returns [] when the payload is not a JSON array (e.g. an object)', () => {
      const dt = fakeDt({ [DND_FILES_MIME]: JSON.stringify({ path: 'x' }) });
      expect(readDragSources(dt)).toEqual([]);
    });

    it('returns [] for malformed JSON instead of throwing', () => {
      const dt = fakeDt({ [DND_FILES_MIME]: '{not valid json' });
      expect(() => readDragSources(dt)).not.toThrow();
      expect(readDragSources(dt)).toEqual([]);
    });

    it('ignores data stored under a different MIME type', () => {
      const dt = fakeDt({ 'text/plain': JSON.stringify(['output/a.txt']) });
      expect(readDragSources(dt)).toEqual([]);
    });
  });
});
