// src/hooks/useDragAndDrop.test.ts
import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { useDragAndDrop } from './useDragAndDrop';

// Helper to create a mock DragEvent with mock functions
const createMockDragEvent = (files: File[] = []): React.DragEvent<HTMLElement> => {
  return {
    preventDefault: vi.fn(),
    stopPropagation: vi.fn(),
    dataTransfer: {
      files: files as unknown as FileList,
    },
  } as unknown as React.DragEvent<HTMLElement>;
};

describe('useDragAndDrop Hook', () => {
  it('should initialize with isDragging as false', () => {
    const onFilesDropped = vi.fn();
    const { result } = renderHook(() => useDragAndDrop({ onFilesDropped }));

    expect(result.current.isDragging).toBe(false);
    expect(result.current.dropzoneProps).toBeDefined();
  });

  describe('Event Handlers', () => {
    it('should set isDragging to true on onDragEnter', () => {
      const onFilesDropped = vi.fn();
      const { result } = renderHook(() => useDragAndDrop({ onFilesDropped }));
      const mockEvent = createMockDragEvent();

      act(() => {
        result.current.dropzoneProps.onDragEnter(mockEvent);
      });

      expect(result.current.isDragging).toBe(true);
      expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1);
      expect(mockEvent.stopPropagation).toHaveBeenCalledTimes(1);
    });

    it('should set isDragging to true on onDragOver', () => {
      const onFilesDropped = vi.fn();
      const { result } = renderHook(() => useDragAndDrop({ onFilesDropped }));
      const mockEvent = createMockDragEvent();

      act(() => {
        result.current.dropzoneProps.onDragOver(mockEvent);
      });

      expect(result.current.isDragging).toBe(true);
      expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1);
      expect(mockEvent.stopPropagation).toHaveBeenCalledTimes(1);
    });

    it('should set isDragging to false on onDragLeave', () => {
      const onFilesDropped = vi.fn();
      const { result } = renderHook(() => useDragAndDrop({ onFilesDropped }));

      // First, enter the drop zone
      act(() => {
        result.current.dropzoneProps.onDragEnter(createMockDragEvent());
      });
      expect(result.current.isDragging).toBe(true);

      // Then, leave it
      const mockLeaveEvent = createMockDragEvent();
      act(() => {
        result.current.dropzoneProps.onDragLeave(mockLeaveEvent);
      });

      expect(result.current.isDragging).toBe(false);
      expect(mockLeaveEvent.preventDefault).toHaveBeenCalledTimes(1);
      expect(mockLeaveEvent.stopPropagation).toHaveBeenCalledTimes(1);
    });

    it('should set isDragging to false and call onFilesDropped on onDrop', () => {
      const onFilesDropped = vi.fn();
      const mockFiles = [new File(['content'], 'file.txt')];
      const { result } = renderHook(() => useDragAndDrop({ onFilesDropped }));

      // Enter the drop zone first
      act(() => {
        result.current.dropzoneProps.onDragEnter(createMockDragEvent());
      });
      expect(result.current.isDragging).toBe(true);

      // Then, drop the file
      const mockDropEvent = createMockDragEvent(mockFiles);
      act(() => {
        result.current.dropzoneProps.onDrop(mockDropEvent);
      });

      expect(result.current.isDragging).toBe(false);
      expect(mockDropEvent.preventDefault).toHaveBeenCalledTimes(1);
      expect(mockDropEvent.stopPropagation).toHaveBeenCalledTimes(1);
      expect(onFilesDropped).toHaveBeenCalledWith(mockDropEvent.dataTransfer.files);
    });

    it('should not call onFilesDropped if no files are present on drop', () => {
      const onFilesDropped = vi.fn();
      const { result } = renderHook(() => useDragAndDrop({ onFilesDropped }));
      const mockDropEvent = createMockDragEvent([]); // No files

      act(() => {
        result.current.dropzoneProps.onDrop(mockDropEvent);
      });

      expect(onFilesDropped).not.toHaveBeenCalled();
    });
  });

  describe('when disabled', () => {
    it('should not set isDragging to true on onDragEnter', () => {
      const onFilesDropped = vi.fn();
      const { result } = renderHook(() => useDragAndDrop({ onFilesDropped, disabled: true }));
      const mockEvent = createMockDragEvent();

      act(() => {
        result.current.dropzoneProps.onDragEnter(mockEvent);
      });

      expect(result.current.isDragging).toBe(false);
    });

    it('should not call onFilesDropped on onDrop', () => {
      const onFilesDropped = vi.fn();
      const mockFiles = [new File([], 'file.txt')];
      const { result } = renderHook(() => useDragAndDrop({ onFilesDropped, disabled: true }));
      const mockDropEvent = createMockDragEvent(mockFiles);

      act(() => {
        result.current.dropzoneProps.onDrop(mockDropEvent);
      });

      expect(onFilesDropped).not.toHaveBeenCalled();
    });
  });
});