/**
 * Detect and support multiple React version
 * this file is served by the user's code
 */
import React, { createElement } from "react";
import { createPortal } from "react-dom";

let renderHelper;
export function setRenderHelper(helper) {
  renderHelper = helper;
}

/** @type Array<React.ReactPortal> */
const portals = new Map();

export function registerPortal(
  Component,
  props,
  element,
  ccRenderingContext,
  SuperblocksCCReactContext,
) {
  if (!ccRenderingContext)
    throw new TypeError("ccRenderingContext is required");
  const portal = createPortal(
    <SuperblocksCCReactContext.Provider value={ccRenderingContext}>
      <CustomComponentErrorBoundary componentRef={Component}>
        <Component {...props} />
      </CustomComponentErrorBoundary>
    </SuperblocksCCReactContext.Provider>,
    element,
    ccRenderingContext.widgetId,
  );
  portals.set(ccRenderingContext.widgetId, portal);
  renderHelper(
    createElement(React.Fragment, null, Array.from(portals.values())),
  );
}
export function removePortal(key) {
  portals.delete(key);
  renderHelper(
    createElement(React.Fragment, null, Array.from(portals.values())),
  );
}

class CustomComponentErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, errorDisplay: null };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    const errorDisplay = error ?? null;
    return { hasError: true, errorDisplay };
  }

  componentDidCatch(error) {
    console.error(error);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.componentRef !== this.props.componentRef) {
      // Clear error boundary if component is changed
      this.setState({ hasError: false, errorDisplay: null });
    }
  }

  render() {
    return (
      <div
        style={{
          height: "100%",
          width: "100%",
        }}
      >
        {this.state.hasError && this.state.errorDisplay ? (
          <div
            style={{
              height: "100%",
              width: "100%",
              backgroundColor: "white",
              display: "flex",
              flexDirection: "column",
              justifyContent: "center",
              alignItems: "center",
              textAlign: "center",
              borderRadius: "4px",
              border: "1px solid #F45252",
            }}
          >
            <p>
              <span
                style={{
                  display: "inline-block",
                  cursor: "pointer",
                  color: "#29bbff",
                }}
                role="button"
                tabIndex={0}
                onClick={() => {
                  this.setState({ hasError: false, errorDisplay: null });
                }}
              >
                &#8635; Reload component
              </span>{" "}
            </p>
            <p>
              <span
                style={{
                  display: "inline-block",
                  cursor: "pointer",
                  color: "#F45252",
                  textDecorationLine: "underline",
                }}
                role="button"
                tabIndex={1}
                onClick={() => {
                  const errorEvent = new CustomEvent("cc-error-display", {
                    detail: {
                      stack: this.state.errorDisplay?.stack,
                      message: this.state.errorDisplay?.message,
                    },
                  });
                  window.dispatchEvent(errorEvent);
                }}
              >
                &#10071; View Error
              </span>
            </p>
          </div>
        ) : (
          this.props.children
        )}
      </div>
    );
  }
}

export { CustomComponentErrorBoundary };
