import { generateGuid } from '@vev/utils';
import { isBoolean, isFunction } from 'lodash';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { SilkeBox } from '../silke-box';
import { ButtonContext, SilkeButton, SilkeButtonProps } from '../silke-button';
import { SilkePopover, SilkePopoverProps } from '../silke-popover';
import styles from './silke-overflow-menu.scss';
import { SilkeDivider } from '../silke-divider';

export type SilkeOverflowMenuProps = {
  children?: React.ReactNode;
  open?: boolean;
  /**
   * What triggers the opening of the overflow menu
   * @default "click"
   */
  trigger?: 'click' | 'right-click';
  onToggleOpen?: (open: boolean) => void;
  /** Pass items or children */
  items?:
    | (SilkeOverflowMenuItem | React.ReactElement | 'divider')[]
    | (() => (SilkeOverflowMenuItem | React.ReactElement | 'divider')[]);
} & SilkeButtonProps &
  Omit<SilkePopoverProps, 'anchor' | 'hide' | 'children' | 'kind'>;

export function SilkeOverflowMenu({
  children,
  label,
  open: externalOpen,
  icon,
  items = [],
  offsetX,
  offsetY,
  trigger = 'click',
  targetOrigin = 'top-right',
  anchorOrigin = 'bottom-right',
  onClick,
  onMouseDown,
  onToggleOpen,
  ...rest
}: SilkeOverflowMenuProps) {
  const [open, setOpen] = useState(false);
  const ref = useRef<HTMLElement>(null);
  if (!label && !icon) icon = 'menuMore';
  const isOpen = isBoolean(externalOpen) ? externalOpen : open;
  const buttonId = useMemo(() => {
    return 'button-' + generateGuid();
  }, []);

  const handleToggleOpen = useCallback(
    (e: any) => {
      e?.stopPropagation();
      if (onMouseDown) onMouseDown(e);
      if (isBoolean(externalOpen) && onToggleOpen) {
        // Handle open/close externally
        onToggleOpen(!isOpen);
      } else if (
        isOpen ||
        (trigger === 'click' && !e?.button) ||
        (trigger === 'right-click' && e?.button === 2)
      ) {
        // Handle open/close internally
        setOpen(!isOpen);
      }
    },
    [externalOpen, trigger, isOpen, onToggleOpen],
  );

  const boxRef = useRef<HTMLDivElement>(null);

  if (anchorOrigin?.includes('top')) offsetY = (offsetY || 0) - 1;
  else offsetY = (offsetY || 0) + 1;

  return (
    <>
      <SilkeButton
        className={isOpen ? styles.btnOpen : undefined}
        ref={ref}
        label={label}
        kind="ghost"
        icon={icon}
        id={buttonId}
        aria-expanded={isOpen}
        aria-label="Menu"
        aria-haspopup="true"
        onMouseDown={handleToggleOpen}
        onContextMenu={trigger === 'right-click' ? (e) => e.preventDefault() : undefined}
        onClick={(e) => {
          e.stopPropagation();
          e.preventDefault();
          if (onClick) onClick(e);
          // Ugly hack to separate mouse and keyboard events
          if (!e.screenX && !e.screenY) handleToggleOpen(e);
        }}
        {...rest}
      />
      {isOpen && (
        <SilkePopover
          contextId="silke.overflow.menu"
          anchor={ref}
          offsetX={offsetX}
          offsetY={offsetY}
          targetOrigin={targetOrigin}
          anchorOrigin={anchorOrigin}
          onRequestClose={handleToggleOpen}
        >
          <ButtonContext size="base">
            {(isFunction(items) || items?.length > 0) && (
              <SilkeBox
                tabIndex={-1}
                column
                pad="xs"
                bg="neutral-10"
                shadow="level2"
                className={styles.menu}
                onClick={() => setOpen(false)}
                ref={boxRef}
                aria-labelledby={buttonId}
              >
                {(isFunction(items) ? items() : items)?.map((item, index) => {
                  return React.isValidElement(item) ? (
                    item
                  ) : item === 'divider' ? (
                    <SilkeDivider key={index} dark />
                  ) : (
                    <SilkeOverflowMenuItem
                      key={index}
                      size={rest.size}
                      {...(item as SilkeOverflowMenuItem)}
                      onClick={(e) => {
                        e.stopPropagation();
                        (item as SilkeOverflowMenuItem).onClick?.(e);
                        handleToggleOpen(e);
                      }}
                    />
                  );
                })}
              </SilkeBox>
            )}
            {children && (
              <div ref={boxRef} tabIndex={-1} aria-labelledby={buttonId}>
                {children}
              </div>
            )}
          </ButtonContext>
        </SilkePopover>
      )}
    </>
  );
}

export type SilkeOverflowMenuItem = {
  label: React.ReactNode;
  kind?: 'default' | 'danger' | 'selected';
  groupLabel?: string;
} & Omit<SilkeButtonProps, 'height' | 'width' | 'align' | 'vAlign' | 'hAlign' | 'kind'>;

export const SilkeOverflowMenuItem = React.forwardRef<HTMLButtonElement, SilkeOverflowMenuItem>(
  ({ label, kind, groupLabel: _groupLabel, ...rest }, ref) => (
    <SilkeButton
      ref={ref as any}
      label={label}
      style={kind === 'selected' ? { backgroundColor: '#323232' } : undefined}
      kind={kind === 'danger' ? 'danger' : 'ghost'}
      rounded={rest.size === 's' ? 'tiny' : 'small'}
      {...rest}
    />
  ),
);

SilkeOverflowMenuItem.displayName = 'SilkeOverflowMenuItem';
