import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { testComponent } from 'lib/test-utils/common';
import { UploadArea } from '../uploadArea';
import type { UploadAreaProps } from '../types';

describe('<UploadArea>', () => {
    const defaultProps: UploadAreaProps = {
        onUpload: vi.fn(),
        onDropRejected: vi.fn(),
        inputId: 'modern-upload',
        variant: 'large',
        info: {
            title: 'Upload Files',
            description: 'Drag and drop files here',
            illustration: 'emptyState',
        },
    };

    testComponent(UploadArea as any, defaultProps);

    const renderComponent = (props?: Partial<UploadAreaProps>) =>
        render(<UploadArea {...defaultProps} {...props} />);

    test('applies style', () => {
        renderComponent({ style: { marginTop: 10 } });
        expect(screen.getByTestId('UploadArea')).toHaveStyle({ marginTop: '10px' });
    });

    describe('variant prop', () => {
        test.each<UploadAreaProps['variant']>(['large', 'medium', 'small'])(
            'renders %s variant with correct className',
            (variant) => {
                renderComponent({ variant });

                expect(screen.getByTestId('UploadArea')).toHaveClass(`UploadArea-${variant}`);
            }
        );
    });

    describe('info prop', () => {
        test('renders title, description and illustration', () => {
            renderComponent({
                info: {
                    title: 'Test Title',
                    description: 'Test Description',
                    illustration: 'emptyState',
                },
            });

            expect(screen.getByText('Test Title')).toBeInTheDocument();
            expect(screen.getByText('Test Description')).toBeInTheDocument();
            expect(screen.getByTestId('EmptyStateIllustration')).toBeInTheDocument();
        });

        test('defaults to uploadArea illustration when not specified', () => {
            const r = renderComponent({
                info: {
                    title: 'Upload Your File',
                    description: 'Drag and drop or browse.',
                },
            });

            expect(r.getByText('Upload Your File')).toBeInTheDocument();
            expect(r.getByTestId('UploadAreaIllustration')).toBeInTheDocument();
        });

        test('renders LoadingSpinner when illustration is "spinner"', () => {
            renderComponent({
                variant: 'large',
                info: {
                    title: 'Uploading...',
                    description: 'Please wait',
                    illustration: 'spinner',
                },
            });

            expect(screen.getByTestId('LoadingSpinner')).toBeInTheDocument();
        });

        test('renders LoadingSpinner with percentage for large variant', () => {
            renderComponent({
                variant: 'large',
                info: {
                    title: 'Uploading...',
                    description: 'Please wait',
                    illustration: 'spinner',
                    percentage: 50,
                },
            });

            expect(screen.getByText('50%')).toBeInTheDocument();
        });

        test.each<[UploadAreaProps['variant'], number]>([
            ['small', 48],
            ['medium', 80],
            ['large', 80],
        ])('renders LoadingSpinner with size %i for %s variant', (variant, expectedSize) => {
            renderComponent({
                variant,
                info: {
                    title: 'Uploading...',
                    description: 'Please wait',
                    illustration: 'spinner',
                },
            });

            const spinner = screen.getByTestId('LoadingSpinner');
            expect(spinner.querySelector('.LoadingSpinner-inner')).toHaveClass(
                `size-${expectedSize}`
            );
        });
    });

    describe('uploadStatus prop', () => {
        test('does not render UploadAreaStatus when uploadStatus is not provided', () => {
            renderComponent();

            expect(screen.queryByTestId('Status')).not.toBeInTheDocument();
        });

        test('renders UploadAreaStatus when uploadStatus is provided', () => {
            renderComponent({
                uploadStatus: {
                    status: '3 files uploaded',
                    statusVariant: 'success',
                    header: 'Files',
                    files: [
                        { name: 'file1.pdf', status: 'Success', statusVariant: 'success' },
                        { name: 'file2.pdf', status: 'Success', statusVariant: 'success' },
                        { name: 'file3.pdf', status: 'Failed', statusVariant: 'error' },
                    ],
                },
            });

            expect(screen.getByTestId('Status')).toBeVisible();
            expect(screen.getByText('3 files uploaded')).toBeVisible();
        });
    });

    describe('interaction', () => {
        test('opens file dialog on click', () => {
            renderComponent();
            const dropzone = screen.getByTestId('UploadArea').querySelector('[role="none"]');
            const input = screen
                .getByTestId('UploadArea')
                .querySelector('input[type="file"]') as HTMLInputElement;

            const clickSpy = vi.fn();
            if (input) {
                input.click = clickSpy;
            }

            if (dropzone) {
                fireEvent.click(dropzone);
            }

            expect(clickSpy).toHaveBeenCalled();
        });

        test('applies disabled className when disabled', () => {
            renderComponent({ disabled: true });
            const dropzone = screen.getByTestId('UploadArea').querySelector('.UploadArea-dropzone');

            expect(dropzone).toHaveClass('UploadArea-dropzone-disabled');
        });
    });

    describe('file input attributes', () => {
        test('sets correct input id', () => {
            renderComponent({ inputId: 'custom-id' });
            const input = screen.getByTestId('UploadArea').querySelector('input[type="file"]');

            expect(input).toHaveAttribute('id', 'custom-id');
        });

        test('sets folder attributes for multiple file selection', () => {
            renderComponent({ multipleFileSelection: true });
            const input = screen.getByTestId('UploadArea').querySelector('input[type="file"]');

            expect(input).toHaveAttribute('webkitdirectory');
            expect(input).toHaveAttribute('mozdirectory');
            expect(input).toHaveAttribute('directory');
        });

        test('does not set folder attributes for single file selection', () => {
            renderComponent({ multipleFileSelection: false });
            const input = screen.getByTestId('UploadArea').querySelector('input[type="file"]');

            expect(input).not.toHaveAttribute('webkitdirectory');
            expect(input).not.toHaveAttribute('mozdirectory');
            expect(input).not.toHaveAttribute('directory');
        });
    });

    describe('children', () => {
        test('renders children inside dropzone', () => {
            renderComponent({
                children: <div>Custom Content</div>,
            });

            expect(screen.getByText('Custom Content')).toBeInTheDocument();
        });
    });

    describe('errorMessage prop', () => {
        test('does not render error message when not provided', () => {
            const r = renderComponent();

            expect(r.queryByTestId('UploadArea-error-message')).not.toBeInTheDocument();
        });

        test('renders error message when provided as string', () => {
            const r = renderComponent({ errorMessage: 'Something went wrong.' });

            expect(r.getByTestId('UploadArea-error-message')).toBeVisible();
            expect(r.getByText('Something went wrong.')).toBeVisible();
        });

        test('renders error message when provided as ReactNode', () => {
            const r = renderComponent({
                errorMessage: <span>Custom error content</span>,
            });

            expect(r.getByTestId('UploadArea-error-message')).toBeVisible();
            expect(r.getByText('Custom error content')).toBeVisible();
        });

        test('renders multi-line error message with bullet points', () => {
            const r = renderComponent({
                errorMessage: (
                    <ul style={{ margin: 0, paddingLeft: '1rem' }}>
                        <li>File size exceeds the maximum limit of 10MB.</li>
                        <li>Only .mp3, .wav, and .flac formats are accepted.</li>
                        <li>File name contains invalid characters.</li>
                    </ul>
                ),
            });

            const errorElement = r.getByTestId('UploadArea-error-message');

            expect(errorElement).toBeVisible();
            expect(r.getByText('File size exceeds the maximum limit of 10MB.')).toBeVisible();
            expect(r.getByText('Only .mp3, .wav, and .flac formats are accepted.')).toBeVisible();
            expect(r.getByText('File name contains invalid characters.')).toBeVisible();

            const listItems = errorElement.querySelectorAll('li');
            expect(listItems).toHaveLength(3);
        });

        test('keeps the normal illustration when errorMessage is present', () => {
            const r = renderComponent({
                info: {
                    title: 'Upload Your File',
                    description: 'Drag and drop or browse.',
                    illustration: 'uploadArea',
                },
                errorMessage: 'Something went wrong.',
            });

            expect(r.getByTestId('UploadAreaIllustration')).toBeInTheDocument();
            expect(r.getByTestId('UploadArea-error-message')).toBeVisible();
        });

        test('renders error message below the dropzone', () => {
            const r = renderComponent({ errorMessage: 'Error below dropzone' });

            const uploadArea = r.getByTestId('UploadArea');
            const dropzone = uploadArea.querySelector('.UploadArea-dropzone');
            const errorMessage = r.getByTestId('UploadArea-error-message');

            expect(dropzone).toBeInTheDocument();
            expect(errorMessage).toBeInTheDocument();

            const dropzoneIndex = Array.from(uploadArea.children).indexOf(dropzone as Element);
            const errorIndex = Array.from(uploadArea.children).indexOf(errorMessage);

            expect(errorIndex).toBeGreaterThan(dropzoneIndex);
        });

        test.each<UploadAreaProps['variant']>(['large', 'medium', 'small'])(
            'renders error message for %s variant',
            (variant) => {
                const r = renderComponent({
                    variant,
                    errorMessage: `Error for ${variant}`,
                });

                expect(r.getByTestId('UploadArea-error-message')).toBeVisible();
            }
        );
    });
});
