import React from "react";
import ReactDOM from "react-dom/client";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";
import { logger } from "./log";

export default class ShadowDomManager {
  #hostElement = null;
  #reactRoot = null;
  #isVisible = false;
  #isProcessing = false;

  _id;
  _className;
  _ReactComponent;
  _props;

  constructor({
    id,
    className = "",
    reactComponent,
    props = {},
    rootElement = document.body,
  }) {
    if (!id || !reactComponent) {
      throw new Error("ID and a React Component must be provided.");
    }
    this._id = id;
    this._className = className;
    this._ReactComponent = reactComponent;
    this._props = props;
    this._rootElement = rootElement;
  }

  get isVisible() {
    return this.#isVisible;
  }

  show(props) {
    if (this.#isVisible || this.#isProcessing) {
      return;
    }

    if (!this.#hostElement) {
      this.#isProcessing = true;
      try {
        this.#mount(props || this._props);
      } catch (error) {
        logger.warn(`Failed to mount component with id "${this._id}":`, error);
        this.#isProcessing = false;
        return;
      } finally {
        this.#isProcessing = false;
      }
    }

    this.#hostElement.style.display = "";
    this.#isVisible = true;
  }

  hide() {
    if (!this.#isVisible || !this.#hostElement) {
      return;
    }
    this.#hostElement.style.display = "none";
    this.#isVisible = false;
  }

  destroy() {
    if (!this.#hostElement) {
      return;
    }
    this.#isProcessing = true;

    if (this.#reactRoot) {
      this.#reactRoot.unmount();
    }

    this.#hostElement.remove();

    this.#hostElement = null;
    this.#reactRoot = null;
    this.#isVisible = false;
    this.#isProcessing = false;
    logger.info(`Component with id "${this._id}" has been destroyed.`);
  }

  toggle(props) {
    if (this.#isVisible) {
      this.hide();
    } else {
      this.show(props || this._props);
    }
  }

  #mount(props) {
    const host = document.createElement("div");
    host.id = this._id;
    if (this._className) {
      host.className = this._className;
    }

    this._rootElement.appendChild(host);
    this.#hostElement = host;
    const shadowContainer = host.attachShadow({ mode: "open" });
    const appRoot = document.createElement("div");
    appRoot.className = `${this._id}_wrapper notranslate`;
    shadowContainer.appendChild(appRoot);

    const cache = createCache({
      key: this._id,
      prepend: true,
      container: shadowContainer,
    });

    const enhancedProps = {
      ...props,
      onClose: this.hide.bind(this),
    };

    const ComponentToRender = this._ReactComponent;
    this.#reactRoot = ReactDOM.createRoot(appRoot);
    this.#reactRoot.render(
      <React.StrictMode>
        <CacheProvider value={cache}>
          <ComponentToRender {...enhancedProps} />
        </CacheProvider>
      </React.StrictMode>
    );
  }
}
