import React, { useMemo } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { Notification } from './Notification';
import { NotificationItem } from './NotificationItem';
import { NotificationActions } from './NotificationActions';

const propTypes = {
  /**
   * The children that will be rendered by the NotificationProvider.
   */
  children: PropTypes.any.isRequired,
  /**
   * Render the Notification root element in the current DOM hierarchy.
   */
  isPortalDisabled: PropTypes.bool,
  /**
   * The target where the Notification node should render when the portal is
   * enabled
   */
  portalTarget: PropTypes.instanceOf(Element),
};

const defaultProps = {
  isPortalDisabled: false,
  portalTarget: document.body,
};

export const NotificationContext = React.createContext();

export const NotificationProvider = ({
  children, isPortalDisabled, portalTarget, ...attributes
}) => {
  const [notifications, setNotifications] = React.useState([]);

  /**
   * Remove a notification from the notification context.
   * @param {string} key The previously set key on the notification object.
   */
  const clearNotification = (key) => {
    setNotifications((notifs) => notifs.filter((notfication) => notfication.key !== key));
  };

  const throwNotifcation = (color = '') => (content, actions = '', {
    cancelLabel = 'Undo',
    duration = 0,
    key = undefined,
    onDurationEndAction = undefined,
    onCancelDurationEndAction = () => {},
  } = {}) => {
    const notificationKey = key || Math.random().toString(36).substr(2, 9);

    setNotifications((notifs) => [...notifs.filter((notfication) => notfication.key !== key), {
      actions,
      cancelLabel,
      color,
      content,
      key: notificationKey,
      duration,
      onDurationEndAction,
      onCancelDurationEndAction,
    }]);

    return notificationKey;
  };

  const clearAllNotifications = () => {
    setNotifications([]);
  };

  const value = useMemo(() => ({
    clearAllNotifications,
    throwError: throwNotifcation('danger'),
    throwSuccess: throwNotifcation('success'),
    throwWarning: throwNotifcation('warning'),
    throwImportant: throwNotifcation('primary'),
    throwMessage: throwNotifcation(),
    clearNotification,
  }), []);

  const notificationNode = (
    <Notification {...attributes}>
      {notifications.map((notification) => (
        <NotificationItem
          cancelLabel={notification.cancelLabel}
          color={notification.color}
          key={notification.key}
          onClose={() => clearNotification(notification.key)}
          duration={notification.duration}
          onDurationEndAction={notification.onDurationEndAction}
          onCancelDurationEndAction={notification.onCancelDurationEndAction}
        >
          {notification.content}
          {notification.actions && (
            <NotificationActions>
              {notification.actions}
            </NotificationActions>
          )}
        </NotificationItem>
      ))}
    </Notification>
  );

  const mountNotificationNode = () => {
    if (isPortalDisabled) {
      return notificationNode;
    }

    return ReactDOM.createPortal(notificationNode, portalTarget);
  };

  return (
    <NotificationContext.Provider value={value}>
      { children }
      { mountNotificationNode() }
    </NotificationContext.Provider>
  );
};

export const useNotification = () => React.useContext(NotificationContext);

NotificationProvider.propTypes = propTypes;
NotificationProvider.defaultProps = defaultProps;
