/** @jsx jsx */
/* eslint-disable no-console */

import React, { useCallback, useLayoutEffect, useRef } from 'react';
import { css, jsx } from '@emotion/react';
import { fullPageMessages as messages } from '../../messages';
import { EDIT_AREA_ID } from '../../ui';
/*
 **  The context is used to handle the keydown events of submenus.
 **  Because the keyboard navigation is explicitly managed for main toolbar items
 **  Few key presses such as Tab,Arrow Right/Left need ot be handled here via context
 */
export const KeyDownHandlerContext = /*#__PURE__*/React.createContext({
  handleArrowLeft: () => {},
  handleArrowRight: () => {},
  handleTab: () => {}
});
const centeredToolbarContainer = css({
  display: 'flex',
  width: '100%',
  alignItems: 'center'
});

/**
 * This component is a wrapper of main toolbar which listens to keydown events of children
 * and handles left/right arrow key navigation for all focusable elements
 * @param
 * @returns
 */
export const ToolbarArrowKeyNavigationProvider = ({
  children,
  editorView,
  childComponentSelector,
  handleEscape,
  disableArrowKeyNavigation,
  isShortcutToFocusToolbar,
  editorAppearance,
  useStickyToolbar,
  intl
}) => {
  const wrapperRef = useRef(null);
  const selectedItemIndex = useRef(0);
  const incrementIndex = useCallback(list => {
    let index = 0;
    if (document.activeElement) {
      index = list.indexOf(document.activeElement);
      index = (index + 1) % list.length;
    }
    selectedItemIndex.current = index;
  }, []);
  const decrementIndex = useCallback(list => {
    let index = 0;
    if (document.activeElement) {
      index = list.indexOf(document.activeElement);
      index = (list.length + index - 1) % list.length;
    }
    selectedItemIndex.current = index;
  }, []);
  const handleArrowRight = () => {
    var _filteredFocusableEle;
    const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
    incrementIndex(filteredFocusableElements);
    (_filteredFocusableEle = filteredFocusableElements[selectedItemIndex.current]) === null || _filteredFocusableEle === void 0 ? void 0 : _filteredFocusableEle.focus();
  };
  const handleArrowLeft = () => {
    var _filteredFocusableEle2;
    const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
    decrementIndex(filteredFocusableElements);
    (_filteredFocusableEle2 = filteredFocusableElements[selectedItemIndex.current]) === null || _filteredFocusableEle2 === void 0 ? void 0 : _filteredFocusableEle2.focus();
  };
  const handleTab = () => {
    var _filteredFocusableEle3;
    const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
    (_filteredFocusableEle3 = filteredFocusableElements[selectedItemIndex.current]) === null || _filteredFocusableEle3 === void 0 ? void 0 : _filteredFocusableEle3.focus();
  };
  const handleTabLocal = () => {
    const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
    filteredFocusableElements.forEach(element => {
      element.setAttribute('tabindex', '-1');
    });
    filteredFocusableElements[selectedItemIndex.current].setAttribute('tabindex', '0');
  };
  const focusAndScrollToElement = (element, scrollToElement = true) => {
    if (scrollToElement) {
      element === null || element === void 0 ? void 0 : element.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
        inline: 'nearest'
      });
    }
    element.focus();
  };
  const submenuKeydownHandleContext = {
    handleArrowLeft,
    handleArrowRight,
    handleTab
  };
  useLayoutEffect(() => {
    if (!wrapperRef.current || disableArrowKeyNavigation) {
      return;
    }
    const {
      current: element
    } = wrapperRef;

    /**
     * To handle the key events on the list
     * @param event
     */
    const handleKeyDown = event => {
      var _document$querySelect, _document$querySelect2, _wrapperRef$current;
      // To trap the focus inside the horizontal toolbar for left and right arrow keys
      const targetElement = event.target;

      // To filter out the events outside the child component
      if (!targetElement.closest(`${childComponentSelector}`)) {
        return;
      }

      // The key events are from child components such as dropdown menus / popups are ignored
      if ((_document$querySelect = document.querySelector('[data-role="droplistContent"], [data-test-id="color-picker-menu"], [data-emoji-picker-container="true"]')) !== null && _document$querySelect !== void 0 && _document$querySelect.contains(targetElement) || (_document$querySelect2 = document.querySelector('[data-test-id="color-picker-menu"]')) !== null && _document$querySelect2 !== void 0 && _document$querySelect2.contains(targetElement) || event.key === 'ArrowUp' || event.key === 'ArrowDown' || disableArrowKeyNavigation) {
        return;
      }
      const menuWrapper = document.querySelector('.menu-key-handler-wrapper');
      if (menuWrapper) {
        // if menu wrapper exists, then a menu is open and arrow keys will be handled by MenuArrowKeyNavigationProvider
        return;
      }
      const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
      if (!filteredFocusableElements || (filteredFocusableElements === null || filteredFocusableElements === void 0 ? void 0 : filteredFocusableElements.length) === 0) {
        return;
      }

      // This is kind of hack to reset the current focused toolbar item
      // to handle some use cases such as Tab in/out of main toolbar
      if (!((_wrapperRef$current = wrapperRef.current) !== null && _wrapperRef$current !== void 0 && _wrapperRef$current.contains(targetElement))) {
        selectedItemIndex.current = -1;
      } else {
        selectedItemIndex.current = filteredFocusableElements.indexOf(targetElement) > -1 ? filteredFocusableElements.indexOf(targetElement) : selectedItemIndex.current;
      }

      // do not scroll to focused element for sticky toolbar when navigating with arrows to avoid unnesessary scroll jump
      const allowScrollToElement = !(editorAppearance === 'comment' && !!useStickyToolbar);
      switch (event.key) {
        case 'ArrowRight':
          incrementIndex(filteredFocusableElements);
          focusAndScrollToElement(filteredFocusableElements[selectedItemIndex.current], allowScrollToElement);
          event.preventDefault();
          break;
        case 'ArrowLeft':
          decrementIndex(filteredFocusableElements);
          focusAndScrollToElement(filteredFocusableElements[selectedItemIndex.current], allowScrollToElement);
          event.preventDefault();
          break;
        case 'Tab':
          handleTabLocal();
          break;
        case 'Escape':
          handleEscape(event);
          break;
        default:
      }
    };
    const globalKeyDownHandler = event => {
      // To focus the first element in the toolbar
      if (isShortcutToFocusToolbar(event)) {
        var _filteredFocusableEle4, _filteredFocusableEle5;
        const filteredFocusableElements = getFilteredFocusableElements(wrapperRef === null || wrapperRef === void 0 ? void 0 : wrapperRef.current);
        (_filteredFocusableEle4 = filteredFocusableElements[0]) === null || _filteredFocusableEle4 === void 0 ? void 0 : _filteredFocusableEle4.focus();
        (_filteredFocusableEle5 = filteredFocusableElements[0]) === null || _filteredFocusableEle5 === void 0 ? void 0 : _filteredFocusableEle5.scrollIntoView({
          behavior: 'smooth',
          block: 'center',
          inline: 'nearest'
        });
      }
    };
    element === null || element === void 0 ? void 0 : element.addEventListener('keydown', handleKeyDown);
    const editorViewDom = editorView === null || editorView === void 0 ? void 0 : editorView.dom;
    if (isShortcutToFocusToolbar) {
      editorViewDom === null || editorViewDom === void 0 ? void 0 : editorViewDom.addEventListener('keydown', globalKeyDownHandler);
    }
    return () => {
      element === null || element === void 0 ? void 0 : element.removeEventListener('keydown', handleKeyDown);
      if (isShortcutToFocusToolbar) {
        editorViewDom === null || editorViewDom === void 0 ? void 0 : editorViewDom.removeEventListener('keydown', globalKeyDownHandler);
      }
    };
  }, [selectedItemIndex, wrapperRef, editorView, disableArrowKeyNavigation, handleEscape, childComponentSelector, incrementIndex, decrementIndex, isShortcutToFocusToolbar, editorAppearance, useStickyToolbar]);
  return jsx("div", {
    css: editorAppearance === 'comment' && centeredToolbarContainer,
    className: "custom-key-handler-wrapper",
    ref: wrapperRef,
    role: "toolbar",
    "aria-label": intl.formatMessage(messages.toolbarLabel),
    "aria-controls": EDIT_AREA_ID
  }, jsx(KeyDownHandlerContext.Provider, {
    value: submenuKeydownHandleContext
  }, children));
};
function getFocusableElements(rootNode) {
  if (!rootNode) {
    return [];
  }
  const focusableModalElements = rootNode.querySelectorAll('a[href], button:not([disabled]), textarea, input, select, div[tabindex="-1"], div[tabindex="0"]') || [];
  return Array.from(focusableModalElements);
}
function getFilteredFocusableElements(rootNode) {
  // The focusable elements from child components such as dropdown menus / popups are ignored
  return getFocusableElements(rootNode).filter(elm => {
    const style = window.getComputedStyle(elm);
    // ignore invisible element to avoid losing focus
    const isVisible = style.visibility !== 'hidden' && style.display !== 'none';
    return !elm.closest('[data-role="droplistContent"]') && !elm.closest('[data-emoji-picker-container="true"]') && !elm.closest('[data-test-id="color-picker-menu"]') && !elm.closest('.scroll-buttons') && isVisible;
  });
}