import { ReferenceObject } from "popper.js";
import React, { RefObject, HTMLProps } from "react";
import { isMobile } from "../_util/is-mobile";
import { useOutsideClick } from "../_util/use-outside-click";

export type Trigger = (props: TriggerProps) => JSX.Element;
export type TriggerWithProps<P = any> = [
  (triggerProps: P & TriggerProps) => JSX.Element,
  P
];

export interface TriggerProps {
  overlayElementRef: RefObject<HTMLElement>;
  childrenElementRef: RefObject<HTMLElement>;
  visible: boolean;
  setVisible: (visible: boolean, delay?: number) => Promise<boolean>;
  openDelay: number;
  closeDelay: number;
  render: (renderProps: TriggerRenderProps) => JSX.Element;
}

export interface TriggerRenderProps {
  overlayProps: HTMLProps<HTMLElement>;
  childrenProps: HTMLProps<HTMLElement>;
  referenceElement?: ReferenceObject;
}

export const buildinTriggers = {
  click: ClickTrigger as Trigger,
  hover: HoverTrigger as Trigger,
  focus: FocusTrigger as Trigger,
  empty: EmptyTrigger as Trigger,
  contextMenu: ContextMenuTrigger as Trigger,
};

/**
 * 点击的时候打开
 *
 * - children 上绑定 onClick 事件，点击时打开
 * - children 上添加 useClickOutside Hook，外部点击时关闭，并排除 overlayElement
 */
export function ClickTrigger({
  childrenElementRef,
  overlayElementRef,
  visible,
  setVisible,
  openDelay = 0,
  closeDelay = 0,
  render,
}: TriggerProps) {
  const { listen, ignoreProps } = useOutsideClick([
    childrenElementRef,
    overlayElementRef,
  ]);

  listen(() => visible && setVisible(false, closeDelay));

  return render({
    overlayProps: ignoreProps,
    childrenProps: {
      onClick: () => setVisible(!visible, openDelay),
    },
  });
}

ClickTrigger.displayName = "ClickTrigger";

/**
 * 右键点击的时候打开
 *
 * - children 上绑定 onContextMenu 事件，点击时打开
 * - children 上添加 useClickOutside Hook，外部点击时关闭，并排除 overlayElement
 */
export function ContextMenuTrigger({
  childrenElementRef,
  overlayElementRef,
  visible,
  setVisible,
  openDelay = 0,
  closeDelay = 0,
  render,
}: TriggerProps) {
  const { listen, ignoreProps } = useOutsideClick([
    childrenElementRef,
    overlayElementRef,
  ]);

  listen(() => visible && setVisible(false, closeDelay));

  return render({
    overlayProps: ignoreProps,
    childrenProps: {
      onContextMenu: e => {
        e.preventDefault();
        setVisible(!visible, openDelay);
      },
    },
  });
}

ContextMenuTrigger.displayName = "ContextMenuTrigger";

/**
 * 鼠标经过的时候打开
 */
export function HoverTrigger(props: TriggerProps) {
  if (isMobile) {
    return <ClickTrigger {...props} />;
  }

  const { setVisible, openDelay = 50, closeDelay = 100, render } = props;
  const commonProps: HTMLProps<HTMLElement> = {
    onMouseEnter: () => setVisible(true, openDelay),
    onMouseLeave: () => setVisible(false, closeDelay),
  };

  return render({
    overlayProps: commonProps,
    childrenProps: commonProps,
  });
}

HoverTrigger.displayName = "HoverTrigger";

export function FocusTrigger({
  setVisible,
  openDelay = 50,
  closeDelay = 100,
  render,
}: TriggerProps) {
  const commonProps: HTMLProps<HTMLElement> = {
    onFocus: () => setVisible(true, openDelay),
    onBlur: () => setVisible(false, closeDelay),
  };
  return render({
    overlayProps: { ...commonProps, tabIndex: 1000 },
    childrenProps: commonProps,
  });
}

FocusTrigger.displayName = "FocusTrigger";

/**
 * 表示空的触发交互
 */
export function EmptyTrigger(props: TriggerProps) {
  return props.render({ overlayProps: {}, childrenProps: {} });
}

EmptyTrigger.displayName = "EmptyTrigger";
