import React, { ReactElement } from 'react';

import '@testing-library/jest-dom';
import { render } from '@testing-library/react';

import { SnackbarProvider, useManageToasts } from 'components/Toasts';
import { convertClassNameToSelector } from 'utils/common';

import { THEME } from '../../constants';
import { ToastOptions, ToastsManagerProps } from './types';
import { toastClassName, TOAST_TYPE } from './components';

const ToastsManager = ({ toasts, theme = THEME.light }: ToastsManagerProps): ReactElement => {
  const { openToast } = useManageToasts(theme);

  toasts.forEach((toast: ToastOptions) => {
    openToast(toast);
  });

  return <></>;
};

describe('Toasts', () => {
  test('renders no toasts if array is empty', () => {
    const { container } = render(
      <SnackbarProvider>
        <ToastsManager toasts={[]} />
      </SnackbarProvider>,
    );
    const toastContents = container.querySelectorAll(`.${toastClassName('content')}`);
    expect(Array.from(toastContents)).toStrictEqual([]);
  });

  test('properly generates classNames for all themes', () => {
    const defaultToastOptions: ToastOptions = { id: 'test-id', message: 'Default toast', type: TOAST_TYPE.SUCCESS };
    Object.values(THEME).forEach((themeName) => {
      const { container } = render(
        <SnackbarProvider>
          <ToastsManager
            toasts={[defaultToastOptions]}
            theme={themeName}
          />
        </SnackbarProvider>,
      );
      const toastThemeClassName = toastClassName(null, { [themeName]: true, [defaultToastOptions.type]: true });
      expect(toastThemeClassName).not.toBeUndefined();
      const query = convertClassNameToSelector(toastThemeClassName);
      const themeToast = container.querySelector(query);
      expect(themeToast).not.toBeNull();
    });
  });

  test('properly generates classNames for all toast types', () => {
    const defaultTheme = THEME.light;
    Object.values(TOAST_TYPE).forEach((toastType) => {
      const toastOptions: ToastOptions = {
        id: `toast-${toastType}`,
        message: `Toast with type ${toastType}`,
        type: toastType,
      };
      const { container } = render(
        <SnackbarProvider>
          <ToastsManager
            toasts={[toastOptions]}
            theme={defaultTheme}
          />
        </SnackbarProvider>,
      );
      const toastTypeClassName = toastClassName(null, { [defaultTheme]: true, [toastType]: true });
      expect(toastTypeClassName).not.toBeUndefined();
      const query = convertClassNameToSelector(toastTypeClassName);
      const themeToast = container.querySelector(query);
      expect(themeToast).not.toBeNull();
    });
  });

  test("doesn't render action button if it's not provided in props", () => {
    const toastWithoutAction: ToastOptions = {
      id: 'toast-without-action',
      message: 'Toast without action button',
      type: TOAST_TYPE.SUCCESS,
    };
    const { container } = render(
      <SnackbarProvider>
        <ToastsManager toasts={[toastWithoutAction]} />
      </SnackbarProvider>,
    );
    const actionButtonClassName = toastClassName('action-button');
    expect(container.querySelector(`.${actionButtonClassName}`)).toBeNull();
  });

  test('renders action button if provided in props', () => {
    const toastWithAction: ToastOptions = {
      id: 'toast-with-action',
      message: 'Toast with action button',
      type: TOAST_TYPE.SUCCESS,
      actionButton: {
        text: 'Action',
        action: () => {},
      },
    };
    const { container } = render(
      <SnackbarProvider>
        <ToastsManager toasts={[toastWithAction]} />
      </SnackbarProvider>,
    );
    const actionButtonClassName = toastClassName('action-button');
    expect(container.querySelector(`.${actionButtonClassName}`)).not.toBeNull();
  });

  test('renders close button on every toast', () => {
    const toasts: ToastOptions[] = [
      {
        id: 'success-toast',
        message: 'Success toast',
        type: TOAST_TYPE.SUCCESS,
      },
      {
        id: 'error-toast',
        message: 'Error toast',
        type: TOAST_TYPE.ERROR,
        actionButton: {
          text: 'Refresh',
          action: (): void => {},
        },
      },
      {
        id: 'warning-toast',
        message: 'Warning toast',
        type: TOAST_TYPE.WARNING,
      },
    ];
    const { container } = render(
      <SnackbarProvider>
        <ToastsManager toasts={toasts} />
      </SnackbarProvider>,
    );
    const closeButtonClassName = toastClassName('close');
    expect(closeButtonClassName).not.toBeUndefined();
    const closeButtons = Array.from(container.querySelectorAll(`.${closeButtonClassName}`));
    expect(closeButtons.length).toBe(toasts.length);
  });

  test('renders toasts with no duplicates', () => {
    const uniqueMessagesCount = 1;
    const toasts: ToastOptions[] = [
      { id: '1', message: '1', type: TOAST_TYPE.SUCCESS },
      { id: '1', message: '2', type: TOAST_TYPE.WARNING },
    ];
    const toastQuery = `.${toastClassName(null)}`;
    const { container } = render(
      <SnackbarProvider preventDuplicate={true}>
        <ToastsManager toasts={toasts} />
      </SnackbarProvider>,
    );
    expect(Array.from(container.querySelectorAll(toastQuery)).length).toBe(uniqueMessagesCount);
  });

  test('shows up to 3 toasts', () => {
    const defaultMaxSnacks = 3;
    const toasts: ToastOptions[] = [
      { id: '1', message: '1', type: TOAST_TYPE.SUCCESS },
      { id: '2', message: '2', type: TOAST_TYPE.WARNING },
      { id: '3', message: '3', type: TOAST_TYPE.SUCCESS },
      { id: '4', message: '4', type: TOAST_TYPE.WARNING },
    ];
    const toastQuery = `.${toastClassName(null)}`;
    const { container } = render(
      <SnackbarProvider>
        <ToastsManager toasts={toasts} />
      </SnackbarProvider>,
    );
    expect(Array.from(container.querySelectorAll(toastQuery)).length).toBe(defaultMaxSnacks);
  });
});
