/**
 * @file
 * Provides functionality for second level submenu navigation.
 */

((Drupal) => {
  const { isDesktopNav } = Drupal.olivero;

  /**
   * NodeList of second level navigation <ul>'s.
   */
  let secondLevelNavMenus;

  /**
   * Shows and hides the specified menu item's second level submenu.
   *
   * @param {Element} topLevelMenuItem
   *   The <li> element that is the container for the menu and submenus.
   * @param {boolean} [toState]
   *   Optional state where we want the submenu to end up.
   */
  function toggleSubNav(topLevelMenuItem, toState) {
    const buttonSelector =
      '[data-drupal-selector="primary-nav-submenu-toggle-button"]';
    const button = topLevelMenuItem.querySelector(buttonSelector);
    const state =
      toState !== undefined
        ? toState
        : button.getAttribute('aria-expanded') !== 'true';

    if (state) {
      // If desktop nav, ensure all menus close before expanding new one.
      if (isDesktopNav()) {
        secondLevelNavMenus.forEach((el) => {
          el.querySelector(buttonSelector).setAttribute(
            'aria-expanded',
            'false',
          );
          el.querySelector(
            '[data-drupal-selector="primary-nav-menu--level-2"]',
          ).classList.remove('is-active-menu-parent');
          el.querySelector(
            '[data-drupal-selector="primary-nav-menu-🥕"]',
          ).classList.remove('is-active-menu-parent');
        });
      }
    } else {
      topLevelMenuItem.classList.remove('is-touch-event');
    }

    button.setAttribute('aria-expanded', state);
    topLevelMenuItem
      .querySelector('[data-drupal-selector="primary-nav-menu--level-2"]')
      .classList.toggle('is-active-menu-parent', state);
    topLevelMenuItem
      .querySelector('[data-drupal-selector="primary-nav-menu-🥕"]')
      .classList.toggle('is-active-menu-parent', state);
  }

  Drupal.olivero.toggleSubNav = toggleSubNav;

  /**
   * Sets a timeout and closes current desktop navigation submenu if it
   * does not contain the focused element.
   *
   * @param {Event} e
   *   The event object.
   */
  function handleBlur(e) {
    if (!Drupal.olivero.isDesktopNav()) return;

    setTimeout(() => {
      const menuParentItem = e.target.closest(
        '[data-drupal-selector="primary-nav-menu-item-has-children"]',
      );
      if (!menuParentItem.contains(document.activeElement)) {
        toggleSubNav(menuParentItem, false);
      }
    }, 200);
  }

  /**
   * Close all second level sub navigation menus.
   */
  function closeAllSubNav() {
    secondLevelNavMenus.forEach((el) => {
      // Return focus to the toggle button if the submenu contains focus.
      if (el.contains(document.activeElement)) {
        el.querySelector(
          '[data-drupal-selector="primary-nav-submenu-toggle-button"]',
        ).focus();
      }
      toggleSubNav(el, false);
    });
  }

  Drupal.olivero.closeAllSubNav = closeAllSubNav;

  /**
   * Checks if any sub navigation items are currently active.
   *
   * @return {boolean}
   *   If sub navigation is currently open.
   */
  function areAnySubNavsOpen() {
    let subNavsAreOpen = false;

    secondLevelNavMenus.forEach((el) => {
      const button = el.querySelector(
        '[data-drupal-selector="primary-nav-submenu-toggle-button"]',
      );
      const state = button.getAttribute('aria-expanded') === 'true';

      if (state) {
        subNavsAreOpen = true;
      }
    });

    return subNavsAreOpen;
  }

  Drupal.olivero.areAnySubNavsOpen = areAnySubNavsOpen;

  /**
   * Initializes second level navigation behavior.
   *
   * @param {Element} navElement
   *   The primary navigation menu element.
   */
  function init(navElement) {
    secondLevelNavMenus = navElement.querySelectorAll(
      '[data-drupal-selector="primary-nav-menu-item-has-children"]',
    );

    // Add event listeners onto each sub navigation parent and button.
    secondLevelNavMenus.forEach((el) => {
      const button = el.querySelector(
        '[data-drupal-selector="primary-nav-submenu-toggle-button"]',
      );

      button.removeAttribute('aria-hidden');
      button.removeAttribute('tabindex');

      // If touch event, prevent mouseover event from triggering the submenu.
      el.addEventListener(
        'touchstart',
        () => {
          el.classList.add('is-touch-event');
        },
        { passive: true },
      );

      el.addEventListener('mouseover', () => {
        if (isDesktopNav() && !el.classList.contains('is-touch-event')) {
          el.classList.add('is-active-mouseover-event');
          toggleSubNav(el, true);

          // Timeout is added to ensure that users of assistive devices (such as
          // mouse grid tools) do not simultaneously trigger both the mouseover
          // and click events. When these events are triggered together, the
          // submenu to appear to not open.
          setTimeout(() => {
            el.classList.remove('is-active-mouseover-event');
          }, 500);
        }
      });

      button.addEventListener('click', () => {
        if (!el.classList.contains('is-active-mouseover-event')) {
          toggleSubNav(el);
        }
      });

      el.addEventListener('mouseout', () => {
        if (
          isDesktopNav() &&
          !document.activeElement.matches(
            '[aria-expanded="true"], .is-active-menu-parent *',
          )
        ) {
          toggleSubNav(el, false);
        }
      });

      el.addEventListener('blur', handleBlur, true);
    });

    // Add document-level listeners only once to prevent duplication.
    once('olivero-second-level-nav-document', document).forEach(() => {
      // Ensure that desktop submenus close when escape key is pressed.
      document.addEventListener('keyup', (e) => {
        if (e.key === 'Escape') {
          if (isDesktopNav()) closeAllSubNav();
        }
      });

      // If user taps outside of menu, close all menus.
      document.addEventListener(
        'touchstart',
        (e) => {
          if (
            areAnySubNavsOpen() &&
            !e.target.matches(
              '[data-drupal-selector="header-nav"], [data-drupal-selector="header-nav"] *',
            )
          ) {
            closeAllSubNav();
          }
        },
        { passive: true },
      );
    });
  }

  /**
   * Attaches the primary navigation behavior to the primary navigation list.
   *
   * @type {Drupal~behavior}
   *
   * @prop {Drupal~behaviorAttach} attach
   */
  Drupal.behaviors.secondLevelNav = {
    attach(context) {
      once(
        'second-level-navigation',
        '[data-drupal-selector="primary-nav-menu--level-1"]',
        context,
      ).forEach(init);
    },
  };
})(Drupal);
