import { isFunction } from 'lodash';
import React, { useEffect, useRef, useState } from 'react';
import { BoxSpace, SilkeBox } from '../silke-box';
import { ButtonContext } from '../silke-button';
import { SilkePopover, SilkePopoverProps } from '../silke-popover';
import { usePopoverRequestClose } from '../silke-popover/popover-context';
import { SilkeContextMenuItem, SilkeContextMenuItemProps } from './silke-context-menu-item';

import { SilkeDivider } from '../silke-divider';
import styles from './silke-context-menu.scss';
import { SilkeTag } from '../silke-tag';

export type SilkeContextMenuItems =
  | (SilkeContextMenuItemProps | React.ReactElement | 'divider')[]
  | (() => (SilkeContextMenuItemProps | React.ReactElement | 'divider')[]);

export type SilkeContextMenuProps = {
  items: SilkeContextMenuItems;
  width?: number;
  trigger?: 'contextmenu' | 'click' | 'mousedown';
  target?: React.RefObject<HTMLElement> | React.MutableRefObject<HTMLElement | undefined>;
  /** Used internally to keep track of menu and closing */
  subMenu?: boolean;
  gap?: BoxSpace;
  beta?: boolean;
  title?: string;
} & Omit<SilkePopoverProps, 'children' | 'kind'>;

export function SilkeContextMenu({
  items,
  target,
  trigger,
  anchor,
  width,
  subMenu,
  gap,
  onRequestClose,
  beta,
  title = '',
  ...rest
}: SilkeContextMenuProps) {
  const [showContextMenu, setShowContextMenu] = useState(false);
  const [pos, setPos] = React.useState<[number, number]>();
  const boxRef = useRef<HTMLElement>(null);

  const requestCloseSub = usePopoverRequestClose('contextmenu.sub');
  useEffect(() => {
    const targetEl = target?.current;
    if (targetEl?.addEventListener) {
      const handelContextMenu = (e: MouseEvent) => {
        e.preventDefault();
        setPos([e.pageX, e.pageY]);
        setShowContextMenu(true);
      };
      targetEl.addEventListener(trigger || 'contextmenu', handelContextMenu);
      return () => {
        targetEl.removeEventListener(trigger || 'contextmenu', handelContextMenu);
      };
    }
  }, [trigger, target]);

  if (target && !showContextMenu) return null;

  return (
    <SilkePopover
      contextId="contextmenu"
      targetOrigin="top-left"
      anchorOrigin="top-left"
      anchor={anchor || pos}
      onRequestClose={() => {
        if (showContextMenu) setShowContextMenu(false);
        onRequestClose?.();
      }}
      {...rest}
    >
      <ButtonContext size="base">
        {items && (
          <SilkeBox
            tabIndex={-1}
            column
            className={styles.menu}
            style={{ width }}
            bg="neutral-10"
            ref={boxRef}
            shadow="level4"
            onMouseDown={(e: React.MouseEvent) => e.stopPropagation()}
            vPad="xs"
            gap={gap}
            onKeyDown={(e: React.KeyboardEvent) => {
              if (e.key === 'ArrowLeft' && !subMenu) {
                e.stopPropagation();
                e.preventDefault();
                requestCloseSub();
              }
              if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
                e.stopPropagation();
                e.preventDefault();
                let index = -1;
                const buttons = e.currentTarget.querySelectorAll('button');
                buttons.forEach((b, i) => {
                  if (document.activeElement === b) index = i;
                });
                index += e.key === 'ArrowDown' ? 1 : -1;
                if (index >= buttons.length) index = 0;
                else if (index < 0) index = buttons.length - 1;
                buttons[index].focus();
              }
            }}
          >
            {(beta || !!title) && (
              <SilkeBox pad="xs" gap="l">
                <SilkeBox hPad="s" className={[styles.item, styles.title].join(' ')}>
                  {title}
                </SilkeBox>
                {beta && (
                  <SilkeBox hPad="xs">
                    <SilkeTag
                      color="dark"
                      label="Beta"
                      size="tiny"
                      style={{ alignSelf: 'center' }}
                    />
                  </SilkeBox>
                )}
              </SilkeBox>
            )}
            {(isFunction(items) ? items() : items)?.map((item, index) => {
              return !item ? null : React.isValidElement(item) ? (
                <React.Fragment key={index}>{item}</React.Fragment>
              ) : item === 'divider' ? (
                <SilkeBox key={index} vPad="xs" column>
                  <SilkeDivider dark />
                </SilkeBox>
              ) : (
                <SilkeContextMenuItem
                  key={index}
                  subItem={subMenu}
                  width={width}
                  {...(item as SilkeContextMenuItemProps)}
                  onClick={(e) => {
                    if (showContextMenu) setShowContextMenu(false);
                    onRequestClose?.();
                    (item as SilkeContextMenuItemProps).onClick?.(e);
                  }}
                />
              );
            })}
          </SilkeBox>
        )}
      </ButtonContext>
    </SilkePopover>
  );
}
