import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _get from "@babel/runtime/helpers/get";
import _inherits from "@babel/runtime/helpers/inherits";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _superPropGet(t, o, e, r) { var p = _get(_getPrototypeOf(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; }
import React from 'react';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { isSSR } from '../core-utils';
import ReactNodeView from '../react-node-view';
import { Extension } from './Extension';
import { ExtensionNodeWrapper } from './ExtensionNodeWrapper';
// getInlineNodeViewProducer is a new api to use instead of ReactNodeView
// when creating inline node views, however, it is difficult to test the impact
// on selections when migrating inlineExtension to use the new api.
// The ReactNodeView api will be visited in the second phase of the selections
// project whilst investigating block nodes. We will revisit the Extension node view there too.
export var ExtensionNode = /*#__PURE__*/function (_ReactNodeView) {
  function ExtensionNode() {
    var _this;
    _classCallCheck(this, ExtensionNode);
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }
    _this = _callSuper(this, ExtensionNode, [].concat(args));
    /**
     * Track whether we found and are reusing SSR'd DOM.
     * When true, we skip React Portal rendering on first init to preserve SSR content.
     */
    _defineProperty(_this, "didReuseSsrDom", false);
    /**
     * Track whether this is the first init call.
     * SSR content preservation only happens on the very first init.
     */
    _defineProperty(_this, "isFirstInit", true);
    return _this;
  }
  _inherits(ExtensionNode, _ReactNodeView);
  return _createClass(ExtensionNode, [{
    key: "ignoreMutation",
    value: function ignoreMutation(mutation) {
      // Extensions can perform async operations that will change the DOM.
      // To avoid having their tree rebuilt, we need to ignore the mutation
      // for atom based extensions if its not a layout, we need to give
      // children a chance to recalc
      return this.node.type.isAtom || mutation.type !== 'selection' && mutation.attributeName !== 'data-layout';
    }

    // Reserve height by setting a minimum height for the extension node view element
  }, {
    key: "createDomRef",
    value: function createDomRef() {
      if (!fg('confluence_connect_macro_preset_height')) {
        // Try to reuse SSR'd DOM node on first init only
        // This preserves SSR content and avoids TTVC mutations during hydration
        if (!isSSR() && this.isFirstInit && this.node.type.name === 'extension' && this.node.attrs.extensionKey === 'toc' && expValEquals('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
          var ssrElement = this.findSSRElement();
          if (ssrElement) {
            this.didReuseSsrDom = true;
            return ssrElement;
          }
        }
        return _superPropGet(ExtensionNode, "createDomRef", this, 3)([]);
      }
      if (!this.node.isInline) {
        var _this$reactComponentP, _this$reactComponentP2;
        var _htmlElement = document.createElement('div');
        var extensionHeight = (_this$reactComponentP = this.reactComponentProps) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.extensionNodeViewOptions) === null || _this$reactComponentP === void 0 || (_this$reactComponentP2 = _this$reactComponentP.getExtensionHeight) === null || _this$reactComponentP2 === void 0 ? void 0 : _this$reactComponentP2.call(_this$reactComponentP, this.node);
        if (extensionHeight) {
          _htmlElement.style.setProperty('min-height', "".concat(extensionHeight, "px"));
        }
        return _htmlElement;
      }
      var htmlElement = document.createElement('span');
      return htmlElement;
    }

    /**
     * Cache for SSR element lookup to avoid repeated DOM queries.
     * undefined = not yet searched, null = searched but not found, HTMLElement = found
     */
  }, {
    key: "findSSRElement",
    value:
    /**
     * Attempts to find an existing SSR'd DOM element for this extension node by extensionKey and localId
     * which should uniquely identify the
     * extension node within the editor content.
     *
     * @returns The SSR'd element if found, otherwise null
     */
    function findSSRElement() {
      if (this.cachedSsrElement !== undefined) {
        return this.cachedSsrElement || null;
      }
      var extensionKey = this.node.attrs.extensionKey;
      var localId = this.node.attrs.localId;
      var editorDom = this.view.dom;
      if (extensionKey && localId) {
        var selector = "[extensionkey=\"".concat(extensionKey, "\"][localid=\"").concat(localId, "\"]");
        var element = editorDom.querySelector(selector);
        if (element && element instanceof HTMLElement) {
          this.cachedSsrElement = element;
          return element;
        }
      }
      this.cachedSsrElement = null;
      return null;
    }

    /**
     * Override init() to skip React Portal rendering on first init if we're reusing SSR'd DOM.
     * This preserves the SSR content without React unnecessarily re-rendering it.
     */
  }, {
    key: "init",
    value: function init() {
      if (!expValEquals('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
        _superPropGet(ExtensionNode, "init", this, 3)([]);
      } else {
        if (!isSSR() && this.node.type.name === 'extension' && this.node.attrs.extensionKey === 'toc') {
          var ssrElement = this.findSSRElement();
          var shouldSkipInitRender = ssrElement !== null;
          _superPropGet(ExtensionNode, "init", this, 3)([shouldSkipInitRender]);
          if (shouldSkipInitRender) {
            this.isFirstInit = false;
          }
        } else {
          _superPropGet(ExtensionNode, "init", this, 3)([]);
        }
      }
      return this;
    }
  }, {
    key: "update",
    value: function update(node, decorations, _innerDecorations) {
      var validUpdate = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () {
        return true;
      };
      // Remove extensionNodeWrapper aka span.relative if we previously reused SSR DOM
      // control is back to React afterwards
      if (this.didReuseSsrDom && expValEquals('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
        var ssrElement = this.findSSRElement();
        if (ssrElement) {
          var extensionNodeWrapper = ssrElement.querySelector('[data-testid="extension-node-wrapper"]');
          if (extensionNodeWrapper) {
            extensionNodeWrapper.remove();
          }
          this.didReuseSsrDom = false;
        }
      }
      return _superPropGet(ExtensionNode, "update", this, 3)([node, decorations, _innerDecorations, validUpdate]);
    }

    /**
     * When interacting with input elements inside an extension's body, the events
     * bubble up to the editor and get handled by it. This almost always gets in the way
     * of being able to actually interact with said input in the extension, i.e.
     * typing inside a text field (in an extension body) will print the text in the editor
     * content area instead. This change prevents the editor view from trying to handle these events,
     * when the target of the event is an input element, so the extension can.
     */
  }, {
    key: "stopEvent",
    value: function stopEvent(event) {
      if (fg('forge-ui-extensionnodeview-stop-event-for-textarea')) {
        return event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement;
      }
      return event.target instanceof HTMLInputElement;
    }
  }, {
    key: "getContentDOM",
    value: function getContentDOM() {
      var _this$reactComponentP3;
      if (this.node.isInline) {
        return;
      }
      if (this.didReuseSsrDom) {
        return;
      }
      var contentDomWrapper = document.createElement('div');
      contentDomWrapper.className = "".concat(this.node.type.name, "-content-dom-wrapper");
      var isBodiedExtension = this.node.type.name === 'bodiedExtension';
      var showMacroInteractionDesignUpdates = (_this$reactComponentP3 = this.reactComponentProps) === null || _this$reactComponentP3 === void 0 || (_this$reactComponentP3 = _this$reactComponentP3.macroInteractionDesignFeatureFlags) === null || _this$reactComponentP3 === void 0 ? void 0 : _this$reactComponentP3.showMacroInteractionDesignUpdates;
      if (isBodiedExtension && showMacroInteractionDesignUpdates && expValEquals('platform_editor_bodiedextension_layoutshift_fix', 'isEnabled', true)) {
        // Create outer wrapper to hold space for the lozenge
        var outerWrapper = document.createElement('div');
        outerWrapper.className = "".concat(this.node.type.name, "-content-outer-wrapper");

        // Create inner wrapper to position inner content
        var innerWrapper = document.createElement('div');
        innerWrapper.className = "".concat(this.node.type.name, "-content-inner-wrapper");
        innerWrapper.appendChild(contentDomWrapper);
        outerWrapper.appendChild(innerWrapper);
        return {
          dom: outerWrapper,
          contentDOM: contentDomWrapper
        };
      }
      return {
        dom: contentDomWrapper
      };
    }
  }, {
    key: "render",
    value: function render(props, forwardRef) {
      var _props$extensionNodeV;
      // If we reused SSR'd DOM on first init, don't render React Portal
      // The SSR content is already perfect and doesn't need re-rendering
      if (this.didReuseSsrDom && this.isFirstInit) {
        return null;
      }
      return /*#__PURE__*/React.createElement(ExtensionNodeWrapper, {
        nodeType: this.node.type.name,
        macroInteractionDesignFeatureFlags: props.macroInteractionDesignFeatureFlags
      }, /*#__PURE__*/React.createElement(Extension, {
        editorView: this.view,
        node: this.node,
        eventDispatcher: this.eventDispatcher
        // The getPos arg is always a function when used with nodes
        // the version of the types we use has a union with the type
        // for marks.
        // This has been fixed in later versions of the definitly typed
        // types (and also in prosmirror-views inbuilt types).
        // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/57384
        ,
        getPos: this.getPos,
        providerFactory: props.providerFactory,
        handleContentDOMRef: forwardRef,
        extensionHandlers: props.extensionHandlers,
        editorAppearance: (_props$extensionNodeV = props.extensionNodeViewOptions) === null || _props$extensionNodeV === void 0 ? void 0 : _props$extensionNodeV.appearance,
        pluginInjectionApi: props.pluginInjectionApi,
        macroInteractionDesignFeatureFlags: props.macroInteractionDesignFeatureFlags,
        showLivePagesBodiedMacrosRendererView: props.showLivePagesBodiedMacrosRendererView,
        showUpdatedLivePages1PBodiedExtensionUI: props.showUpdatedLivePages1PBodiedExtensionUI,
        rendererExtensionHandlers: props.rendererExtensionHandlers
      }));
    }
  }]);
}(ReactNodeView);
export default function ExtensionNodeView(portalProviderAPI, eventDispatcher, providerFactory, extensionHandlers, extensionNodeViewOptions, pluginInjectionApi, macroInteractionDesignFeatureFlags, showLivePagesBodiedMacrosRendererView, showUpdatedLivePages1PBodiedExtensionUI, rendererExtensionHandlers) {
  return function (node, view, getPos) {
    return new ExtensionNode(node, view, getPos, portalProviderAPI, eventDispatcher, {
      providerFactory: providerFactory,
      extensionHandlers: extensionHandlers,
      extensionNodeViewOptions: extensionNodeViewOptions,
      pluginInjectionApi: pluginInjectionApi,
      macroInteractionDesignFeatureFlags: macroInteractionDesignFeatureFlags,
      showLivePagesBodiedMacrosRendererView: showLivePagesBodiedMacrosRendererView,
      showUpdatedLivePages1PBodiedExtensionUI: showUpdatedLivePages1PBodiedExtensionUI,
      rendererExtensionHandlers: rendererExtensionHandlers
    }).init();
  };
}