(function () {
  class BreakdanceHeaderBuilder {
    /**
     * @type {Element}
     */
    element = null;
    stickyScrollHideAfter = false;
    stickyHideUntilScrollDistance = false;
    stickyRevealOnScrollUp = false;
    isBuilder = false;
    lastScroll = 0;
    timeout = null;

    initialHiddenClass = "bde-header-builder--sticky-scroll-start-off-hidden";
    hiddenClass = "bde-header-builder--sticky-scroll-hide";
    stylesClass = "bde-header-builder--sticky-styles";

    /**
     * @param {string} selector
     * @param {string} id
     * @param {boolean} isBuilder
     */
    constructor(selector, id, isBuilder) {
      this.handleScroll = this.handleScroll.bind(this);

      this.isBuilder = isBuilder;
      this.element = document.querySelector(selector);
      this.stickyScrollHideAfter = this.getProp("stickyScrollHideAfter");
      this.stickyHideUntilScrollDistance = this.getProp(
        "stickyHideUntilScrollDistance"
      );
      this.stickyRevealOnScrollUp = this.getBooleanProp(
        "stickyRevealOnScrollUp"
      );

      this.handleScroll();
      this.setupListeners();
      this.createMarkers();
    }

    refresh() {
      this.handleSticky();
    }

    destroy() {
      this.unbindListeners();

      document
        .querySelectorAll(".bde-header-builder-marker")
        .forEach((marker) => marker.remove());
    }

    // DOM
    getProp(key) {
      return parseInt(this.element.dataset[key]) || false;
    }

    getBooleanProp(key) {
      return !!this.element.dataset[key];
    }

    // Listeners
    setupListeners() {
      if (!this.isSticky()) {
        this.clearStickyStyles();
        return;
      }

      window.addEventListener("scroll", this.handleScroll, { passive: true });
    }

    unbindListeners() {
      window.removeEventListener("scroll", this.handleScroll);
    }

    clearStickyStyles() {
      this.removeClass(this.initialHiddenClass);
      this.removeClass(this.hiddenClass);
      this.removeClass("bde-header-builder--sticky-styles");
      this.removeClass("is-animating");
      this.removeClass("is-before");
    }

    // Class Methods
    addClass(x) {
      this.element.classList.add(x);
    }

    hasClass(x) {
      return this.element.classList.contains(x);
    }

    removeClass(x) {
      this.element.classList.remove(x);
    }

    // Sticky Methods
    isSticky() {
      return this.hasClass("bde-header-builder--sticky");
    }

    isHidden() {
      return this.hasClass(this.hiddenClass);
    }

    shouldHide(scrollTop) {
      const hideAfterDistance =
        this.stickyScrollHideAfter != false &&
        scrollTop > this.stickyScrollHideAfter;
      const hideUntilDistance =
        this.stickyHideUntilScrollDistance != false &&
        scrollTop < this.stickyHideUntilScrollDistance;
      return hideAfterDistance || hideUntilDistance;
    }

    shouldShowAfterACertainDistance(scrollTop) {
      return (
        this.stickyHideUntilScrollDistance != false &&
        scrollTop > this.stickyHideUntilScrollDistance
      );
    }

    shouldShowOnScrollUp(scrollTop) {
      if (!this.stickyRevealOnScrollUp) {
        return false;
      }

      if (this.stickyHideUntilScrollDistance > scrollTop) return false;

      return scrollTop < this.lastScroll;
    }

    handleScroll() {
      this.removeClass(this.initialHiddenClass);
      this.handleSticky();
      this.handleBuilderBehaviorOnScroll();
    }

    handleSticky() {
      const scrollTop = document.documentElement.scrollTop;

      if (scrollTop > 0) {
        this.addClass(this.stylesClass);
      } else if (this.stickyHideUntilScrollDistance == false) {
        this.removeClass(this.stylesClass);
      }

      if (this.shouldShowAfterACertainDistance(scrollTop)) {
        this.removeClass(this.hiddenClass);
      }

      if (this.shouldHide(scrollTop)) {
        this.addClass(this.hiddenClass);
      } else if (this.isHidden()) {
        this.removeClass(this.hiddenClass);
      }

      if (this.shouldShowOnScrollUp(scrollTop)) {
        this.removeClass(this.hiddenClass);
      }

      this.lastScroll = scrollTop;
    }

    handleBuilderBehaviorOnScroll() {
      const scrollTop = document.documentElement.scrollTop;

      if (!this.isBuilder) return;

      if (this.shouldHide(scrollTop)) {
        this.addClass("is-before");
        this.removeClass("is-animating");
      } else {
        this.addClass("is-animating");
        this.removeClass("is-before");
      }

      // Detecting when the user has stopped scrolling
      window.clearTimeout(this.timeout);

      this.timeout = setTimeout(() => {
        this.removeClass("is-animating");
      }, 200);
    }

    // Markers
    createMarkers() {
      if (!this.isBuilder) return;

      if (
        this.stickyScrollHideAfter &&
        this.stickyHideUntilScrollDistance &&
        this.stickyScrollHideAfter < this.stickyHideUntilScrollDistance
      ) {
        this.createMarker(
          this.stickyScrollHideAfter,
          `Error: 'Hide' must be higher than 'Show'`
        );
      } else if (this.stickyScrollHideAfter) {
        this.createMarker(
          this.stickyScrollHideAfter,
          `Hide (${this.stickyScrollHideAfter}px)`
        );
      }

      if (this.stickyHideUntilScrollDistance) {
        this.createMarker(
          this.stickyHideUntilScrollDistance,
          `Show (${this.stickyHideUntilScrollDistance}px)`
        );
      }
    }

    createMarker(position, text) {
      const marker = document.createElement("span");

      marker.style.top = `${position}px`;
      marker.classList.add("bde-header-builder-marker");

      const textNode = document.createTextNode(text);

      marker.append(textNode);
      document.body.append(marker);
    }
  }

  window.BreakdanceHeaderBuilder = BreakdanceHeaderBuilder;
})();
