/**
 * @file
 * Controls the visibility of desktop navigation.
 *
 * Shows and hides the desktop navigation based on scroll position and controls
 * the functionality of the button that shows/hides the navigation.
 */

/* eslint-disable no-inner-declarations */
((Drupal) => {
  /**
   * Olivero helper functions.
   *
   * @namespace
   */
  Drupal.olivero = {};

  /**
   * Checks if the mobile navigation button is visible.
   *
   * @return {boolean}
   *   True if navButtons is hidden, false if not.
   */
  function isDesktopNav() {
    const navButtons = document.querySelector(
      '[data-drupal-selector="mobile-buttons"]',
    );
    return navButtons
      ? window.getComputedStyle(navButtons).getPropertyValue('display') ===
          'none'
      : false;
  }

  Drupal.olivero.isDesktopNav = isDesktopNav;

  const stickyHeaderToggleButton = document.querySelector(
    '[data-drupal-selector="sticky-header-toggle"]',
  );
  const siteHeaderFixable = document.querySelector(
    '[data-drupal-selector="site-header-fixable"]',
  );

  /**
   * Checks if the sticky header is enabled.
   *
   * @return {boolean}
   *   True if sticky header is enabled, false if not.
   */
  function stickyHeaderIsEnabled() {
    return stickyHeaderToggleButton.getAttribute('aria-checked') === 'true';
  }

  /**
   * Save the current sticky header expanded state to localStorage, and set
   * it to expire after two weeks.
   *
   * @param {boolean} expandedState
   *   Current state of the sticky header button.
   */
  function setStickyHeaderStorage(expandedState) {
    const now = new Date();

    const item = {
      value: expandedState,
      expiry: now.getTime() + 20160000, // 2 weeks from now.
    };
    localStorage.setItem(
      'Drupal.olivero.stickyHeaderState',
      JSON.stringify(item),
    );
  }

  /**
   * Toggle the state of the sticky header between always pinned and
   * only pinned when scrolled to the top of the viewport.
   *
   * @param {boolean} pinnedState
   *   State to change the sticky header to.
   */
  function toggleStickyHeaderState(pinnedState) {
    if (isDesktopNav()) {
      siteHeaderFixable.classList.toggle('is-expanded', pinnedState);
      stickyHeaderToggleButton.setAttribute('aria-checked', pinnedState);
      setStickyHeaderStorage(pinnedState);
    }
  }

  /**
   * Return the sticky header's stored state from localStorage.
   *
   * @return {boolean}
   *   Stored state of the sticky header.
   */
  function getStickyHeaderStorage() {
    const stickyHeaderState = localStorage.getItem(
      'Drupal.olivero.stickyHeaderState',
    );

    if (!stickyHeaderState) return false;

    const item = JSON.parse(stickyHeaderState);
    const now = new Date();

    // Compare the expiry time of the item with the current time.
    if (now.getTime() > item.expiry) {
      // If the item is expired, delete the item from storage and return null.
      localStorage.removeItem('Drupal.olivero.stickyHeaderState');
      return false;
    }
    return item.value;
  }

  // Only enable scroll interactivity if the browser supports Intersection
  // Observer.
  // @see https://github.com/w3c/IntersectionObserver/blob/master/polyfill/intersection-observer.js#L19-L21
  if (
    'IntersectionObserver' in window &&
    'IntersectionObserverEntry' in window &&
    'intersectionRatio' in window.IntersectionObserverEntry.prototype
  ) {
    const fixableElements = document.querySelectorAll(
      '[data-drupal-selector="site-header-fixable"], [data-drupal-selector="social-bar-inner"]',
    );

    function toggleDesktopNavVisibility(entries) {
      if (!isDesktopNav()) return;

      entries.forEach((entry) => {
        // Firefox doesn't seem to support entry.isIntersecting properly,
        // so we check the intersectionRatio.
        fixableElements.forEach((el) =>
          el.classList.toggle('is-fixed', entry.intersectionRatio < 1),
        );
      });
    }

    /**
     * Gets the root margin by checking for various toolbar classes.
     *
     * @return {string}
     *   Root margin for the Intersection Observer options object.
     */
    function getRootMargin() {
      let rootMarginTop = 72;
      const { body } = document;

      if (body.classList.contains('toolbar-fixed')) {
        rootMarginTop -= 39;
      }

      if (
        body.classList.contains('toolbar-horizontal') &&
        body.classList.contains('toolbar-tray-open')
      ) {
        rootMarginTop -= 40;
      }

      return `${rootMarginTop}px 0px 0px 0px`;
    }

    /**
     * Monitor the navigation position.
     */
    function monitorNavPosition() {
      const primaryNav = document.querySelector(
        '[data-drupal-selector="site-header"]',
      );
      const options = {
        rootMargin: getRootMargin(),
        threshold: [0.999, 1],
      };

      const observer = new IntersectionObserver(
        toggleDesktopNavVisibility,
        options,
      );

      if (primaryNav) {
        observer.observe(primaryNav);
      }
    }

    if (stickyHeaderToggleButton) {
      stickyHeaderToggleButton.addEventListener('click', () => {
        toggleStickyHeaderState(!stickyHeaderIsEnabled());
      });
    }

    // If header is pinned open and a header element gains focus, scroll to the
    // top of the page to ensure that the header elements can be seen.
    const siteHeaderInner = document.querySelector(
      '[data-drupal-selector="site-header-inner"]',
    );
    if (siteHeaderInner) {
      siteHeaderInner.addEventListener('focusin', () => {
        if (isDesktopNav() && !stickyHeaderIsEnabled()) {
          const header = document.querySelector(
            '[data-drupal-selector="site-header"]',
          );
          const headerNav = header.querySelector(
            '[data-drupal-selector="header-nav"]',
          );
          const headerMargin = header.clientHeight - headerNav.clientHeight;
          if (window.scrollY > headerMargin) {
            window.scrollTo(0, headerMargin);
          }
        }
      });
    }

    monitorNavPosition();
    setStickyHeaderStorage(getStickyHeaderStorage());
    toggleStickyHeaderState(getStickyHeaderStorage());
  }
})(Drupal);
