import React from "react";

type ErrorHandler = (error: Error, info: React.ErrorInfo) => void;
type ErrorHandlingComponent<Props> = (
  props: Props,
  error?: Error
) => React.ReactNode;

type ErrorState = { error?: Error };

function Catch<Props extends object>(
  component: ErrorHandlingComponent<Props>,
  errorHandler?: ErrorHandler
): React.ComponentType<Props> {
  function Inner({ error, props }: { error?: Error; props: Props }) {
    return <React.Fragment>{component(props, error)}</React.Fragment>;
  }

  return class CatchClass extends React.Component<Props, ErrorState> {
    state: ErrorState = {
      error: undefined,
    };

    static getDerivedStateFromError(error: Error) {
      return { error };
    }

    componentDidCatch(error: Error, info: React.ErrorInfo) {
      if (errorHandler) {
        errorHandler(error, info);
      }
    }

    render() {
      return <Inner error={this.state.error} props={this.props} />;
    }
  };
}

const divStyles: React.CSSProperties = {
  display: "flex",
  flexDirection: "column",
  alignItems: "center",
  justifyContent: "center",
};

const ErrorBoundary = Catch(function (
  props: React.PropsWithChildren,
  error?: Error
) {
  if (error) {
    return (
      <div style={divStyles}>
        <h2>
          There was an error on the page. Please reload the application and try
          again.
        </h2>
        <p />
        <p>{error.message}</p>
      </div>
    );
  }

  return <>{props.children}</>;
});

export default ErrorBoundary;
