import { render, fireEvent, waitFor } from '@testing-library/svelte';
import { describe, it, expect, vi } from 'vitest';
import FileUploader from './FileUploader.svelte';
// Helper to create a File object
function createFile(name = 'file.txt', content = 'hello', type = 'text/plain') {
    return new File([content], name, { type });
}
describe('FileUploader component', () => {
    it('adds file when input changes and shows file name in list', async () => {
        const { container, getByText } = render(FileUploader, { files: [] });
        const input = container.querySelector('input[type="file"]');
        expect(input).toBeTruthy();
        const file = createFile('test.txt', 'content', 'text/plain');
        Object.defineProperty(input, 'files', {
            value: [file],
            writable: false,
        });
        await fireEvent.change(input);
        await waitFor(() => {
            expect(getByText('test.txt')).toBeTruthy();
        });
    });
    it('prevents duplicate files (same name) when adding twice', async () => {
        const { container, getAllByText } = render(FileUploader, { files: [] });
        const input = container.querySelector('input[type="file"]');
        const file = createFile('duplicate.txt');
        Object.defineProperty(input, 'files', { value: [file], writable: false, configurable: true });
        await fireEvent.change(input);
        // Try adding same file again
        Object.defineProperty(input, 'files', { value: [file], writable: false, configurable: true });
        await fireEvent.change(input);
        const matches = getAllByText('duplicate.txt');
        // Should only be listed once
        expect(matches.length).toBe(1);
    });
    it('dispatches validation event and calls onvalidation callback with correct validation state', async () => {
        const onvalidation = vi.fn();
        const { container } = render(FileUploader, {
            files: [],
            allowedextensions: ['png'],
            maxsize: 5, // bytes -> make file too big
            onvalidation,
        });
        const uploaderEl = container.querySelector('.mc-file-uploader');
        let validationEventDetail = null;
        uploaderEl.addEventListener('validation', (e) => {
            validationEventDetail = e.detail;
        });
        const input = container.querySelector('input[type="file"]');
        const file = createFile('image.jpg', 'content'); // extension jpg not allowed, size > 5
        Object.defineProperty(input, 'files', { value: [file], writable: false });
        await fireEvent.change(input);
        await waitFor(() => {
            // callback should have been called
            expect(onvalidation).toHaveBeenCalled();
            // event should have been dispatched with validation state
            expect(validationEventDetail).toBeTruthy();
            // file key should exist
            expect(validationEventDetail['image.jpg']).toBeDefined();
            // extension should be false
            expect(validationEventDetail['image.jpg'].extension).toBe(false);
            // size should be false because maxsize is tiny
            expect(validationEventDetail['image.jpg'].size).toBe(false);
        });
    });
    it('supports drop to add files and allows deleting an item', async () => {
        const { container, getByText, queryByText } = render(FileUploader, {
            files: [],
            format: 'stacked',
            deletebuttonlabel: 'Delete',
        });
        const dropZone = container.querySelector('.mc-file-uploader__input');
        const file = createFile('to-delete.txt');
        await fireEvent.drop(dropZone, {
            dataTransfer: {
                files: [file],
                types: ['Files'],
            },
        });
        await waitFor(() => {
            expect(getByText('to-delete.txt')).toBeTruthy();
        });
        // Find a button with delete label and click it to remove the file
        const deleteButton = getByText('Delete');
        expect(deleteButton).toBeTruthy();
        await fireEvent.click(deleteButton);
        await waitFor(() => {
            expect(queryByText('to-delete.txt')).toBeNull();
        });
    });
    it('adds dragged class on dragenter and removes it after matching dragleave calls', async () => {
        const { container } = render(FileUploader);
        const input = container.querySelector('.mc-file-uploader__input');
        expect(input).toBeTruthy();
        // first enter -> dragged
        await fireEvent.dragEnter(input);
        expect(input.classList.contains('mc-file-uploader__input--dragged')).toBe(true);
        // second enter -> still dragged (counter increment)
        await fireEvent.dragEnter(input);
        expect(input.classList.contains('mc-file-uploader__input--dragged')).toBe(true);
        // one leave -> still dragged (counter not zero)
        await fireEvent.dragLeave(input);
        expect(input.classList.contains('mc-file-uploader__input--dragged')).toBe(true);
        // final leave -> not dragged
        await fireEvent.dragLeave(input);
        expect(input.classList.contains('mc-file-uploader__input--dragged')).toBe(false);
    });
    it('ignores drag events when hasdragdrop is false', async () => {
        const { container } = render(FileUploader, { hasdragdrop: false });
        const input = container.querySelector('.mc-file-uploader__input');
        expect(input).toBeTruthy();
        await fireEvent.dragEnter(input);
        expect(input.classList.contains('mc-file-uploader__input--dragged')).toBe(false);
        await fireEvent.dragLeave(input);
        expect(input.classList.contains('mc-file-uploader__input--dragged')).toBe(false);
    });
    it('keyboard "Enter" or " " triggers file input click', async () => {
        const { container } = render(FileUploader, { files: [] });
        const dropZone = container.querySelector('.mc-file-uploader__input');
        const input = container.querySelector('input[type="file"]');
        expect(input).toBeTruthy();
        const clickSpy = vi.spyOn(input, 'click');
        // press Enter
        await fireEvent.keyDown(dropZone, { key: 'Enter' });
        // press Space
        await fireEvent.keyDown(dropZone, { key: ' ' });
        expect(clickSpy).toHaveBeenCalled();
        clickSpy.mockRestore();
    });
    it('does not render files list if showfileslist is false', () => {
        const { container } = render(FileUploader, { props: { files: [], showfileslist: false } });
        expect(container.querySelector('.mc-file-uploader__files-list')).toBeNull();
    });
});
