import { App, Plugin } from 'vue';
import { IonicConfig, setupConfig } from '@ionic/core';
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
import { needsKebabCase } from './utils';

/**
* We need to make sure that the web component fires an event
* that will not conflict with the user's @ionChange binding,
* otherwise the binding's callback will fire before any
* v-model values have been updated.
*/
const toLowerCase = (eventName: string) => eventName === 'ionChange' ? 'v-ionchange' : eventName.toLowerCase();
const toKebabCase = (eventName: string) => eventName === 'ionChange' ? 'v-ion-change' : eventName.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();

/**
 * Vue 3.0.6 fixed a bug where events on custom elements
 * were always converted to lower case, so "ionRefresh"
 * became "ionRefresh". We need to account for the old
 * issue as well as the new behavior where "ionRefresh"
 * is converted to "ion-refresh".
 * See https://github.com/vuejs/vue-next/pull/2847
 */
const getHelperFunctions = (needsKebabCase: boolean = true) => {
  const conversionFn = (needsKebabCase) ? toKebabCase : toLowerCase;
  return {
      ael: (el: any, eventName: string, cb: any, opts: any) => el.addEventListener(conversionFn(eventName), cb, opts),
      rel: (el: any, eventName: string, cb: any, opts: any) => el.removeEventListener(conversionFn(eventName), cb, opts),
      ce: (eventName: string, opts: any) => new CustomEvent(conversionFn(eventName), opts)
  };
};

export const IonicVue: Plugin = {

  async install(app: App, config: IonicConfig = {}) {
    if (typeof (window as any) !== 'undefined') {
      const { ael, rel, ce } = getHelperFunctions(needsKebabCase(app.version));
      setupConfig({
        ...config,
        _ael: ael,
        _rel: rel,
      });
      await applyPolyfills();
      await defineCustomElements(window, {
        exclude: ['ion-tabs'],
        ce,
        ael,
        rel
      } as any);
    }
  }
};
