import Vue, { VNode, CreateElement } from 'vue';
import { createPopper, Instance } from '@popperjs/core/lib/popper-lite.js';
import preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow.js';
import flip from '@popperjs/core/lib/modifiers/flip.js';
import arrow from '@popperjs/core/lib/modifiers/arrow.js';
import offset from '@popperjs/core/lib/modifiers/offset.js';
import { Placement } from '@popperjs/core/lib';
import { isFunc } from 'qx-util';
import { AppServiceBase } from '@ibizstudio-ex/runtime';
import './ibz-tooltip.less';

interface Store {
  getters: {
    getZIndex: Function;
  };
  commit: Function;
}

/**
 * tooltip工具类
 *
 * @export
 * @class IBzTooltipUtil
 */
export class IBzTooltipUtil {
  /**
   * Prop承载容器
   *
   * @private
   * @type {HTMLDivElement}
   * @memberof IBzTooltipUtil
   */
  private container: HTMLDivElement = document.createElement('div');

  /**
   * vue实例
   *
   * @private
   * @type {Vue}
   * @memberof IBzTooltipUtil
   */
  private vueExample!: Vue;

  /**
   * 飘窗实例
   *
   * @private
   * @type {Instance}
   * @memberof IBzTooltipUtil
   */
  private popperExample?: Instance;

  /**
   * 是否显示tooltip
   *
   * @private
   * @memberof IBzTooltipUtil
   */
  private showTip = false;

  /**
   * 显示层级
   *
   * @private
   * @memberof IBzTooltipUtil
   */
  private zIndex = 100;

  /**
   * 提示控制器
   *
   * @private
   * @type {*}
   * @memberof IBzTooltipUtil
   */
  private c: any;

  /**
   * 鼠标是否悬浮
   *
   * @memberof IBzTooltipUtil
   */
  isHover = false;

  /**
   * 定制器
   *
   * @type {*}
   * @memberof IBzTooltipUtil
   */
  timer: any = null;

  /**
   * Creates an instance of IBzTooltipUtil.
   * @param {*} c
   * @memberof IBzTooltipUtil
   */
  constructor(c: any) {
    this.c = c;
    const fn = offset.fn;
    offset.fn = (data: any) => {
      if (data) {
        Object.assign(data.options, { offset: [4, 4] });
      }
      fn(data);
    };
    this.initStore();
  }

  /**
   * 鼠标移入
   *
   * @memberof IBzTooltipUtil
   */
  private mouseenter(): void {
    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = null;
    }
    this.isHover = true;
  }

  /**
   * 鼠标移出
   *
   * @memberof IBzTooltipUtil
   */
  private mouseleave(): void {
    this.isHover = false;
    this.timer = setTimeout(() => {
      if (!this.c.isHover) {
        this.popperDestroy();
      }
    }, 50);
  }

  /**
   * Vue状态管理器
   *
   * @type {Store}
   * @memberof IBzTooltipUtil
   */
  store: Store | null = null;

  /**
   * 初始化Vue状态管理器
   *
   * @memberof IBzTooltipUtil
   */
  private initStore() {
    const appService = AppServiceBase.getInstance();
    this.store = appService.getAppStore();
  }

  /**
   * 初始化vue实例
   *
   * @private
   * @returns {void}
   * @memberof IBzTooltipUtil
   */
  private initVueExample(): void {
    const self = this;
    if (!self.store) {
      self.initStore();
    }
    self.container.className = 'ibz-tooltip-wrapper';
    const div = document.createElement('div');
    self.container.appendChild(div);
    document.body.appendChild(self.container);
    this.vueExample = new Vue({
      el: div,
      data: {
        content: null,
        renderContent: null,
      },
      methods: {
        click(e: MouseEvent) {
          e.stopPropagation();
        },
      },
      render(h: CreateElement) {
        let content: any;
        if (this.renderContent) {
          content = (this.renderContent as any)(h);
        } else if (this.content) {
          content = <div class='content'>{this.content}</div>;
        }
        return (
          <div class='ibz-tooltip' style={{ 'z-index': self.zIndex }} on-click={this.click} on-mouseenter={() => self.mouseenter()} on-mouseleave={() => self.mouseleave()}>
            <div ref='arrow' class='ibz-tooltip-arrow' data-popper-arrow></div>
            {content}
          </div>
        );
      },
    });
  }

  /**
   * 打开提示框
   *
   * @param {Element} el
   * @param {((h: CreateElement) => any | string | VNode | VNode[])} [content]
   * @param {Placement} [position='auto']
   * @param {('local' | 'async')} [mode='local'] 提示框呈现模式，本地模式 | 异步模式。异步模式需要请求呈现
   * @param {*} [params]
   * @memberof IBzTooltipUtil
   */
  public openPopover(el: Element, content?: (h: CreateElement) => any | string | VNode | VNode[], position: Placement = 'auto', mode: 'local' | 'async' = 'local', params?: any): void {
    if (!this.vueExample) {
      this.initVueExample();
      setTimeout(() => {
        this.openPopover(el, content, position, mode, params);
      }, 100);
      return;
    }
    this.popperDestroy();
    const zIndex = this.store?.getters.getZIndex();
    if (zIndex) {
      this.zIndex = zIndex + 1;
      this.store?.commit('upZIndex');
    }
    // 更新vue实例内容
    this.showTip = true;
    if (isFunc(content)) {
      Object.assign(this.vueExample.$data, { content: null, renderContent: content });
    } else {
      Object.assign(this.vueExample.$data, { content, renderContent: null });
    }
    const vueRef: any = this.vueExample.$el;
    this.popperExample = createPopper(el, vueRef, {
      placement: position,
      modifiers: [preventOverflow, flip, arrow, offset],
    });
    this.vueExample.$forceUpdate();
  }

  /**
   * 销毁提示框
   *
   * @memberof IBzTooltipUtil
   */
  public popperDestroy(): void {
    if (this.popperExample) {
      this.popperExample.destroy();
      this.popperExample = undefined;
      this.showTip = false;
      this.vueExample.$forceUpdate();
    }
  }

  /**
   * tip重新计算位置
   *
   * @memberof IBzTooltipUtil
   */
  popperTick(): void {
    this.popperExample?.forceUpdate();
  }
}
