/* eslint-disable @typescript-eslint/no-explicit-any */
import { arrow, computePosition, flip, offset, shift } from '@floating-ui/dom';
import { IPopoverOptions } from '@ibiz-template/runtime';
import { useNamespace } from '@ibiz-template/vue-util';
import {
  computed,
  CreateElement,
  defineComponent,
  onUnmounted,
  PropType,
  ref,
  VNode,
} from 'vue';
import '@ibiz-template/theme/style/components/util/popover/popover.scss';
import { OverlayPopoverContainer } from '../overlay-popover-container/overlay-popover-container';

/**
 * 计算飘窗显示
 *
 * @author chitanda
 * @date 2022-11-08 21:11:18
 * @param {HTMLElement} element
 * @param {HTMLElement} el
 * @param {HTMLElement} arrEl
 * @param {IPopoverOptions} opts
 * @return {*}  {Promise<void>}
 */
async function computePos(
  element: HTMLElement,
  el: HTMLElement,
  arrEl: HTMLElement,
  opts: IPopoverOptions,
): Promise<void> {
  const middlewareArr = [offset(opts.offsetOpts || 6), flip(), shift()];
  if (!opts.noArrow) {
    middlewareArr.push(arrow({ element: arrEl! }));
  }
  const options = await computePosition(element, el, {
    placement: opts.placement,
    strategy: 'absolute',
    middleware: middlewareArr,
  });
  {
    const { x, y, placement, middlewareData } = options;
    const { style } = el;
    style.left = `${x}px`;
    style.top = `${y}px`;

    if (!opts.noArrow) {
      // 箭头位置
      const { x: arrowX, y: arrowY } = middlewareData.arrow!;

      const staticSide: any = {
        top: 'bottom',
        right: 'left',
        bottom: 'top',
        left: 'right',
      }[placement.split('-')[0]];

      Object.assign(arrEl.style, {
        left: arrowX != null ? `${arrowX}px` : '',
        top: arrowY != null ? `${arrowY}px` : '',
        right: '',
        bottom: '',
        [staticSide]: '-4px',
      });
    }
  }
}

const AppPopoverComponent = defineComponent({
  props: {
    opts: {
      type: Object as PropType<IPopoverOptions>,
      default: () => ({}),
    },
  },
  setup(props, ctx) {
    // 样式命名空间
    const ns = useNamespace('popover');
    // 是否显示
    const isShow = ref(false);
    // 是否悬浮在内容区，当悬浮在内容区时 autoClose 禁用
    const isHover = ref(false);
    // 跟 dom 元素
    const el = ref<HTMLDivElement>();
    // arrow dom 元素
    const arrEl = ref<HTMLDivElement>();
    // 是否可以自动关闭
    const autoClose = computed<boolean>(() => {
      return props.opts.autoClose === true && isHover.value === false;
    });

    // 鼠标入飘窗内容区
    function onMouseenter(e: MouseEvent): void {
      e.stopPropagation();
      isHover.value = true;
    }

    // 鼠标出飘窗内容区
    function onMouseleave(e: MouseEvent): void {
      e.stopPropagation();
      isHover.value = false;
    }

    // 点击容器关闭飘窗
    function dismiss(): void {
      ctx.emit('dismiss');
    }

    // 组件销毁用于事件触发
    function destroy(e: Event): void {
      e.stopPropagation();
      if (autoClose.value) {
        dismiss();
      }
    }

    function addEvents(): void {
      window.addEventListener('mousedown', destroy, { capture: true });
      window.addEventListener('blur', destroy, { capture: true });
      window.addEventListener('resize', destroy, { capture: true });
    }

    function removeEvents(): void {
      window.removeEventListener('mousedown', destroy, { capture: true });
      window.removeEventListener('blur', destroy, { capture: true });
      window.removeEventListener('resize', destroy, { capture: true });
    }

    addEvents();

    onUnmounted(() => {
      removeEvents();
    });

    /**
     * 飘窗显示并计算位置
     *
     * @author chitanda
     * @date 2022-11-09 12:11:04
     * @param {HTMLElement} target
     * @return {*}  {Promise<void>}
     */
    async function present(target: HTMLElement): Promise<void> {
      isShow.value = true;
      await computePos(target, el.value!, arrEl.value!, props.opts);
    }

    return {
      ns,
      el,
      arrEl,
      isShow,
      isHover,
      present,
      dismiss,
      onMouseenter,
      onMouseleave,
    };
  },
  render() {
    return (
      <div
        class={[
          this.ns.b(),
          this.ns.is('show', this.isShow),
          this.ns.is('hover', this.isHover),
        ]}
        ref='el'
        onMouseenter={this.onMouseenter}
        onMouseleave={this.onMouseleave}
      >
        {!this.opts.noArrow && (
          <div class={[this.ns.e('arrow')]} ref='arrEl'></div>
        )}
        {this.$slots.default}
      </div>
    );
  },
});

/**
 * 创建飘窗
 *
 * @author chitanda
 * @date 2022-11-09 15:11:08
 * @export
 * @param {(_h: CreateElement) => VNode} render
 * @param {IPopoverOptions} [opts]
 * @return {*}  {OverlayPopoverContainer}
 */
export function createPopover(
  render: (_h: CreateElement) => VNode,
  opts?: IPopoverOptions,
): OverlayPopoverContainer {
  return new OverlayPopoverContainer(AppPopoverComponent, render, opts);
}
