import { Button, Theme } from "@mui/material";
import { makeStyles } from "@mui/styles";
import {
  SnackbarAction,
  SnackbarProvider as NotistackSnackbarProvider,
  useSnackbar as useNotistackSnackbar
} from "notistack";
import React, { createContext, PropsWithChildren, useContext } from "react";

const useStyles = makeStyles((theme: Theme) => ({
  bottomLeft: {
    position: "absolute",
    bottom: theme.spacing(7),
    left: theme.spacing(1),
    [theme.breakpoints.up("sm")]: {
      bottom: theme.spacing(1)
    }
  }
}));

interface ISnackbarContext {
  success: (message: string, action?: SnackbarAction) => void;
  warning: (message: string, action?: SnackbarAction) => void;
  error: (message: string, action?: SnackbarAction) => void;
  info: (message: string, action?: SnackbarAction) => void;
  openSnack: (message: string, action?: SnackbarAction) => void;
  closeSnackbar: (key?: string | number | undefined) => void;
}

type Status = "error" | "success" | "warning" | "info" | "default";
let openExternalSnack: (status: Status, message: string) => void;
export const SnackbarContext = createContext<ISnackbarContext>({} as ISnackbarContext);

interface Props {}

function SnackbarHelper(props: PropsWithChildren<Props>) {
  const { children } = props;
  const { enqueueSnackbar, closeSnackbar } = useNotistackSnackbar();

  const openNewSnack = (status: Status, message: string) => {
    switch (status) {
      case "success":
        success(message);
        break;
      case "warning":
        warning(message);
        break;
      case "error":
        error(message);
        break;
      case "info":
        info(message);
        break;
      default:
        openSnack(message);
        break;
    }
  };
  openExternalSnack = openNewSnack;

  const openSnack = (message: string, action?: SnackbarAction) => {
    enqueueSnackbar(message, { variant: "default", action });
  };

  const success = (message: string, action?: SnackbarAction) => {
    enqueueSnackbar(message, { variant: "success", action });
  };

  const warning = (message: string, action?: SnackbarAction) => {
    enqueueSnackbar(message, { variant: "warning", action });
  };

  const error = (message: string, action?: SnackbarAction) => {
    let msg = message as any;
    if (typeof message !== "string") {
      if (Object.keys(message).includes("response")) {
        msg = msg.response.data.error;
      }
    }
    enqueueSnackbar(msg, { variant: "error", action });
  };

  const info = (message: string, action?: SnackbarAction) => {
    enqueueSnackbar(message, { variant: "info", action });
  };

  return (
    <SnackbarContext.Provider value={{ success, warning, error, info, openSnack, closeSnackbar }}>
      {children}
    </SnackbarContext.Provider>
  );
}

export default function SnackbarProvider(props: PropsWithChildren<Props>) {
  const { children } = props;
  const classes = useStyles();
  const providerRef = React.createRef<NotistackSnackbarProvider>();

  const onClickDismiss = (key: React.ReactText) => () => {
    if (providerRef && providerRef.current) {
      providerRef.current.closeSnackbar(key);
    }
  };

  return (
    <NotistackSnackbarProvider
      ref={providerRef}
      maxSnack={1}
      anchorOrigin={{
        vertical: "bottom",
        horizontal: "left"
      }}
      hideIconVariant
      autoHideDuration={7000}
      action={key => (
        <Button
          key={"snackbar-" + key}
          size="small"
          aria-label="close"
          color="inherit"
          onClick={onClickDismiss(key)}>
          Dismiss
        </Button>
      )}
      classes={{
        containerAnchorOriginBottomLeft: classes.bottomLeft
      }}>
      <SnackbarHelper>{children}</SnackbarHelper>
    </NotistackSnackbarProvider>
  );
}

export const useSnackbar = () => useContext(SnackbarContext);

// Snackbar access for external sources that are not React components
export const openSnackbar = (status: Status, message: string) => {
  if (openExternalSnack === undefined) {
    console.warn("openSnackbar is undefined - most likely not loaded into state yet");
  } else {
    openExternalSnack(status, message);
  }
};
