/**
 * This library is deprecated and scheduled to be removed once the
 * filtered_search component is replaced with GitLab's new Pajamas
 * filter vue component.
 *
 * The documentation has been removed from the gitlab codebase but
 * can still be found in the commit history here:
 * https://gitlab.com/gitlab-org/gitlab/-/blob/28f20e28/doc/development/fe_guide/droplab/droplab.md
 */

import { DATA_TRIGGER } from './constants';
import HookButton from './hook_button';
import HookInput from './hook_input';
import Keyboard from './keyboard';
import utils from './utils';

class DropLab {
  constructor() {
    this.ready = false;
    this.hooks = [];
    this.queuedData = [];
    this.config = {};

    this.eventWrapper = {};
  }

  loadStatic() {
    const dropdownTriggers = [].slice.apply(document.querySelectorAll(`[${DATA_TRIGGER}]`));
    this.addHooks(dropdownTriggers);
  }

  addData(...args) {
    this.applyArgs(args, 'processAddData');
  }

  setData(...args) {
    this.applyArgs(args, 'processSetData');
  }

  destroy() {
    this.hooks.forEach((hook) => hook.destroy());
    this.hooks = [];
    this.removeEvents();
  }

  applyArgs(args, methodName) {
    if (this.ready) return this[methodName](...args);

    this.queuedData = this.queuedData || [];
    this.queuedData.push(args);

    return this.ready;
  }

  processAddData(trigger, data) {
    this.processData(trigger, data, 'addData');
  }

  processSetData(trigger, data) {
    this.processData(trigger, data, 'setData');
  }

  processData(trigger, data, methodName) {
    this.hooks.forEach((hook) => {
      if (Array.isArray(trigger)) hook.list[methodName](trigger);

      if (hook.trigger.id === trigger) hook.list[methodName](data);
    });
  }

  addEvents() {
    this.eventWrapper.documentClicked = this.documentClicked.bind(this);
    document.addEventListener('click', this.eventWrapper.documentClicked);
  }

  documentClicked(e) {
    if (e.defaultPrevented) return;

    if (utils.isDropDownParts(e.target)) return;

    if (e.target.tagName !== 'UL') {
      const closestUl = utils.closest(e.target, 'UL');
      if (utils.isDropDownParts(closestUl)) return;
    }

    this.hooks.forEach((hook) => hook.list.hide());
  }

  removeEvents() {
    document.removeEventListener('click', this.eventWrapper.documentClicked);
  }

  changeHookList(trigger, list, plugins, config) {
    const availableTrigger =
      typeof trigger === 'string' ? document.getElementById(trigger) : trigger;

    this.hooks.forEach((hook, i) => {
      const aHook = hook;

      aHook.list.list.dataset.dropdownActive = false;

      if (aHook.trigger !== availableTrigger) return;

      aHook.destroy();
      this.hooks.splice(i, 1);
      this.addHook(availableTrigger, list, plugins, config);
    });
  }

  addHook(hook, list, plugins, config) {
    const availableHook = typeof hook === 'string' ? document.querySelector(hook) : hook;
    let availableList;

    if (typeof list === 'string') {
      availableList = document.querySelector(list);
    } else if (list instanceof Element) {
      availableList = list;
    } else {
      availableList = document.querySelector(hook.dataset[utils.toCamelCase(DATA_TRIGGER)]);
    }

    availableList.dataset.dropdownActive = true;

    const HookObject = availableHook.tagName === 'INPUT' ? HookInput : HookButton;
    this.hooks.push(new HookObject(availableHook, availableList, plugins, config));

    return this;
  }

  addHooks(hooks, plugins, config) {
    hooks.forEach((hook) => this.addHook(hook, null, plugins, config));
    return this;
  }

  setConfig(obj) {
    this.config = obj;
  }

  fireReady() {
    const readyEvent = new CustomEvent('ready.dl', {
      detail: {
        dropdown: this,
      },
    });
    document.dispatchEvent(readyEvent);

    this.ready = true;
  }

  init(hook, list, plugins, config) {
    if (hook) {
      this.addHook(hook, list, plugins, config);
    } else {
      this.loadStatic();
    }

    this.addEvents();

    Keyboard();

    this.fireReady();

    this.queuedData.forEach((data) => this.addData(data));
    this.queuedData = [];

    return this;
  }
}

export default DropLab;
