(function() {
  const { mergeObjects, getCurrentBreakpoint } = BreakdanceFrontend.utils;
  window.breakdancePopupInstances = {};
  window.breakdanceHasShownPopup = false;

  class BreakdancePopup {
    defaultOptions = {
      keepOpenOnHashlinkClicks: false,
      closeOnClickOutside: true,
      closeOnEscapeKey: true,
      closeAfterMilliseconds: null,
      showCloseButtonAfterMilliseconds: null,
      disableScrollWhenOpen: false,
      avoidMultiple: false,
      entranceAnimation: null,
      exitAnimation: null,
      limitPageLoad: null,
      limitSession: null,
      limitForever: null,
      breakpointConditions: [],
      triggers: [],
    };

    showCount = 0;
    idleTimeMilliseconds = 0;
    entranceAnimation = false;
    exitAnimation = false;
    lastScrollPosition = window.scrollY;

    boundEvents = {};

    openClass = "breakdance-popup-open";
    animatingClass = "breakdance-popup-animating";

    constructor(id, options) {
      if (window.breakdancePopupInstances[id]) {
        return;
      }
      this.id = id;
      this.options = mergeObjects(this.defaultOptions, options);
      this.triggers = this.options.triggers;
      this.storageKey = `breakdance_popup_${id}_count`;
      window.breakdancePopupInstances[id] = this;

      this.triggers.forEach((trigger) => {
        this.initTrigger(trigger.slug, trigger.options);
      });
      this.init();
    }

    init() {
      this.element = document.querySelector(
        `[data-breakdance-popup-id="${this.id}"]`
      );
      this.wrapper = this.element.closest(".bde-popup");

      if (!this.element) {
        return false;
      }

      if (!this.wrapper) {
        return false;
      }

      this.initCloseButton();

      this.wrapper.addEventListener("breakdance_popup_open", () => {
      });

      this.wrapper.addEventListener("breakdance_popup_close", () => {
        this.handleClose();
      });

      return true;
    }

    setOptions(options) {
      this.options = { ...this.options, ...options };
    }

    open(forceOpen = false) {
      const event = new Event("breakdance_play_animations", { bubbles: true });
      this.element.dispatchEvent(event);

      return new Promise((resolve, reject) => {
        if (this.shouldHideAtBreakpoint()) {
          return resolve();
        }

        if (this.isOpen() || this.isAnimating()) {
          return resolve();
        }

        if (!forceOpen && this.hasReachedLimit()) {
          return resolve();
        }

        if (this.element.dataset?.breakdancePopupUsingProTriggers) {
          alert(
            `This popup is using a Pro-only trigger. The popup would show up now instead of this alert. Get Breakdance Pro to get access to all popup triggers.`
          );

          return resolve();
        }

        this.incrementShowCounts();

        this.registerCloseListeners();

        if (this.options.entranceAnimation) {
          this.playEntranceAnimation();
        } else {
          this.wrapper.classList.add(this.openClass);
        }

        this.wrapper.dispatchEvent(new CustomEvent("breakdance_popup_open"));
        return resolve();

      });
    }

    registerCloseListeners() {
      this.boundCloseIfEscapeOrClickOutside = this.closeIfEscapeOrClickOutside.bind(
        this
      );

      if (this.options.closeOnEscapeKey) {
        document.addEventListener(
          "keydown",
          this.boundCloseIfEscapeOrClickOutside
        );
      }

      this.boundMaybeAutomaticallyClosePopup = this.maybeAutomaticallyClosePopup.bind(this);
      this.element.addEventListener("click", this.boundMaybeAutomaticallyClosePopup);

      if (this.options.closeOnClickOutside) {
        document.addEventListener(
          "mousedown",
          this.boundCloseIfEscapeOrClickOutside
        );
      }

      if (this.options.closeAfterMilliseconds) {
        setTimeout(() => {
          if (this.isOpen()) {
            this.close();
          }
        }, this.options.closeAfterMilliseconds);
      }

      if (this.options.disableScrollWhenOpen) {
        document.querySelector("html").classList.add("breakdance-noscroll");
      }
    }

    removeCloseListeners() {
      if (this.boundCloseIfEscapeOrClickOutside) {
        document.removeEventListener(
          "keydown",
          this.boundCloseIfEscapeOrClickOutside
        );
        document.removeEventListener(
          "mousedown",
          this.boundCloseIfEscapeOrClickOutside
        );
        this.element.removeEventListener(
          "click",
          this.boundMaybeAutomaticallyClosePopup
        );
      }
    }

    hasReachedLimit() {
      if (
        this.options.avoidMultiple &&
        window.breakdanceHasShownPopup === true
      ) {
        return true;
      }

      if (
        this.options.limitPageLoad &&
        this.getShowCount("page_load") >= this.options.limitPageLoad
      ) {
        return true;
      }

      if (
        this.options.limitSession &&
        this.getShowCount("session") >= this.options.limitSession
      ) {
        return true;
      }

      if (
        this.options.limitForever &&
        this.getShowCount("local") >= this.options.limitForever
      ) {
        return true;
      }

      return false;
    }

    isOpen() {
      return this.wrapper && this.wrapper.classList.contains(this.openClass);
    }

    isAnimating() {
      return this.wrapper && this.wrapper.classList.contains(this.animatingClass);
    }

    closeIfEscapeOrClickOutside(event) {
      const shouldClose =
        (event.keyCode === 27 && this.isPopupOnTop()) ||
        event.target === this.wrapper;

      if (shouldClose) {
        event.preventDefault();
        event.stopPropagation();
        this.close();
      }
    }

    isPopupOnTop() {
      const topMostElement = document.elementFromPoint(
        this.wrapper.offsetLeft,
        this.wrapper.offsetTop
      );
      return topMostElement === this.wrapper;
    }

    maybeAutomaticallyClosePopup(event) {

      if (this.options.keepOpenOnHashlinkClicks) {
        return;
      }

      /*
      if a disable-popup-autoclose attribute is present on a parent 
      of the event target, don't automatically close the popup
      this feature is experimental and may not be available in future versions of Breakdance
      */
      const elementWitDisablePopupAutocloseAttribute = event.target.closest("[disable-popup-autoclose]");

      if (elementWitDisablePopupAutocloseAttribute) {
        return;
      }

      /*
      autoclose the popup when:
       - clicking on a link with an attribute of close-popup-on-click (this feature is experimental and may not be available in future versions of Breakdance)
       - clicking on a link with a href of #
       - clicking a #hashlink that leads somewhere on the page
      */

      if (event.target.closest('[close-popup-on-click]')) {
        this.close();
        return;
      }

      const link = event.target.closest("a");
      if (!link) return;

      if (link.getAttribute('href') === '#') {
        this.close();
        return;
      }
      
      const url = new URL(link);
      if (!url.hash) return;

      const hashAsIdOrName = url.hash.substring(1);

      if (hashAsIdOrName && document.querySelector(`#${hashAsIdOrName}, a[name=${hashAsIdOrName}]`)) {
        this.close();
        return;
      }

    }

    close() {
      return new Promise((resolve, reject) => {
        if (!this.isOpen() || this.isAnimating()) {
          return resolve();
        }

        this.removeCloseListeners();

        if (this.options.disableScrollWhenOpen) {
          document
            .querySelector("html")
            .classList.remove("breakdance-noscroll");
        }

        if (this.options.exitAnimation) {
          this.playExitAnimation();
        } else {
          this.wrapper.classList.remove(this.openClass);
        }

        this.wrapper.dispatchEvent(new CustomEvent("breakdance_popup_close"));
        return resolve();

      });
    }

    toggle() {
      if (this.isOpen()) {
        return this.close();
      }
      return this.open();
    }

    showPopupOnScrollPercentage(options, callback) {
      return event => {
        const targetPercent = options.percent / 100;
        const scrollTop = window.scrollY;
        const documentHeight = document.body.offsetHeight;
        const windowHeight = window.innerHeight;
        const scrollPercent = scrollTop / (documentHeight - windowHeight);
        if (scrollPercent > targetPercent) {
          this.open().then(callback);
        }
      };
    }

    showPopupOnScrollToSelector(options) {
      const element = document.querySelector(options.selector);
      if (!element) {
        return;
      }
      const ob = new IntersectionObserver(
        (entries, observer) => {
          entries.forEach(entry => {
            if (entry.isIntersecting && !this.isOpen()) {
              this.open().then(() => {
                observer.unobserve(entry.target);
              });
            }
          });
        },
        {
          root: null,
          rootMargin: "0px",
          threshold: 0.2
        }
      );
      ob.observe(element);
    }

    showPopupOnScrollUp(callback) {
      return event => {
        const scrollTop = window.scrollY;
        if (scrollTop < this.lastScrollPosition) {
          this.open().then(callback);
        }
        this.lastScrollPosition = scrollTop;
      };
    }

    showPopupAfterInactivityDelay(delayInMilliseconds) {
      const idleTimeout = 1000;
      const idleInterval = setInterval(() => {
        if (this.idleTimeMilliseconds >= delayInMilliseconds) {
          this.open();
          clearInterval(idleInterval);
          if (boundResetIdleTime) {
            document.removeEventListener("mousemove", boundResetIdleTime);
            document.removeEventListener("keydown", boundResetIdleTime);
          }
        } else {
          this.idleTimeMilliseconds += idleTimeout;
        }
      }, idleTimeout);
      const boundResetIdleTime = this.resetIdleTime.bind(this);
      document.addEventListener("mousemove", boundResetIdleTime);
      document.addEventListener("keydown", boundResetIdleTime);
    }

    resetIdleTime() {
      this.idleTimeMilliseconds = 0;
    }

    showPopupOnExitIntent(callback) {
      return event => {
        const isExitIntent =
          !event.toElement && !event.relatedTarget && event.clientY < 10;
        if (isExitIntent) {
          this.open().then(callback);
        }
      };
    }

    showPopupOnClickEvent(options, callback) {
      return event => {
        if (options.clickType === "anywhere") {
          return this.open().then(callback);
        }
        if (options.selector !== null) {
          if (event.target.closest(options.selector)) {
            event.preventDefault();
            event.stopPropagation();
            return this.open().then(callback);
          }
        }
      };
    }

    showPopupOnLoad(delayInMilliseconds) {
      setTimeout(() => {
        this.open();
      }, delayInMilliseconds);
    }

    async playEntranceAnimation() {

      if (this.entranceAnimation) {
        this.entranceAnimation.destroy();
      }

      if (this.options.entranceAnimation !== null) {
        this.entranceAnimation = new BreakdancePopupAnimation(
          this.element,
          this.wrapper,
          this.options.entranceAnimation
        );
      }

      await this.entranceAnimation.play();

    }

    async playExitAnimation(pauseTweenAtEnd = false) {

      if (this.exitAnimation) {
        this.exitAnimation.destroy();
      }

      if (this.options.exitAnimation !== null) {
        this.exitAnimation = new BreakdancePopupAnimation(
          this.element,
          this.wrapper,
          this.options.exitAnimation,
          true
        );
      }

      await this.exitAnimation.play(true);

      if (pauseTweenAtEnd) {
        this.exitAnimation.tween.pause(0);
      }
      
      this.wrapper.classList.remove(this.openClass);
      this.exitAnimation.destroy();

    }

    initTrigger(slug, options = {}) {
      if (slug === "load") {
        const delay = options.delay ? options.delay * 1000 : 0;
        this.showPopupOnLoad(delay);
      }

      if (slug === "inactivity") {
        const delay = options.delay ? options.delay * 1000 : 0;
        this.showPopupAfterInactivityDelay(delay);
      }

      if (slug === "scroll" && options.scrollType === "selector") {
        this.showPopupOnScrollToSelector(options);
      }

      if (slug === "scroll" && options.scrollType === "percent") {
        const scrollEventListener = this.showPopupOnScrollPercentage(
          options,
          () => {
            window.removeEventListener("scroll", scrollEventListener);
          }
        );
        window.addEventListener("scroll", scrollEventListener);
      }

      if (slug === "scroll_up") {
        const scrollUpEventListener = this.showPopupOnScrollUp(() => {
          window.removeEventListener("scroll", scrollUpEventListener);
        });
        window.addEventListener("scroll", scrollUpEventListener);
      }

      if (slug === "exit_intent") {
        const exitIntentEventListener = this.showPopupOnExitIntent(() => {
          document.removeEventListener("mouseout", exitIntentEventListener);
        });
        document.addEventListener("mouseout", exitIntentEventListener);
      }

      if (slug === "click") {
        const boundShowPopupOnClickEvent = this.showPopupOnClickEvent(
          options,
          () => {
            if (options.clickType === "anywhere") {
              document.removeEventListener("click", boundShowPopupOnClickEvent);
            }
          }
        );
        document.addEventListener("click", boundShowPopupOnClickEvent);
      }
    }

    initCloseButton() {
      if (this.options.showCloseButtonAfterMilliseconds) {
        setTimeout(() => {
          if (!this.isOpen()) {
            return;
          }
          const closeButton = this.element.querySelector(
            ".breakdance-popup-close-button"
          );
          if (closeButton) {
            closeButton.classList.remove("hidden");
          }
        }, this.options.showCloseButtonAfterMilliseconds);
      }
    }

    incrementShowCounts() {
      window.breakdanceHasShownPopup = true;

      const sessionStorageValue = this.getShowCount("session");
      sessionStorage.setItem(
        this.storageKey,
        (sessionStorageValue + 1).toString()
      );

      const localStorageValue = this.getShowCount("local");
      localStorage.setItem(this.storageKey, (localStorageValue + 1).toString());

      this.showCount += 1;
    }

    getShowCount(type) {
      let showCount = 0;

      if (type === "page_load") {
        showCount = this.showCount;
      }

      if (type === "session") {
        const sessionStorageItem = sessionStorage.getItem(this.storageKey);
        showCount = sessionStorageItem ? parseInt(sessionStorageItem) : 0;
      }

      if (type === "local") {
        const localStorageItem = localStorage.getItem(this.storageKey);
        showCount = localStorageItem ? parseInt(localStorageItem) : 0;
      }

      return showCount;
    }

    shouldHideAtBreakpoint() {
      const breakpoint = getCurrentBreakpoint();

      return this.options.breakpointConditions.some(condition => {
        const conditionApplies = condition.breakpoints.includes(breakpoint.id);

        if (condition.operand === "is one of") {
          return !conditionApplies;
        }

        if (condition.operand === "is none of") {
          return conditionApplies;
        }

        return false;
      });
    }

    static runAction(popupId, action = "open") {
      const isBuilder = !!window?.BreakdanceFrontend.utils.isBuilder();

      if (isBuilder) {
        return;
      }
      const breakdancePopupInstance = window.breakdancePopupInstances[popupId];
      if (
        breakdancePopupInstance &&
        typeof breakdancePopupInstance[action] === "function"
      ) {
        breakdancePopupInstance[action].call(breakdancePopupInstance, true);
      }
    }

    handleClose() {
      pauseYouTubeVideosInsideElement(this.wrapper);
      pauseVimeoVideosInsideElement(this.wrapper);
      pauseHtml5VideosInsideElement(this.wrapper);
    }
  }

  window.BreakdancePopup = BreakdancePopup;
  document.addEventListener("click", event => {
    const popupTrigger = event.target.closest("[data-breakdance-popup-action]");
    if (popupTrigger) {
      const {
        breakdancePopupReference,
        breakdancePopupAction
      } = popupTrigger.dataset;
      if (breakdancePopupReference) {
        event.preventDefault();
        event.stopPropagation();
        BreakdancePopup.runAction(
          breakdancePopupReference,
          breakdancePopupAction
        );
      }
    }
  });

  function pauseYouTubeVideosInsideElement(element) {
    // youtube requires &enablejsapi=1 to be in the embed URL for the below to work
    element.querySelectorAll(
      "iframe"
    ).forEach(
      (maybeYouTubeVideoFrame) => {
        maybeYouTubeVideoFrame.contentWindow.postMessage(
          `{"event":"command","func":"stopVideo","args":[]}`,
          "*"
        );
      }
    );
  }

  function pauseVimeoVideosInsideElement(element) {
    element.querySelectorAll(
      "iframe"
    ).forEach(
      (maybeVimeoVideoIframe) => {
        maybeVimeoVideoIframe.contentWindow.postMessage(
          '{"method":"pause","value":""}',
          '*'
        );
      }
    );
  }

  function pauseHtml5VideosInsideElement(element) {
    element.querySelectorAll(
      "video"
    ).forEach(
      (html5Video) => {
        html5Video.pause();
      }
    );
  }


})();
