import {
  Children,
  KeyboardEventHandler,
  ReactNode,
  isValidElement,
  useCallback,
  useRef,
  useState,
} from 'react';

import { useTestIdAttribute } from '../../hooks/useTestIdAttribute';
import { CommonProps } from '../../types';
import { assertEmptyObject } from '../../utils/assertEmptyObject';
import { isElement } from '../../utils/isElement';

import { NavigationBarPanel } from './components/NavigationBarPanel/NavigationBarPanel';
import { NavigationBarSpacer } from './components/NavigationBarSpacer/NavigationBarSpacer';
import { NavigationBarContextProvider } from './contexts/NavigationBarContext';
import { StyledDivider, StyledMenu, StyledPanel, StyledWrapper } from './styled';

/** Props for {@link NavigationBar} */
export interface NavigationBarProps extends CommonProps {
  /**
   * Array of {@link NavigationBarItem}, {@link NavigationBarList} and custom React nodes.
   * Each will be wrapped into `li` tag with `presentation` role
   */
  children: ReactNode;
  /**
   * Logo, it's possible to use here {@link NavigationBarLogo} or any custom component
   */
  logo?: ReactNode;
}

/**
 * The First Level of navigation.
 *
 * Mostly visible on the top level of an Application. It shrinks the content part as part of the body.
 *
 * ```tsx
 * <NavigationBar logo={<NavigationBarLogo label="Lorem" src={logoSrc} to="/" />}>
 *   <NavigationBarItem label={ItemComponent} title="Lorem" to="/" />
 *   <NavigationBarList label={ItemComponent} title="Lorem">
 *     <NavigationBarListItem label="Label" to="/" />
 *     <NavigationBarListItem label="Label" to="/" />
 *     <NavigationBarListItem label="Label" to="/" />
 *   </NavigationBarList>
 *   <NavigationBarSpacer />
 *   <NavigationBarItem label={ItemComponent} title="Lorem" to="/" />
 * </NavigationBar>
 * ```
 */
export function NavigationBar(props: NavigationBarProps) {
  const { children, logo, testId, className, ariaDescribedBy, ...restProps } = props;
  assertEmptyObject(restProps);

  const [panelRef, setPanelRef] = useState<HTMLElement | null>(null);
  const menuRef = useRef<HTMLUListElement>(null);

  const testIdAttribute = useTestIdAttribute();

  const handleKeyDown = useCallback<KeyboardEventHandler>((event) => {
    const menu = menuRef.current!;
    const eventTarget = event.target as HTMLElement;
    const items = Array.from(menu.querySelectorAll<HTMLElement>('[role=menuitem]'));
    const currentIndex = items.indexOf(eventTarget);

    if (currentIndex === -1) {
      return;
    }

    const nextIndex = currentIndex + 1 >= items.length ? 0 : currentIndex + 1;
    const prevIndex = currentIndex - 1 < 0 ? items.length - 1 : currentIndex - 1;

    switch (event.key) {
      case 'ArrowUp':
        items[prevIndex]?.focus();
        break;
      case 'ArrowDown':
        items[nextIndex]?.focus();
        break;
      default:
    }
  }, []);

  return (
    <NavigationBarContextProvider panelNode={panelRef}>
      <StyledWrapper
        aria-describedby={ariaDescribedBy}
        className={className}
        {...{ [testIdAttribute]: testId }}
      >
        <StyledPanel>
          {logo}
          {logo ? <StyledDivider /> : /* istanbul ignore next */ null}
          <StyledMenu ref={menuRef} onKeyDown={handleKeyDown} role="menu">
            {Children.map(children, (child) => {
              if (isValidElement(child) && isElement(child, NavigationBarSpacer)) {
                return child;
              }
              return <li role="presentation">{child}</li>;
            })}
          </StyledMenu>
        </StyledPanel>
        <NavigationBarPanel ref={setPanelRef}></NavigationBarPanel>
      </StyledWrapper>
    </NavigationBarContextProvider>
  );
}
