import { Vue } from '../vue';
import { IS_JSDOM, HAS_WINDOW_SUPPORT } from '../constants/env';
import { setConfig } from './config-set';
import { warn } from './warn';

/**
 * Checks if there are multiple instances of Vue, and warns (once) about possible issues.
 * @param {object} Vue
 */
const checkMultipleVue = (() => {
  let checkMultipleVueWarned = false;
  const MULTIPLE_VUE_WARNING = ['Multiple instances of Vue detected!', 'You may need to set up an alias for Vue in your bundler config.', 'See: https://bootstrap-vue.org/docs#using-module-bundlers'].join('\n');
  return Vue$1 => {
    /* istanbul ignore next */
    if (!checkMultipleVueWarned && Vue !== Vue$1 && !IS_JSDOM) {
      warn(MULTIPLE_VUE_WARNING);
    }
    checkMultipleVueWarned = true;
  };
})();

/**
 * Plugin install factory function.
 * @param {object} { components, directives }
 * @returns {function} plugin install function
 */
const installFactory = function () {
  let {
    components,
    directives,
    plugins
  } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  const install = function (Vue) {
    let config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    if (install.installed) {
      /* istanbul ignore next */
      return;
    }
    install.installed = true;
    checkMultipleVue(Vue);
    setConfig(config, Vue);
    registerComponents(Vue, components);
    registerDirectives(Vue, directives);
    registerPlugins(Vue, plugins);
  };
  install.installed = false;
  return install;
};

/**
 * Plugin object factory function.
 * @param {object} { components, directives, plugins }
 * @returns {object} plugin install object
 */
const pluginFactory = function () {
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  let extend = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  return {
    ...extend,
    install: installFactory(options)
  };
};

/**
 * Load a group of plugins.
 * @param {object} Vue
 * @param {object} Plugin definitions
 */
const registerPlugins = function (Vue) {
  let plugins = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  for (const plugin in plugins) {
    if (plugin && plugins[plugin]) {
      Vue.use(plugins[plugin]);
    }
  }
};

/**
 * Load a component.
 * @param {object} Vue
 * @param {string} Component name
 * @param {object} Component definition
 */
const registerComponent = (Vue, name, def) => {
  if (Vue && name && def) {
    Vue.component(name, def);
  }
};

/**
 * Load a group of components.
 * @param {object} Vue
 * @param {object} Object of component definitions
 */
const registerComponents = function (Vue) {
  let components = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  for (const component in components) {
    registerComponent(Vue, component, components[component]);
  }
};

/**
 * Load a directive.
 * @param {object} Vue
 * @param {string} Directive name
 * @param {object} Directive definition
 */
const registerDirective = (Vue, name, def) => {
  if (Vue && name && def) {
    // Ensure that any leading V is removed from the
    // name, as Vue adds it automatically
    Vue.directive(name.replace(/^VB/, 'B'), def);
  }
};

/**
 * Load a group of directives.
 * @param {object} Vue
 * @param {object} Object of directive definitions
 */
const registerDirectives = function (Vue) {
  let directives = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  for (const directive in directives) {
    registerDirective(Vue, directive, directives[directive]);
  }
};

/**
 * Install plugin if window.Vue available
 * @param {object} Plugin definition
 */
const vueUse = VuePlugin => {
  /* istanbul ignore next */
  if (HAS_WINDOW_SUPPORT && window.Vue) {
    window.Vue.use(VuePlugin);
  }
  /* istanbul ignore next */
  if (HAS_WINDOW_SUPPORT && VuePlugin.NAME) {
    window[VuePlugin.NAME] = VuePlugin;
  }
};

export { checkMultipleVue, installFactory, pluginFactory, registerComponent, registerComponents, registerDirective, registerDirectives, registerPlugins, vueUse };
