import React, { useReducer, useMemo, useCallback } from "react";
import PropTypes from "prop-types";
import { toast } from "react-toastify";
import useStyles from "../components/Toast/styles";
import ToastContainer from "../components/Toast/ToastContainer";
import Notification from "../components/Notification";

const positions = [
  toast.POSITION.TOP_LEFT,
  toast.POSITION.TOP_CENTER,
  toast.POSITION.TOP_RIGHT,
  toast.POSITION.BOTTOM_LEFT,
  toast.POSITION.BOTTOM_CENTER,
  toast.POSITION.BOTTOM_RIGHT
];

const ToastStateContext = React.createContext();
const ToastDispatchContext = React.createContext();

const toastInitialState = { position: 2 };
const toastReducer = (state, action) => {
  switch (action.type) {
    case "onNotificationPositionChange":
      return { ...state, position: action.payload.position };
    default: {
      return state;
    }
  }
};

export const toastDispatchActions = (dispatch) => ({
  changeNotificationPosition: (position) =>
    dispatch({ type: "onNotificationPositionChange", payload: { position } })
});

const ToastProvider = ({ children }) => {
  const [state, dispatch] = useReducer(toastReducer, toastInitialState);
  const dispatchActions = useMemo(() => toastDispatchActions(dispatch), [
    dispatch
  ]);

  const classes = useStyles();

  const sendNotification = useCallback(
    (componentProps, options) => {
      return toast(
        <Notification
          {...componentProps}
          className={classes.notificationComponent}
        />,
        options
      );
    },
    [classes]
  );

  const notify = useCallback(
    (message, type, extraButtonClick) => {
      let componentProps;

      switch (type) {
        case "info":
          componentProps = {
            type: "feedback",
            message,
            variant: "contained",
            color: "primary"
          };
          break;
        case "error":
          componentProps = {
            type: "message",
            message,
            variant: "contained",
            color: "secondary",
            extraButton: "Resend",
            extraButtonClick
          };
          break;
        case "defence":
          componentProps = {
            type: "defence",
            message,
            variant: "contained",
            color: "info"
          };
          break;
        case "disk":
          componentProps = {
            type: "disc",
            message,
            variant: "rounded",
            color: "info"
          };
          break;
        default:
          componentProps = {
            type: "shipped",
            message,
            variant: "contained",
            color: "success"
          };
      }

      const toastId = sendNotification(componentProps, {
        type,
        position: positions[state.position],
        progressClassName: classes.progress,
        className: classes.notification
      });

      return toastId;
    },
    [sendNotification, classes, state.position]
  );

  const actions = useMemo(
    () => ({
      ...dispatchActions,
      info: (message) => notify(message, "info"),
      error: (message) => notify(message, "error"),
      success: (message) => notify(message, "success"),
      defence: (message) => notify(message, "defence"),
      notify
    }),
    [dispatchActions, notify]
  );

  return (
    <ToastStateContext.Provider value={state}>
      <ToastDispatchContext.Provider value={actions}>
        <>
          {children}
          <ToastContainer />
        </>
      </ToastDispatchContext.Provider>
    </ToastStateContext.Provider>
  );
};

ToastProvider.propTypes = {
  children: PropTypes.node
};

const useToast = () => {
  const context = React.useContext(ToastDispatchContext);
  if (context === undefined) {
    throw new Error("useToast must be used within a ToastProvider");
  }
  return context;
};

const useToastState = () => {
  var context = React.useContext(ToastStateContext);
  if (context === undefined) {
    throw new Error("useToastState must be used within a ToastProvider");
  }
  return context;
};

export { ToastProvider, useToast, useToastState };
