import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { getDocument } from '@atlaskit/browser-apis';
import { EXPERIENCE_FAILURE_REASON } from './consts';
import { popupWithNestedElement } from './popupWithNestedElement';
const PORTAL_CONTAINER_SELECTOR = 'body > .atlaskit-portal-container';

/**
 * Popup check type determines how popups are observed based on their DOM location:
 * - 'inline': Popups appearing in toolbar button-groups (emoji, media, table selector, image)
 * - 'editorRoot': Popups attached to editor root (e.g., mention popups)
 * - 'editorContent': Content-level popups or modals in portal containers (e.g., block menu)
 * - 'portalRoot': Popups in body > .atlaskit-portal-container (e.g., flags, modals)
 */

export class ExperienceCheckPopupMutation {
  constructor(config) {
    _defineProperty(this, "observers", []);
    this.config = config;
  }

  /**
   * Returns the list of DOM elements to observe based on popup type.
   */
  getObserveTargets() {
    const {
      config
    } = this;
    switch (config.type) {
      case 'inline':
        return this.getInlineTargets(config);
      case 'editorRoot':
        return this.getEditorRootTargets(config);
      case 'editorContent':
        return this.getEditorContentTargets(config);
      case 'portalRoot':
        return this.getPortalRootTargets();
    }
  }

  /**
   * For 'portalRoot' type: observe .atlaskit-portal-container.
   * Popups like flags and modals render in body > .atlaskit-portal-container.
   */
  getPortalRootTargets() {
    var _getDocument;
    const portalContainer = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.querySelector(PORTAL_CONTAINER_SELECTOR);
    return portalContainer ? [portalContainer] : [];
  }

  /**
   * For 'editorContent' type: observe the target (mount point) and any existing
   * [data-editor-popup] wrappers within it. Content-level popups and modals
   * appear in portal containers.
   */
  getEditorContentTargets(config) {
    const target = config.getTarget();
    if (!target) {
      return [];
    }
    const targets = [target];
    const wrappers = target.querySelectorAll('[data-editor-popup]');
    for (const wrapper of wrappers) {
      targets.push(wrapper);
    }
    return targets;
  }

  /**
   * For 'inline' type: observe the target element directly.
   * The caller is responsible for resolving the correct container
   * (e.g. the toolbar button-group) via the getTarget function.
   */
  getInlineTargets(config) {
    const target = config.getTarget();
    if (!target) {
      return [];
    }
    return [target];
  }

  /**
   * For 'editorRoot' type: observe the actual editor root container.
   * The editorDom is the ProseMirror element, but popups appear as direct children
   * of the parent .akEditor container. So we observe the parent of editorDom.
   */
  getEditorRootTargets(config) {
    const targets = [];
    const editorDom = config.getEditorDom();
    if (editorDom) {
      const editorRoot = editorDom.closest('.akEditor') || editorDom.parentElement;
      if (editorRoot instanceof HTMLElement) {
        targets.push(editorRoot);
      }
    }
    return targets;
  }
  start(callback) {
    const doc = getDocument();
    const observeTargets = this.getObserveTargets();
    if (!doc || !observeTargets.length) {
      callback({
        status: 'failure',
        reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
      });
      return;
    }
    const query = this.config.nestedElementQuery;
    const subtree = this.config.type === 'inline' && this.config.subtree === true;
    const observe = el => {
      const observer = new MutationObserver(onMutation);
      observer.observe(el, {
        childList: true,
        subtree
      });
      this.observers.push(observer);
    };
    const onMutation = mutations => {
      const found = mutations.some(({
        type,
        addedNodes
      }) => type === 'childList' && [...addedNodes].some(node => node instanceof HTMLElement && (popupWithNestedElement(node, query) || node.matches(query) || node.querySelector(query) !== null)));
      if (found) {
        callback({
          status: 'success'
        });
        return;
      }
    };
    for (const observeTarget of observeTargets) {
      observe(observeTarget);
    }
  }
  stop() {
    for (const observer of this.observers) {
      observer.disconnect();
    }
    this.observers = [];
  }
}