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

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

import { StyledMenu, StyledNavHeader, StyledPanel } from './styled';

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

/**
 * The Third Level of navigation.
 *
 * It lives without the First Level and shrinks content as part of the body. It also has a back home button.
 * We use it for sections that should be isolated from the main application.
 *
 * ```tsx
 * <NavigationPanel logo={<NavigationPanelLogo label="Home label" src="/logo.svg" to="/" />}>
 *   <NavigationPanelItem title="Label" to="/" />
 *   <NavigationPanelItem title="Label" to="/" />
 * </NavigationPanel>
 * ```
 */
export function NavigationPanel(props: NavigationPanelProps) {
  const { children, title, logo, testId, className, ariaDescribedBy, ...restProps } = props;
  assertEmptyObject(restProps);

  const testIdAttribute = useTestIdAttribute();

  const menuRef = useRef<HTMLUListElement>(null);

  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);

    /* istanbul ignore next */
    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 (
    <StyledPanel aria-describedby={ariaDescribedBy} className={className} {...{ [testIdAttribute]: testId }}>
      {logo}
      <nav>
        <StyledNavHeader {...{ [testIdAttribute]: makeTestId(testId, 'header') }}>{title}</StyledNavHeader>
        <StyledMenu
          ref={menuRef}
          onKeyDown={handleKeyDown}
          role="menu"
          {...{ [testIdAttribute]: makeTestId(testId, 'menu') }}
        >
          {Children.map(children, (child) => (
            <li role="presentation" {...{ [testIdAttribute]: makeTestId(testId, 'item') }}>
              {child}
            </li>
          ))}
        </StyledMenu>
      </nav>
    </StyledPanel>
  );
}
