<template>

  <div class="itf-popover-container">
    <slot name="activator" :on="{ click: show }"></slot>

    <div class="itf-popover itf-append-context" ref="popover">
      <slot></slot>
    </div>
  </div>

</template>
<script>
import {
  Vue, Component, Prop, Watch, PropSync,
} from 'vue-property-decorator';

let globalModalIndex = 0; // base modal z-index

export default @Component({
  name: 'itfPopover',
  components: {
  },
})
class itfPopover extends Vue {
  @PropSync('visible') value;

  @Prop({ type: String, default: 'bottom', validator: (value) => ['bottom', 'left', 'right', 'top', 'auto'].includes(value) }) placement;

  @Prop({ type: String, default: 'click', validator: (value) => ['click', 'focus', 'hover', 'manual'].includes(value) }) trigger;

  @Prop({ type: String, default: '' }) customClass;
  @Prop({ type: Boolean }) appendToBody;
  @Prop({ type: Boolean }) appendToContext;
  @Prop({ type: String }) locator;
  @Prop({ type: Boolean, default: false }) isActivatorHighlighted;

  modalId = '';
  modalEl = null;
  targetEl = null;

  ignoreUpdate = false; // потрібно щоб не оновлювалось visible декілька разів, бо буде баг коли вікно відкривається і одразу закривається

  initTimeout = null;

  beforeDestroy() {
    this.destroy();
  }

  @Watch('value')
  onVisibleChanged(newValue, oldValue) {
    if (!this.modalEl || this.ignoreUpdate) {
      return;
    }
    if (newValue) {
      this.modalEl?.show();
    } else {
      this.modalEl?.hide();
    }
  }

  @Watch('locator')
  onLocatorChanged(newValue, oldValue) {
    if (newValue !== oldValue && newValue && this.modalEl) {
      this.initPopover();
    }
  }

  @Watch('isActivatorHighlighted')
  onIsActivatorHighlightedChanged(newValue, oldValue) {
    if (!this.targetEl) return;
    if (newValue) {
      this.targetEl.classList.add('pulsing');
    } else {
      this.targetEl.classList.remove('pulsing');
    }
  }

  created() {
    globalModalIndex += 1;
    this.modalId = `popoverId${globalModalIndex}`;
  }

  async mounted() {
    this.refresh(100);
  }

  destroy() {
    if (this.initTimeout) {
      clearTimeout(this.initTimeout);
      this.initTimeout = null;
    }
    if (this.modalEl) {
      this.modalEl.dispose();
      this.modalEl = null;
    }
    if (this.targetEl) this.resetTargetEl();
  }

  refresh(delay = 300) {
    this.destroy();

    if (this.initTimeout) clearTimeout(this.initTimeout);
    this.initTimeout = setTimeout(() => this.initPopover(), delay);
  }

  async initPopover() {
    if (typeof window === 'undefined') {
      return;
    }

    // Remove listeners from the previous target element before finding the new one
    if (this.modalEl) this.destroy();

    // Determine target
    if (this.locator) {
      this.targetEl = document.querySelector(this.locator);
      if (!this.targetEl) return;
    } else {
      this.targetEl = this.$el;
    }

    if (this.isActivatorHighlighted) this.targetEl.classList.add('pulsing');

    const el = this.$refs.popover || "";
    const { default: Popover } = await import('bootstrap/js/src/popover');

    if (this._isDestroyed) return;

    let context = document.body;
    if (this.appendToContext && this.targetEl instanceof Node && this.targetEl.parentNode) {
      context = this.targetEl.closest('.itf-append-context') || document.body;
      this.targetEl.parentNode.removeChild(this.targetEl);
      context.appendChild(this.targetEl); // should append only to body
    } else if (this.appendToBody && this.targetEl instanceof Node && this.targetEl.parentNode) {
      this.targetEl.parentNode.removeChild(this.targetEl);
      context.appendChild(this.targetEl); // should append only to body
    }

    this.modalEl = new Popover(this.targetEl, {
      content: el,
      container: context,
      sanitize: false,
      html: true,
      customClass: this.customClass,
      placement: this.placement,
      trigger: this.trigger,
    });

    this.onVisibleChanged(this.value);
    this.targetEl.addEventListener('shown.bs.popover', this.handleShown);
    this.targetEl.addEventListener('hidden.bs.popover', this.handleHidden);
  }

  resetTargetEl() {
    this.targetEl.removeEventListener('shown.bs.popover', this.handleShown);
    this.targetEl.removeEventListener('hidden.bs.popover', this.handleHidden);
    this.targetEl.classList.remove('pulsing');
    this.targetEl = null;
  }

  handleShown() {
    this.ignoreUpdate = true;
    this.value = true;
    document.body.addEventListener('click', this.clickOutside);
    this.$nextTick(() => this.ignoreUpdate = false);
  }

  handleHidden() {
    this.ignoreUpdate = true;
    this.value = false;
    document.body.removeEventListener('click', this.clickOutside);
    this.$nextTick(() => this.ignoreUpdate = false);
  }

  clickOutside(e) {
    if (this.trigger !== 'click' || !e.target) {
      return;
    }
    if (this.targetEl !== e.target && e.target.closest('.itf-popover') !== this.$refs.popover) {
      this.hide();
    }
  }

  show() {
    if (this.modalEl) {
      this.modalEl.show();
    }
  }

  hide() {
    if (this.modalEl) {
      // треба затримка щоб попав не показався знову через отримання фокусу
      setTimeout(() => this.modalEl?.hide(), 10);
    }
  }
}
</script>
