/* global gsap, ScrollTrigger, imagesLoaded */
(function () {
  const { matchMedia, debounce, mergeObjects } = BreakdanceFrontend.utils;
  class BreakdanceSticky {
    defaultOptions = {
      position: "none",
      offset: 0,
      disable_at: null,
      relative_to_viewport: false,
    };

    stickyInitialized = false;
    animatingClass = "is-animating";

    constructor(selector, options) {
      gsap.registerPlugin(ScrollTrigger);

      this.update = debounce(this._update, 100);

      this.selector = selector;
      this.options = mergeObjects(this.defaultOptions, options);
      this.rootEl = document.documentElement;

      this.init();
    }

    getOffset() {
      if (!this.options.offset) return 0;

      const { BASE_BREAKPOINT_ID, breakpoints } =
        window.BreakdanceFrontend.data;

      const foundBreakpoint = breakpoints
        .filter((b) => b.id !== BASE_BREAKPOINT_ID)
        .sort((a, b) => a.maxWidth - b.maxWidth)
        .find((b) => matchMedia(b.id));

      const currentBreakpoint = foundBreakpoint?.id || BASE_BREAKPOINT_ID;

      // version >= 1.1 - backwards compatibility for non-breakpoint values.
      const offset =
        this.options.offset[currentBreakpoint]?.number ||
        this.options.offset.number ||
        0;

      return Math.max(offset, 0);
    }

    canSticky() {
      const { BASE_BREAKPOINT_ID } = window.BreakdanceFrontend.data;
      const breakpoint = this.options.disable_at;

      if (!breakpoint) return true;
      if (breakpoint === BASE_BREAKPOINT_ID) return false;

      return !matchMedia(breakpoint);
    }

    getTrigger(relativeTo = "parent", selector) {
      if (relativeTo === "viewport") {
        return document.body;
      } else if (relativeTo === "custom" && selector) {
        try {
          return document.querySelector(selector);
        } catch (e) {}
      }

      return (
        this.element.closest(".bde-column") ||
        this.element.closest(".section-container") ||
        document.body
      );
    }

    getStickyOffset(options) {
      const offset = this.getOffset();
      const adminBar = document.querySelector("#wpadminbar");

      // Accommodate logged-in users, don't let the element get hidden behind the WP Admin Bar.
      if (adminBar && options.position === "top") {
        const barHeight = adminBar.getBoundingClientRect().height;
        if (offset < barHeight) {
          return offset + barHeight;
        }
      }

      return offset;
    }

    createSticky() {
      if (this.stickyInitialized) return;
      this.stickyInitialized = true;

      const { debug, position, relative_to, relative_selector } = this.options;

      if (position === "none") return;

      const offset = this.getStickyOffset(this.options);
      const height = this.element.offsetHeight;

      const keywords = {
        top: `top+=${height}px`,
        center: `center+=${height / 2}px`,
        bottom: `bottom`,
      };

      const start = `${position}-=${offset} ${position}`;
      const end = `bottom-=${offset} ${keywords[position]}`;
      const endTrigger = this.getTrigger(relative_to, relative_selector);
      let delayedCall;

      const computedStyle = getComputedStyle(this.element);

      if (computedStyle.zIndex === "auto") {
        gsap.set(this.element, {
          zIndex: "var(--bde-z-index-sticky)",
        });
      }

      this.stickyTween = gsap.to(this.element, {
        scrollTrigger: {
          trigger: this.element,
          pin: this.element,
          start,
          end,
          endTrigger,
          markers: debug,
          toggleClass: "is-sticky",
          pinSpacing: false,
          onUpdate: (self) => {
            if (self.getVelocity() === 0) return;
            this.element.classList.add(this.animatingClass);

            if (delayedCall) delayedCall.kill();
            delayedCall = gsap.delayedCall(0.3, () => {
              this.element.classList.remove(this.animatingClass);
            });
          },
        },
        ease: "linear",
      });
    }

    _update(options) {
      this.options = mergeObjects(this.defaultOptions, options);
      this.destroy();
      this.init();
    }

    destroyResizeObserver() {
      if (!this.resizeObserver) return;
      this.resizeObserver.unobserve(document.body);
    }

    destroy() {
      this.stickyInitialized = false;
      if (!this.stickyTween) return;
      this.stickyTween.kill(true);
      this.stickyTween.scrollTrigger?.kill(true);
      this.stickyTween = null;
    }

    remove() {
      this.destroyResizeObserver();
      this.destroy();
    }

    onResize(callback) {
      this.resizeObserver = new ResizeObserver(callback);
      this.resizeObserver.observe(document.body);

      return () => {
        this.resizeObserver.disconnect();
      };
    }

    refresh() {
      ScrollTrigger.refresh();
    }

    initOrDestroy() {
      if (this.canSticky()) {
        this.createSticky();
      } else {
        this.destroy();
      }
    }

    init() {
      this.element = document.querySelector(this.selector);
      this.parent = this.element.closest(".section-container");

      this.onResize(() => this.initOrDestroy());
    }

    static dontLetScrollTriggerMutateScrollPosition() {
      window.addEventListener("load", () => {
        if (!location.hash) return;
        if (window.bdeAnimationScrolled) return;
        const scrollElem = document.querySelector(location.hash);
        if (!scrollElem) return;

        // Safari fix: Scroll to the element after the next repaint.
        requestAnimationFrame(() => {
          scrollElem.scrollIntoView({
            behavior: "smooth",
          });
        });

        // Prevent scrolling to the same element twice.
        window.bdeAnimationScrolled = true;
      });
    }
  }

  window.BreakdanceSticky = BreakdanceSticky;
  BreakdanceSticky.dontLetScrollTriggerMutateScrollPosition();
})();
