import { Button, Spinner } from "@fluentui/react-components";
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;
  loadingMessage?: string;
}): React.ReactElement {
  const [state, setState] = useState(State.Loading);

  const counter = useRef<any | null>(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) ?? (
        <div
          style={{
            display: "flex",
            justifyContent: "center",
            alignItems: "center",
            height: "100%",
            flex: "1",
            flexDirection: "column",
          }}
        >
          <div style={{ margin: "50px" }}>{p.errMsg}</div>

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

          {p.errAdditionalElement && p.errAdditionalElement()}
        </div>
      )
    );

  if (state === State.Loading || p.ready === false)
    return (
      p.buildLoading?.() ?? (
        <Spinner
          labelPosition="below"
          label={p.loadingMessage}
          style={{ margin: "auto" }}
        />
      )
    );

  return p.build();
}
