import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React, { Component } from 'react';
import memoizeOne from 'memoize-one';
import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
import { getExtensionModuleNodePrivateProps, getNodeRenderer } from '../extensions';
import { getExtensionRenderer } from '../utils';
import Extension from './Extension/Extension';
import InlineExtension from './Extension/InlineExtension';
import MultiBodiedExtension from './MultiBodiedExtension';
export class ExtensionComponent extends Component {
  constructor(...args) {
    super(...args);
    _defineProperty(this, "privatePropsParsed", false);
    _defineProperty(this, "state", {});
    _defineProperty(this, "mounted", false);
    // memoized to avoid rerender on extension state changes
    _defineProperty(this, "getNodeRenderer", memoizeOne(getNodeRenderer));
    _defineProperty(this, "getExtensionModuleNodePrivateProps", memoizeOne(getExtensionModuleNodePrivateProps));
    _defineProperty(this, "setIsNodeHovered", isHovered => {
      this.setState({
        isNodeHovered: isHovered
      });
    });
    _defineProperty(this, "setStateFromPromise", (stateKey, promise) => {
      promise && promise.then(p => {
        if (!this.mounted) {
          return;
        }
        this.setState({
          [stateKey]: p
        });
      });
    });
    /**
     * Parses any private nodes once an extension provider is available.
     *
     * We do this separately from resolving a node renderer component since the
     * private props come from extension provider, rather than an extension
     * handler which only handles `render`/component concerns.
     */
    _defineProperty(this, "parsePrivateNodePropsIfNeeded", async () => {
      if (this.privatePropsParsed || !this.state.extensionProvider) {
        return;
      }
      this.privatePropsParsed = true;
      const {
        extensionType,
        extensionKey
      } = this.props.node.attrs;

      /**
       * getExtensionModuleNodePrivateProps can throw if there are issues in the
       * manifest
       */
      try {
        const privateProps = await this.getExtensionModuleNodePrivateProps(this.state.extensionProvider, extensionType, extensionKey);
        this.setState({
          _privateProps: privateProps
        });
      } catch (e) {
        // eslint-disable-next-line no-console
        console.error('Provided extension handler has thrown an error\n', e);
        /** We don't want this error to block renderer */
        /** We keep rendering the default content */
      }
    });
    _defineProperty(this, "handleExtension", (pmNode, actions) => {
      var _pmNode$marks, _pmNode$marks$find, _pmNode$marks$find$at;
      const {
        extensionHandlers,
        editorView
      } = this.props;
      const {
        extensionType,
        extensionKey,
        parameters,
        text
      } = pmNode.attrs;
      const isBodiedExtension = pmNode.type.name === 'bodiedExtension';
      if (isBodiedExtension) {
        return;
      }
      const fragmentLocalId = pmNode === null || pmNode === void 0 ? void 0 : (_pmNode$marks = pmNode.marks) === null || _pmNode$marks === void 0 ? void 0 : (_pmNode$marks$find = _pmNode$marks.find(m => m.type.name === 'fragment')) === null || _pmNode$marks$find === void 0 ? void 0 : (_pmNode$marks$find$at = _pmNode$marks$find.attrs) === null || _pmNode$marks$find$at === void 0 ? void 0 : _pmNode$marks$find$at.localId;
      const node = {
        type: pmNode.type.name,
        extensionType,
        extensionKey,
        parameters,
        content: text,
        localId: pmNode.attrs.localId,
        fragmentLocalId
      };
      let result;
      if (extensionHandlers && extensionHandlers[extensionType]) {
        const render = getExtensionRenderer(extensionHandlers[extensionType]);
        result = render(node, editorView.state.doc, actions);
      }
      if (!result) {
        const extensionHandlerFromProvider = this.state.extensionProvider && this.getNodeRenderer(this.state.extensionProvider, extensionType, extensionKey);
        if (extensionHandlerFromProvider) {
          const NodeRenderer = extensionHandlerFromProvider;
          if (node.type === 'multiBodiedExtension') {
            return /*#__PURE__*/React.createElement(NodeRenderer, {
              node: node,
              references: this.props.references,
              actions: actions
            });
          } else {
            return /*#__PURE__*/React.createElement(NodeRenderer, {
              node: node,
              references: this.props.references
            });
          }
        }
      }
      return result;
    });
  }
  UNSAFE_componentWillMount() {
    this.mounted = true;
  }
  componentDidMount() {
    const {
      extensionProvider
    } = this.props;
    if (extensionProvider) {
      this.setStateFromPromise('extensionProvider', extensionProvider);
    }
  }
  componentDidUpdate() {
    this.parsePrivateNodePropsIfNeeded();
  }
  componentWillUnmount() {
    this.mounted = false;
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    const {
      extensionProvider
    } = nextProps;
    if (extensionProvider && this.props.extensionProvider !== extensionProvider) {
      this.setStateFromPromise('extensionProvider', extensionProvider);
    }
  }
  render() {
    var _this$state$_privateP;
    const {
      node,
      handleContentDOMRef,
      editorView,
      references,
      editorAppearance,
      pluginInjectionApi,
      getPos,
      eventDispatcher,
      showMacroInteractionDesignUpdates
    } = this.props;
    const {
      selection
    } = editorView.state;
    const selectedNode = selection instanceof NodeSelection && selection.node;
    const position = typeof getPos === 'function' && getPos();
    const resolvedPosition = position && editorView.state.doc.resolve(position);
    const isNodeNested = !!(resolvedPosition && resolvedPosition.depth > 0);
    if (node.type.name === 'multiBodiedExtension') {
      return /*#__PURE__*/React.createElement(MultiBodiedExtension, {
        node: node,
        editorView: editorView,
        getPos: getPos,
        handleContentDOMRef: handleContentDOMRef,
        tryExtensionHandler: this.tryExtensionHandler.bind(this),
        eventDispatcher: eventDispatcher,
        pluginInjectionApi: pluginInjectionApi,
        editorAppearance: editorAppearance,
        showMacroInteractionDesignUpdates: showMacroInteractionDesignUpdates,
        isNodeSelected: selectedNode === node,
        isNodeNested: isNodeNested,
        isNodeHovered: this.state.isNodeHovered,
        setIsNodeHovered: this.setIsNodeHovered
      });
    }
    const extensionHandlerResult = this.tryExtensionHandler(undefined);
    switch (node.type.name) {
      case 'extension':
      case 'bodiedExtension':
        return /*#__PURE__*/React.createElement(Extension, {
          node: node,
          getPos: this.props.getPos,
          references: references,
          extensionProvider: this.state.extensionProvider,
          handleContentDOMRef: handleContentDOMRef,
          view: editorView,
          editorAppearance: editorAppearance,
          hideFrame: (_this$state$_privateP = this.state._privateProps) === null || _this$state$_privateP === void 0 ? void 0 : _this$state$_privateP.__hideFrame,
          pluginInjectionApi: pluginInjectionApi,
          showMacroInteractionDesignUpdates: showMacroInteractionDesignUpdates,
          isNodeSelected: selectedNode === node,
          isNodeHovered: this.state.isNodeHovered,
          isNodeNested: isNodeNested,
          setIsNodeHovered: this.setIsNodeHovered
        }, extensionHandlerResult);
      case 'inlineExtension':
        return /*#__PURE__*/React.createElement(InlineExtension, {
          node: node,
          showMacroInteractionDesignUpdates: showMacroInteractionDesignUpdates,
          isNodeSelected: selectedNode === node,
          pluginInjectionApi: pluginInjectionApi,
          isNodeHovered: this.state.isNodeHovered,
          setIsNodeHovered: this.setIsNodeHovered
        }, extensionHandlerResult);
      default:
        return null;
    }
  }
  tryExtensionHandler(actions) {
    const {
      node
    } = this.props;
    try {
      const extensionContent = this.handleExtension(node, actions);
      if (extensionContent && /*#__PURE__*/React.isValidElement(extensionContent)) {
        return extensionContent;
      }
    } catch (e) {
      // eslint-disable-next-line no-console
      console.error('Provided extension handler has thrown an error\n', e);
      /** We don't want this error to block renderer */
      /** We keep rendering the default content */
    }
    return null;
  }
}