const setStyles = (el, styles) => {
  Object.keys(styles).forEach((key) => {
    el.style[key] = styles[key];
  });
};

const setAttrs = (el, attrs) => {
  Object.keys(attrs).forEach((key) => {
    el.setAttribute(key, attrs[key]);
  });
};

const getAttr = (el, attr) => el.getAttribute(attr);

const privateKeys = {
  defaultOptions: Symbol('defaultOptions'),
  render: Symbol('render'),
  show: Symbol('show'),
  hide: Symbol('hide'),
  removeDOM: Symbol('removeDOM'),
};

const siiimpleToast = {
  [privateKeys.defaultOptions]: {
    container: 'body',
    class: 'siiimpleToast',
    position: 'top|center',
    margin: 15,
    delay: 0,
    duration: 3000,
    style: {},
  },

  setOptions(options = {}) {
    return {
      ...siiimpleToast,
      [privateKeys.defaultOptions]: {
        ...this[privateKeys.defaultOptions],
        ...options,
      },
    };
  },

  [privateKeys.render](state, message, options = {}) {
    const mergedOptions = {
      ...this[privateKeys.defaultOptions],
      ...options,
    };

    const {
      class: className,
      position,
      delay,
      duration,
      style,
    } = mergedOptions;

    const newToast = document.createElement('div');

    // logging via attrs
    newToast.className = className;
    newToast.innerHTML = message;

    setAttrs(newToast, {
      'data-position': position,
      'data-state': state,
    });

    setStyles(newToast, style);

    // use .setTimeout() instead of $.queue()
    let time = 0;
    setTimeout(() => {
      this[privateKeys.show](newToast, mergedOptions);
    }, time += delay);
    setTimeout(() => {
      this[privateKeys.hide](newToast, mergedOptions);
    }, time += duration);

    // support method chaining
    return this;
  },

  [privateKeys.show](el, { container, class: className, margin }) {
    const hasPos = (v, pos) => getAttr(v, 'data-position').indexOf(pos) > -1;

    const root = document.querySelector(container);
    root.insertBefore(el, root.firstChild);

    // set initial position
    setStyles(el, {
      position: container === 'body' ? 'fixed' : 'absolute',
      [hasPos(el, 'top') ? 'top' : 'bottom']: '-100px',
      [hasPos(el, 'left') && 'left']: '15px',
      [hasPos(el, 'center') && 'left']: `${(root.clientWidth / 2) - (el.clientWidth / 2)}px`,
      [hasPos(el, 'right') && 'right']: '15px',
    });

    setStyles(el, {
      transform: 'scale(1)',
      opacity: 1,
    });

    // push effect
    let pushStack = margin;

    Array
      .from(document.querySelectorAll(`.${className}[data-position="${getAttr(el, 'data-position')}"]`))
      .filter(toast => toast.parentElement === el.parentElement)// matching container
      .forEach((toast) => {
        setStyles(toast, {
          [hasPos(toast, 'top') ? 'top' : 'bottom']: `${pushStack}px`,
        });

        pushStack += toast.offsetHeight + margin;
      });
  },

  [privateKeys.hide](el) {
    const hasPos = (v, pos) => getAttr(v, 'data-position').indexOf(pos) > -1;
    const { left, width } = el.getBoundingClientRect();

    setStyles(el, {
      [hasPos(el, 'left') && 'left']: `${width}px`,
      [hasPos(el, 'center') && 'left']: `${left + width}px`,
      [hasPos(el, 'right') && 'right']: `-${width}px`,
      opacity: 0,
    });

    const whenTransitionEnd = () => {
      this[privateKeys.removeDOM](el);
      el.removeEventListener('transitionend', whenTransitionEnd);
    };

    el.addEventListener('transitionend', whenTransitionEnd);
  },

  [privateKeys.removeDOM](el) {// eslint-disable-line
    const parent = el.parentElement;
    parent.removeChild(el);
  },

  message(message, options) {
    return this[privateKeys.render]('default', message, options);
  },
  success(message, options) {
    return this[privateKeys.render]('success', message, options);
  },
  warn(message, options) {
    return this[privateKeys.render]('warn', message, options);
  },
  alert(message, options) {
    return this[privateKeys.render]('alert', message, options);
  }
};

window.toast = siiimpleToast;