import { ReactNode, useMemo } from 'react';

import { useTranslation } from '../../core/hooks/useTranslation';
import { useTestIdAttribute } from '../../hooks/useTestIdAttribute';
import { CommonProps } from '../../types';
import { assertEmptyObject } from '../../utils/assertEmptyObject';
import { assertUnreachable } from '../../utils/assertUnreachable';
import { makeTestId } from '../../utils/makeTestId';
import { IconGlyph } from '../Icon/constants';

import { ToastAppearance } from './constants';
import { ToastContextProvider } from './contexts/ToastContext';
import { StyledToast, TunedCloseIcon, TunedIcon, TunedToastButton } from './styled';

/** Props for {@link Toast} */
export interface ToastProps extends CommonProps {
  /** Visual appearance */
  appearance?: ToastAppearance;
  /** Function that will be invoked when user click by Dismiss button or by any actions */
  onDismiss?: () => void;
  children: ReactNode;
}

/**
 * Component that can be used as toast in {@link ToastManager}.
 *
 * Usually you should not use this component directly, use {@link useToast} hook instead.
 *
 * ```tsx
 * import { Toast, ToastAppearance } from 'ui-kit';
 *
 * <Toast appearance={ToastAppearance.Success} onDismiss={handleToastDismiss}>
 *   You've won $100 000 000.
 *   <ToastAction onClick={handleToastActionClick}>Send PayPal</ToastAction>
 * </Toast>
 * ```
 */
export function Toast(props: ToastProps) {
  const {
    appearance = ToastAppearance.Default,
    onDismiss,
    children,
    className,
    testId,
    ariaDescribedBy,
    ...rest
  } = props;
  assertEmptyObject(rest);

  const { t } = useTranslation();
  const testIdAttribute = useTestIdAttribute();

  const iconGlyph = useMemo(() => {
    switch (appearance) {
      case ToastAppearance.Default:
        return IconGlyph.InfoSolid;
      case ToastAppearance.Success:
        return IconGlyph.CheckCircle;
      case ToastAppearance.Warning:
        return IconGlyph.Warning;
      case ToastAppearance.Error:
        return IconGlyph.Error;
      /* istanbul ignore next */
      default:
        assertUnreachable(appearance);
    }
  }, [appearance]);

  return (
    <StyledToast
      $appearance={appearance}
      aria-describedby={ariaDescribedBy}
      aria-label={t(`ui.toast.${appearance}`)}
      className={className}
      role="alert"
      {...{ [testIdAttribute]: testId }}
    >
      <ToastContextProvider appearance={appearance} onDismiss={onDismiss}>
        <TunedIcon $appearance={appearance} glyph={iconGlyph} testId={makeTestId(testId, 'icon')} />
        {children}
        <TunedToastButton
          ariaLabel={t('ui.toast.dismissButton')}
          onClick={onDismiss}
          testId={makeTestId(testId, 'dismiss-button')}
        >
          <TunedCloseIcon glyph={IconGlyph.Close} />
        </TunedToastButton>
      </ToastContextProvider>
    </StyledToast>
  );
}
