import * as React from 'react';

import Positioner, { Position } from '../Positioner/index.jsx';
import { func, node, number } from 'prop-types';

import MenuDivider from './MenuDivider';
import MenuItem from './MenuItem';
import MenuLabel from './MenuLabel';
import MenuSidelevel from './MenuSidelevel';
import MenuSublevel from './MenuSublevel';
import { Stack } from '../Layout';
import flatMap from 'lodash/flatMap';
import useControlProp from '../../hooks/useControlProp';
import { useHoverContext } from '../Popover/hoverContext';
import useOnOutsideClick from '../../hooks/useOnOutsideClick';

const HoverPositioner = ({
  targetRef,
  contentRef,
  sidelevel,
  sidelevelPosition,
  childArray,
  ...rest
}) => (
  <Positioner
    targetOffset={11}
    targetRef={targetRef}
    contentRef={contentRef}
    shown={!!sidelevel}
    position={sidelevelPosition}
    renderContent={({ ref, style }) => (
      <span ref={ref} style={{ ...style, transform: 'translateY(calc(50% - 24px))' }} {...rest}>
        <Stack width="fit-content" gap="none">
          {childArray[sidelevel] && sidelevel && (
            <React.Fragment>
              {childArray[sidelevel].props.showLevelLabel && (
                <MenuLabel>{childArray[sidelevel].props.label}</MenuLabel>
              )}
              {childArray[sidelevel].props.children}
            </React.Fragment>
          )}
        </Stack>
      </span>
    )}
  />
);

const Menu = ({
  children,
  sublevel: sublevelProp,
  onSublevelChange,
  sidelevelPosition = Position.RIGHT,
}) => {
  const [sublevel, setSublevel] = useControlProp(sublevelProp, onSublevelChange, null);
  const [sidelevel, setSidelevel] = React.useState(null);
  const childArray = flatMap(React.Children.toArray(children), (child) =>
    child.type === React.Fragment ? React.Children.toArray(child.props.children) : child,
  );

  const buttonRef = React.useRef();
  const menuRef = React.useRef();
  const popoverHoverRef = useHoverContext();

  function resetLevels() {
    setSidelevel(null);
    setSublevel(null);
  }

  useOnOutsideClick([buttonRef, menuRef, popoverHoverRef], resetLevels, sidelevel || sublevel);

  function getType(child, i) {
    switch (child.type) {
      case MenuSidelevel:
        return React.cloneElement(child, {
          active: sidelevel === i,
          onSelect: (e) => {
            setSidelevel(sidelevel === i ? null : i);
            buttonRef.current = e.target;
          },
        });
      case MenuSublevel:
        return React.cloneElement(child, {
          onSelect: () => setSublevel(i),
        });
      default:
        return React.cloneElement(child);
    }
  }

  return (
    <React.Fragment>
      <span ref={menuRef}>
        {sublevel === null &&
          childArray.map((child, i) => {
            return <span key={i.toString()}>{getType(child, i)}</span>;
          })}
        {sublevel && (
          <React.Fragment>
            {childArray[sublevel].props.showLevelLabel && (
              <MenuLabel>{childArray[sublevel].props.label}</MenuLabel>
            )}
            {childArray[sublevel].props.children}
          </React.Fragment>
        )}
      </span>
      <HoverPositioner
        targetRef={buttonRef}
        contentRef={popoverHoverRef || menuRef}
        sidelevel={sidelevel}
        sidelevelPosition={sidelevelPosition}
        childArray={childArray}
      />
    </React.Fragment>
  );
};

Menu.propTypes = {
  children: node.isRequired,
  sublevel: number,
  onSublevelChange: func,
};

Menu.Item = MenuItem;
Menu.Label = MenuLabel;
Menu.Sublevel = MenuSublevel;
Menu.SideLevel = MenuSidelevel;
Menu.Divider = MenuDivider;

export default Menu;
