import { ref, reactive, computed, watch, onMounted, onActivated, nextTick } from 'vue';
import { computePosition, offset, flip, shift, arrow } from '@floating-ui/dom';
import { isBoolean, isFunction } from 'lodash-es';
import { useVModel } from '@vueuse/core';
import popupManager from '../_util/popupManager';
import getElementFromVueInstance from '../_util/getElementFromVueInstance';

const MAP = {
  bottom: 'up',
  top: 'down',
  left: 'right',
  right: 'left'
};
var usePopper = (props, emit) => {
  const visible = useVModel(props, 'modelValue', emit, {
    passive: props.passive
  });
  const updateVisible = val => {
    visible.value = val;
  };
  const virtualRect = ref(null);
  const triggerRef = ref();
  const popperRef = ref();
  const arrowRef = ref();
  const popperStyle = reactive({
    zIndex: popupManager.nextZIndex()
  });
  const placement = ref(props.placement);
  const cacheVisible = ref(true);
  const transitionName = computed(() => {
    const placementValue = placement.value;
    return `fes-slide-${MAP[placementValue.split('-')[0]]}`;
  });
  const computePopper = () => {
    if (isBoolean(props.disabled) && props.disabled) {
      return;
    }
    if (isFunction(props.disabled) && props.disabled()) {
      return;
    }
    if (!visible.value) {
      return;
    }
    popperStyle.zIndex = popupManager.nextZIndex();
    nextTick(() => {
      const rawTriggerEl = getElementFromVueInstance(triggerRef.value);
      const triggerRect = rawTriggerEl.getBoundingClientRect();
      // trigger 不可见的时候立即隐藏，允许误差
      if (triggerRect.width <= 1 && triggerRect.height <= 1) {
        updateVisible(false);
        return;
      }
      const triggerEl = props.trigger === 'contextmenu' // 仅在右键时，使用鼠标具体触发位置
      ? {
        getBoundingClientRect: () => virtualRect.value && {
          width: 0,
          height: 0,
          top: virtualRect.value.y,
          right: virtualRect.value.x,
          bottom: virtualRect.value.y,
          left: virtualRect.value.x
        },
        contextElement: rawTriggerEl
      } : rawTriggerEl;
      const popperEl = popperRef.value;
      computePosition(triggerEl, popperEl, {
        placement: props.placement,
        middleware: [offset(props.offset),
        // 当位置不够时切换到对面方向
        flip(),
        // 当无法完全显示时，自动调整主轴位置
        shift(), props.arrow && arrow({
          element: arrowRef.value
        })].filter(Boolean)
      }).then(state => {
        // 当方向改变时，动画需要重新执行
        if (placement.value !== state.placement) {
          cacheVisible.value = false;
          nextTick(() => {
            cacheVisible.value = true;
          });
          placement.value = state.placement;
          return;
        }
        placement.value = state.placement;
        Object.assign(popperEl.style, {
          left: `${state.x}px`,
          top: `${state.y}px`
        });
        if (props.arrow) {
          // Accessing the data
          const {
            x: arrowX,
            y: arrowY
          } = state.middlewareData.arrow;
          const staticSide = {
            top: 'bottom',
            right: 'left',
            bottom: 'top',
            left: 'right'
          }[state.placement.split('-')[0]];
          Object.assign(arrowRef.value.style, {
            left: arrowX != null ? `${arrowX}px` : '',
            top: arrowY != null ? `${arrowY}px` : '',
            right: '',
            bottom: '',
            [staticSide]: '-3px'
          });
        }
      });
    });
  };
  const updateVirtualRect = value => {
    virtualRect.value = value;
  };
  watch(virtualRect, computePopper);
  onMounted(computePopper);
  onActivated(computePopper);
  return {
    visible,
    updateVisible,
    triggerRef,
    popperRef,
    arrowRef,
    popperStyle,
    computePopper,
    updateVirtualRect,
    cacheVisible,
    transitionName
  };
};

export { usePopper as default };
