import React from "react";
import { BubbleContent, BubbleContentProps } from "./BubbleContent";
import { isChildOfType } from "../_util/is-child-of-type";
import { Button } from "../button";
import { useScheduleUpdate } from "../_util/use-schedule-update";
import { Input } from "../input";
import { Select } from "../select";
import { Popover, PopoverProps } from "../popover";
import { TextArea } from "../input/TextArea";

export interface BubbleProps
  extends Pick<
      PopoverProps,
      | "trigger"
      | "visible"
      | "onVisibleChange"
      | "placementOffset"
      | "escapeWithReference"
      | "flipped"
      | "closeOnScroll"
      | "destroyOnClose"
      | "openDelay"
      | "closeDelay"
      | "onExited"
      | "overlayClassName"
      | "overlayStyle"
      | "animationScaleFrom"
      | "transitionTimeout"
      | "updateOnDimensionChange"
      | "referenceElement"
      | "popupContainer"
      | "positionFixed"
    >,
    BubbleContentProps {
  /**
   * 气泡内容
   *
   * 如果气泡内容传入 `null` 或者 `undefined`，则不会出现气泡
   */
  content?: React.ReactNode;

  /**
   * 气泡箭头是否指向目标元素中心
   *
   * @default false
   * @since 2.2.0
   */
  arrowPointAtCenter?: boolean;

  /**
   * 触发更新位置的变量依赖列表，可用于性能优化
   *
   * **更新位置可防止气泡溢出屏幕或产生偏移**
   *
   * @default [content]
   */
  updateDeps?: any[];
}

export const Bubble = React.forwardRef(function Bubble(
  {
    visible,
    onVisibleChange,
    content,
    arrowPointAtCenter,
    trigger = "hover",
    overlayClassName,
    overlayStyle,
    openDelay,
    closeDelay,
    closeOnScroll,
    updateOnDimensionChange,
    transitionTimeout,
    escapeWithReference,
    referenceElement,
    popupContainer,
    destroyOnClose,
    placement = "top",
    placementOffset,
    animationScaleFrom,
    onExited,
    children,
    updateDeps = [content],
    flipped,
    positionFixed,
    ...bubbleProps
  }: BubbleProps,
  ref: React.Ref<HTMLDivElement>
) {
  // 内容变化时更新位置
  const scheduleUpdateRef = useScheduleUpdate(
    Array.isArray(updateDeps) ? updateDeps : [content]
  );

  // issue: https://github.com/facebook/react/issues/4251
  if (
    trigger !== "click" &&
    React.Children.count(children) === 1 &&
    (isChildOfType(children, "button") ||
      isChildOfType(children, "input") ||
      isChildOfType(children, "textarea") ||
      isChildOfType(children, "select") ||
      isChildOfType(children, Button) ||
      isChildOfType(children, Input) ||
      isChildOfType(children, TextArea) ||
      (isChildOfType(children, Select) &&
        children.props.type !== "simulate")) &&
    children.props.disabled
  ) {
    // eslint-disable-next-line no-param-reassign
    children = (
      <span
        style={{
          // eslint-disable-next-line dot-notation
          display: children.props["size"] === "full" ? "block" : "inline-block",
          cursor: "not-allowed",
        }}
      >
        {React.cloneElement(children as React.ReactElement, {
          style: {
            ...(children.props.style || {}),
            pointerEvents: "none",
          },
        })}
      </span>
    );
  }

  let offset = placementOffset ?? 10;
  if (arrowPointAtCenter) {
    offset = Array.isArray(offset) ? offset[1] : offset;
    const [, placementModifier] = placement.split("-");
    if (placementModifier === "start") {
      offset = ["50%-26", offset];
    } else if (placementModifier === "end") {
      offset = ["26-50%", offset];
    }
  }

  return (
    <Popover
      visible={visible}
      onVisibleChange={onVisibleChange}
      closeOnScroll={closeOnScroll}
      trigger={trigger}
      placement={placement}
      placementOffset={offset}
      flipped={flipped}
      positionFixed={positionFixed}
      animationScaleFrom={animationScaleFrom}
      referenceElement={referenceElement}
      overlay={({ scheduleUpdate, placement }) => {
        scheduleUpdateRef.current = scheduleUpdate;
        if (!content && content !== 0) {
          return null;
        }
        return (
          <BubbleContent ref={ref} {...bubbleProps} placement={placement}>
            {content}
          </BubbleContent>
        );
      }}
      overlayClassName={overlayClassName}
      overlayStyle={overlayStyle}
      openDelay={openDelay}
      closeDelay={closeDelay}
      updateOnDimensionChange={updateOnDimensionChange}
      transitionTimeout={transitionTimeout}
      escapeWithReference={escapeWithReference}
      popupContainer={popupContainer}
      destroyOnClose={destroyOnClose}
      onExited={onExited}
    >
      {children}
    </Popover>
  );
});

Bubble.displayName = "Bubble";
