/**
 * BoorUJS
 *
 * Apply event-based actions through data-* attributes. The attributes are structured like so: [data-event-action]
 */

import { $, $$ } from './utils/dom';
import { fetchHtml, handleError } from './utils/requests';
import { showBlock } from './utils/image';
import { addTag } from './tagsinput';

/* eslint-disable prettier/prettier */

// Event types and any qualifying conditions - return true to not run action
const types = {
  click(event)    { return event.button !== 0; /* Left-click only */ },
  change()        { /* No qualifier */ },
  fetchcomplete() { /* No qualifier */ },
};

const actions = {
  hide(data)       { selectorCb(data.base, data.value, el => el.classList.add('hidden')); },
  show(data)       { selectorCb(data.base, data.value, el => el.classList.remove('hidden')); },
  toggle(data)     { selectorCb(data.base, data.value, el => el.classList.toggle('hidden')); },
  submit(data)     { selectorCb(data.base, data.value, el => el.submit()); },
  disable(data)    { selectorCb(data.base, data.value, el => el.disabled = true); },
  focus(data)      { document.querySelector(data.value).focus(); },
  unfilter(data)   { showBlock(data.el.closest('.image-show-container')); },
  tabHide(data)    { selectorCbChildren(data.base, data.value, el => el.classList.add('hidden')); },
  preventdefault() { /* The existence of this entry is enough */ },

  copy(data) {
    document.querySelector(data.value).select();
    document.execCommand('copy');
  },

  inputvalue(data) {
    document.querySelector(data.value).value = data.el.dataset.setValue;
  },

  selectvalue(data) {
    document.querySelector(data.value).value = data.el.querySelector(':checked').dataset.setValue;
  },

  checkall(data) {
    $$(`${data.value} input[type=checkbox]`).forEach(c => {
      c.checked = !c.checked;
    });
  },

  addtag(data) {
    addTag(document.querySelector(data.el.closest('[data-target]').dataset.target), data.el.dataset.tagName);
  },

  tab(data) {
    const block = data.el.parentNode.parentNode,
          newTab = $(`.block__tab[data-tab="${data.value}"]`),
          loadTab = data.el.dataset.loadTab;

    // Switch tab
    const selectedTab = block.querySelector('.selected');
    if (selectedTab) {
      selectedTab.classList.remove('selected');
    }
    data.el.classList.add('selected');

    // Switch contents
    this.tabHide({ base: block, value: '.block__tab' });
    this.show({ base: block, value: `.block__tab[data-tab="${data.value}"]` });

    // If the tab has a 'data-load-tab' attribute, load and insert the content
    if (loadTab && !newTab.dataset.loaded) {
      fetchHtml(loadTab)
        .then(handleError)
        .then(response => response.text())
        .then(response => newTab.innerHTML = response)
        .then(() => newTab.dataset.loaded = true)
        .catch(() => newTab.textContent = 'Error!');
    }
  },
};

/* eslint-enable prettier/prettier */

// Use this function to apply a callback to elements matching the selectors
function selectorCb(base = document, selector, cb) {
  [].forEach.call(base.querySelectorAll(selector), cb);
}

function selectorCbChildren(base = document, selector, cb) {
  const sel = $$(selector, base);

  for (const el of base.children) {
    if (!sel.includes(el)) continue;

    cb(el);
  }
}

function matchAttributes(event) {
  if (!types[event.type](event)) {
    for (const action in actions) {
      const attr = `data-${event.type}-${action.toLowerCase()}`,
        el = event.target && event.target.closest(`[${attr}]`),
        value = el && el.getAttribute(attr);

      if (el) {
        // Return true if you don't want to preventDefault
        actions[action]({ attr, el, value }) || event.preventDefault();
      }
    }
  }
}

function registerEvents() {
  for (const type in types) document.addEventListener(type, matchAttributes);
}

export { registerEvents };
