import { render, screen, userEvent } from '../../test-utils';

import UploadButton, { UploadButtonProps, TEST_IDS } from './UploadButton';
import { imageFileTypes } from './defaults';

describe('UploadButton', () => {
  const pngFile = new File(['foo'], 'foo.png', { type: 'image/png' });
  const jpgFile = new File(['foo'], 'foo.jpg', { type: 'image/jpg' });

  const props: UploadButtonProps = {
    onChange: jest.fn(),
  };

  const renderComponent = (customProps = props) => render(<UploadButton {...customProps} />);

  describe('single file upload', () => {
    beforeEach(() => {
      renderComponent();
    });

    it('should render the file input form', () => {
      expect(screen.getByText('Upload file')).toBeInTheDocument();
    });

    it('should render the file input button with title, if provided', () => {
      const buttonTitle = 'Upload your id';
      renderComponent({ ...props, uploadButtonTitle: buttonTitle });
      expect(screen.getByText(buttonTitle)).toBeInTheDocument();
    });

    it('should set accept to imageTypes', () => {
      expect(screen.getByTestId(TEST_IDS.uploadInput)).toHaveAttribute(
        'accept',
        imageFileTypes.join(','),
      );
    });

    it('trigger onChange callback with a FileList', async () => {
      const input = screen.getByTestId(TEST_IDS.uploadInput);
      await userEvent.upload(input, [pngFile, jpgFile]);

      expect(props.onChange).toHaveBeenCalledTimes(1);
      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
      expect((props.onChange as jest.Mock).mock.calls[0][0]).toHaveLength(1);
    });
  });

  describe('multiple file upload', () => {
    beforeEach(() => {
      (props.onChange as jest.Mock).mockClear();
      renderComponent({ ...props, multiple: true });
    });

    it('should disable the file input', () => {
      expect(screen.getByTestId(TEST_IDS.uploadInput)).toHaveAttribute('multiple');
    });

    it('trigger onChange callback with a FileList', async () => {
      const input = screen.getByTestId(TEST_IDS.uploadInput);
      await userEvent.upload(input, [pngFile, jpgFile]);

      expect(props.onChange).toHaveBeenCalledTimes(1);
      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
      expect((props.onChange as jest.Mock).mock.calls[0][0]).toHaveLength(2);
    });
  });

  describe('disabled state', () => {
    beforeEach(() => {
      renderComponent({ ...props, disabled: true });
    });

    it('should disable the file input', () => {
      expect(screen.getByTestId(TEST_IDS.uploadInput)).toBeDisabled();
    });
  });

  describe('accept all file types', () => {
    beforeEach(() => {
      renderComponent({ ...props, fileTypes: ['*'] });
    });

    it('should not add accept attribute to file input', () => {
      expect(screen.getByTestId(TEST_IDS.uploadInput)).not.toHaveAttribute('accept');
    });
  });

  describe('description for the button', () => {
    it('should show default description based on the default file types provided', () => {
      renderComponent({ ...props });
      expect(screen.getByText('PDF, JPG, JPEG, PNG, less than 5MB')).toBeInTheDocument();
    });

    it('should show default all files allowed description if all files are accepted', () => {
      renderComponent({ ...props, fileTypes: ['*'] });
      expect(screen.getByText('All file types, less than 5MB')).toBeInTheDocument();
    });

    it('should show no file size limit if sizeLimit is set to `null`', () => {
      renderComponent({ ...props, fileTypes: ['*'], sizeLimit: null });
      expect(screen.getByText('All file types')).toBeInTheDocument();
      expect(screen.queryByText(/less than/i)).not.toBeInTheDocument();
    });

    it('should show custom description if provided', () => {
      const description = 'Test description';
      renderComponent({ ...props, fileTypes: ['*'], description });
      expect(screen.getByText('Test description')).toBeInTheDocument();
    });

    it('should show maxFiles next to the description, if provided', () => {
      renderComponent({ ...props, fileTypes: ['*'], maxFiles: 5 });
      expect(screen.getByText(/Maximum 5 files\./)).toBeInTheDocument();
    });
  });
});
