"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
Object.defineProperty(exports, "LazyNodeView", {
  enumerable: true,
  get: function get() {
    return _nodeView.LazyNodeView;
  }
});
Object.defineProperty(exports, "convertToInlineCss", {
  enumerable: true,
  get: function get() {
    return _cssHelper.convertToInlineCss;
  }
});
exports.lazyNodeViewDecorationPluginKey = void 0;
exports.testOnlyIgnoreLazyNodeView = testOnlyIgnoreLazyNodeView;
exports.withLazyLoading = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _state = require("@atlaskit/editor-prosemirror/state");
var _lazyScheduler = require("./lazy-scheduler");
var _makeNodePlaceholderId = require("./makeNodePlaceholderId");
var _nodeView = require("./node-view");
var _cssHelper = require("./css-helper");
// Disable no-re-export rule for entry point files
/* eslint-disable @atlaskit/editor/no-re-export */

/**
 * 📢 Public Plugin Key
 *
 * Communication channel between LazyNodeView loader and LazyNodeViewDecorationPlugin.
 */
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
var lazyNodeViewDecorationPluginKey = exports.lazyNodeViewDecorationPluginKey = new _state.PluginKey('lazyNodeViewDecoration');

/**
 * 📢 Public Type
 *
 * @see {withLazyLoading}
 */

/**
 * 📢 Public Type
 *
 * @see {withLazyLoading}
 */

/**
 * 🧱 Internal: Editor FE Platform
 *
 * Caches loaded node view factory functions
 */

/**
 * 🧱 Internal: Editor FE Platform
 *
 * Controls which node was scheduled to load the original node view code
 */
var requestedNodesPerEditorView = new WeakMap();

/**
 * 🧱 Internal: Editor FE Platform
 *
 * Caches loaded node view factory functions for each editor view
 */
var resolvedNodesPerEditorView = new WeakMap();

/**
 * 🧱 Internal: Editor FE Platform
 *
 * Stores editorView -> raf to debounce NodeView updates.
 */
var debounceToEditorViewMap = new WeakMap();
var testOnlyIgnoreLazyNodeViewSet = new WeakSet();
// eslint-disable-next-line @repo/internal/deprecations/deprecation-ticket-required -- Ignored via go/ED-25883
/**
 * 🧱 Internal: Editor FE Platform
 *
 * Used in tests to prevent lazy node view being replaced by a real node view.
 *
 * This needs to be replaced with proper implementation once LazyNodeView is converted to a plugin.
 *
 * @deprecated DO NOT USE THIS OUTSIDE TESTS.
 */
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
function testOnlyIgnoreLazyNodeView(view) {
  testOnlyIgnoreLazyNodeViewSet.add(view);
}

/**
 * 📢 Public: Any EditorPlugin can use this function
 *
 * Wraps a NodeView constructor with laziness, allowing the NodeView to be loaded only when required.
 *
 * This higher-order function is designed to optimize the loading and rendering performance
 * of ProseMirror editor nodes by deferring the loading of their associated NodeViews until they are actually needed.
 * This is particularly useful for complex or heavy NodeViews, such as tables, table cells, rows, and headers within
 * the ProseMirror editor. By using dynamic imports (with promises), the initial load time of the editor can be significantly
 * reduced, leading to a smoother and faster user experience.
 *
 * The function accepts configuration parameters including the node name, a loader function that dynamically imports
 * the NodeView, and a function to retrieve NodeView options. It returns a NodeViewConstructor that ProseMirror
 * can use when rendering nodes of the specified type.
 *
 * @template NodeViewOptions - The type parameter that describes the shape of the options object for the NodeView.
 * @param {LazyLoadingProps<NodeViewOptions>} params - Configuration parameters for lazy loading.
 * @param {string} params.nodeName - The name of the node (e.g., 'table', 'tableCell', 'tableHeader', 'tableRow') for which the lazy-loaded NodeView is intended.
 * @param {() => Promise<CreateReactNodeViewProps<NodeViewOptions>>} params.loader - A function that, when called, returns a promise that resolves to the actual NodeView constructor. This function typically uses dynamic `import()` to load the NodeView code.
 * @param {() => NodeViewOptions} params.getNodeViewOptions - A function that returns the options to be passed to the NodeView constructor. These options can include dependencies like `portalProviderAPI`, `eventDispatcher`, and others, which are necessary for the NodeView's operation.
 * @param {DispatchAnalyticsEvent} [params.dispatchAnalyticsEvent] - An optional function for dispatching analytics events, which can be used to monitor the performance and usage of the lazy-loaded NodeViews.
 * @returns {NodeViewConstructor} A constructor function for creating a NodeView that ProseMirror can instantiate when it encounters a node of the specified type. This constructor is a lightweight placeholder until the actual NodeView is loaded.
 *
 * @example
 * // Lazy load a table NodeView with specific options
 * const lazyTableView = withLazyLoading({
 *   nodeName: 'table',
 *   loader: () => import('./table').then(module => module.createTableView),
 *   getNodeViewOptions: () => ({
 *     portalProviderAPI,
 *     eventDispatcher,
 *     getEditorContainerWidth,
 *     getEditorFeatureFlags,
 *     dispatchAnalyticsEvent,
 *     pluginInjectionApi,
 *   }),
 * });
 *
 * // Then, use `lazyTableView` in ProseMirror editor setup to enhance 'table' nodes with lazy loading
 */

// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
var withLazyLoading = exports.withLazyLoading = function withLazyLoading(_ref) {
  var nodeName = _ref.nodeName,
    loader = _ref.loader,
    getNodeViewOptions = _ref.getNodeViewOptions;
  return function (node, view, getPos, decorations, innerDecorations) {
    var _node$type;
    var requestedNodes = requestedNodesPerEditorView.get(view);
    if (!requestedNodes) {
      requestedNodes = new Map();
      requestedNodesPerEditorView.set(view, requestedNodes);
    }
    var resolvedNodeViews = resolvedNodesPerEditorView.get(view);
    if (!resolvedNodeViews) {
      resolvedNodesPerEditorView.set(view, new Map());
    }
    var wasAlreadyRequested = requestedNodes.has(nodeName);
    if (wasAlreadyRequested) {
      var resolvedNodeView = resolvedNodeViews === null || resolvedNodeViews === void 0 ? void 0 : resolvedNodeViews.get(nodeName);
      if (resolvedNodeView && !testOnlyIgnoreLazyNodeViewSet.has(view)) {
        return resolvedNodeView(node, view, getPos, decorations, innerDecorations);
      }
      return new _nodeView.LazyNodeView(node, view, getPos, decorations);
    }
    var loaderPromise = loader().then(function (nodeViewFuncModule) {
      var _resolvedNodesPerEdit;
      var nodeViewFunc = function nodeViewFunc(node, view, getPos, decorations, innerDecorations) {
        var nodeView = nodeViewFuncModule(node, view, getPos, decorations, getNodeViewOptions, innerDecorations);
        // eslint-disable-next-line @atlaskit/editor/no-as-casting
        var dom = nodeView.dom;
        dom.setAttribute('data-vc', "editor-lnv-loaded--".concat(node.type.name));
        var pos = getPos();
        if (pos !== undefined) {
          dom.setAttribute('data-editor-lnv-placeholder-replace', (0, _makeNodePlaceholderId.makeNodePlaceholderId)(node.type.name, pos));
        }
        return nodeView;
      };
      (_resolvedNodesPerEdit = resolvedNodesPerEditorView.get(view)) === null || _resolvedNodesPerEdit === void 0 || _resolvedNodesPerEdit.set(nodeName, nodeViewFunc);

      /**
       * Triggering lazyNodeViewDecoration plugin to apply decorations
       * to nodes with newly loaded NodeViews.
       */
      var _ref2 = debounceToEditorViewMap.get(view) || [null, new Set()],
        _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
        callbackId = _ref3[0],
        nodeTypes = _ref3[1];
      if (callbackId) {
        (0, _lazyScheduler.cancelCallback)(callbackId);
      }
      nodeTypes.add(node.type.name);
      var nextCallbackId = (0, _lazyScheduler.scheduleCallback)(function () {
        debounceToEditorViewMap.set(view, [null, new Set()]);
        var tr = view.state.tr;
        tr.setMeta(lazyNodeViewDecorationPluginKey, {
          type: 'add',
          nodeTypes: nodeTypes
        });
        view.dispatch(tr);
      });
      debounceToEditorViewMap.set(view, [nextCallbackId, nodeTypes]);
      /**
       * END triggering LazyNodeViewDecoration plugin
       */

      return nodeViewFunc;
    });
    requestedNodes.set(nodeName, loaderPromise);
    if (typeof ((_node$type = node.type) === null || _node$type === void 0 || (_node$type = _node$type.spec) === null || _node$type === void 0 ? void 0 : _node$type.toDOM) !== 'function') {
      // TODO: ED-23982 - Analytics
      // dispatchAnalyticsEvent({
      //   action: ACTION.LAZY_NODE_VIEW_ERROR,
      //   actionSubject: ACTION_SUBJECT.LAZY_NODE_VIEW,
      //   eventType: EVENT_TYPE.OPERATIONAL,
      //   attributes: {
      //     nodeName,
      //     error: 'No spec found',
      //   },
      // });
    }
    return new _nodeView.LazyNodeView(node, view, getPos, decorations);
  };
};