import { Alert, Box, Button, CircularProgress } from "@mui/material";
import React, { useEffect, useRef, useState } from "react";

enum State {
  Loading,
  Ready,
  Error,
}

export function AsyncWidget(p: {
  loadKey: any;
  load: () => Promise<void>;
  errMsg: string;
  build: () => React.ReactElement;
  ready?: boolean;
  buildLoading?: () => React.ReactElement;
  buildError?: (e: string) => React.ReactElement;
  errAdditionalElement?: () => React.ReactElement;
}): React.ReactElement {
  const [state, setState] = useState(State.Loading);

  const counter = useRef<any>(null);

  const load = async () => {
    try {
      setState(State.Loading);
      await p.load();
      setState(State.Ready);
    } catch (e) {
      console.error(e);
      setState(State.Error);
    }
  };

  useEffect(() => {
    if (counter.current === p.loadKey) return;
    counter.current = p.loadKey;

    load();
  });

  if (state === State.Error)
    return (
      p.buildError?.(p.errMsg) ?? (
        <Box
          component="div"
          sx={{
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            height: "100%",
            flex: "1",
            flexDirection: "column",
            backgroundColor: (theme) =>
              theme.palette.mode === "light"
                ? theme.palette.grey[100]
                : theme.palette.grey[900],
          }}
        >
          <Alert
            variant="outlined"
            severity="error"
            style={{ margin: "0px 15px 15px 15px" }}
          >
            {p.errMsg}
          </Alert>

          <Button onClick={load}>Try again</Button>

          {p.errAdditionalElement?.()}
        </Box>
      )
    );

  if (state === State.Loading || p.ready === false)
    return (
      p.buildLoading?.() ?? (
        <Box
          component="div"
          sx={{
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            height: "100%",
            flex: "1",
            backgroundColor: (theme) =>
              theme.palette.mode === "light"
                ? theme.palette.grey[100]
                : theme.palette.grey[900],
          }}
        >
          <CircularProgress />
        </Box>
      )
    );

  return p.build();
}
