import Snackbar from '@mui/material/Snackbar';
import { Alert } from '../alert';
import { Modal } from '../modal';
import type { ModalProps } from '../modal';
import { useEffect, useState } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import type {
  NotificationProps,
  ShowNotifAction,
  ShowNotifOptions,
  HandleNotificationClose
} from './notification.types';
import uniqueId from 'lodash/uniqueId';

const defaultNotifOptions: ShowNotifOptions = {
  method: 'wait'
};

type Modals = ModalProps[];
let notificationHandler: Dispatch<SetStateAction<NotificationProps[]>> | null = null;

let modalHandler: Dispatch<SetStateAction<Modals>> | null = null;

function subscribe(
  notifFn: Dispatch<SetStateAction<NotificationProps[]>>,
  modalFn: Dispatch<SetStateAction<Modals>>
) {
  notificationHandler = notifFn;
  modalHandler = modalFn;

  return () => {
    notificationHandler = null;
    modalHandler = null;
  };
}

export function hideNotif() {
  if (notificationHandler) {
    notificationHandler(prev => prev.slice(1));
  }
}

export const showNotif: ShowNotifAction = (message, opt = defaultNotifOptions) => {
  if (notificationHandler) {
    const { method, ...rest } = opt;
    const key = uniqueId('notif');
    if (method === 'replace') {
      notificationHandler(prev => [{ message, key, ...rest }, ...prev.slice(1)]);
    } else {
      notificationHandler(prev => [...prev, { message, key, ...rest }]);
    }
  }
};

export const closeModal = (id?: string) => {
  if (modalHandler) {
    modalHandler(modals => (id ? modals.filter(modal => modal.id !== id) : []));
  }
};

export const openModal = (
  modal: Omit<ModalProps, 'id' | 'open' | 'onClose'>,
  onModalOpened?: () => void
): string | void => {
  if (modalHandler) {
    const id = Date.now().toString();
    modalHandler(modals =>
      modals.concat({
        ...modal,
        id,
        open: true,
        onClose: () => {
          closeModal(id);
        }
      })
    );
    onModalOpened?.();
    return id;
  }
};

const Notification = () => {
  const [notifications, setNotifications] = useState<NotificationProps[]>([]);
  const [modals, setModals] = useState<Modals>([]);
  useEffect(() => {
    subscribe(setNotifications, setModals);
  }, []);

  if (modals.length)
    return (
      <>
        {modals.map(modal => (
          <Modal key={modal.id} {...modal} />
        ))}
      </>
    );

  const currNotif = notifications[0];
  if (!currNotif) return null;

  const {
    message,
    ms = 6000,
    alertProps,
    onAfterClose,
    onBeforeClose,
    withCloseButton = true,
    snackbarProps,
    disableDismissMethod,
    key
  } = currNotif;

  const handleClose: HandleNotificationClose = (_event, reason) => {
    if (!disableDismissMethod?.[reason] || (withCloseButton && reason === 'userclick')) {
      const notif = currNotif;
      if (onBeforeClose) onBeforeClose(currNotif);
      hideNotif();
      if (onAfterClose) onAfterClose(notif);
    }
  };
  return currNotif ? (
    <Snackbar
      open
      autoHideDuration={alertProps?.action ? null : ms}
      onClose={handleClose}
      key={key}
      {...snackbarProps}
    >
      <Alert
        {...alertProps}
        onClose={withCloseButton ? e => handleClose(e, 'userclick') : undefined}
      >
        {message}
      </Alert>
    </Snackbar>
  ) : null;
};

export default Notification;
