(function() {
  'use strict';

  window.utils = window.utils || {};

  var JS_INITIALIZED = 'js-form-initialized';
  var SUBMIT_BUTTON_SELECTOR = '[type="submit"]:not([formnovalidate])';
  var AUTOSUBMIT_BUTTON_SELECTOR = '[type="submit"][data-autosubmit]';
  var AJAX_SUBMIT_FLAG = 'ajaxSubmit';

  var FORM_GROUP_CLASS = 'form-group';
  var FORM_GROUP_WITH_ERRORS_CLASS = 'form-group--has-error';

  function formValidator(inputs) {
    var done = true;
    inputs.forEach(function(inp) {
      var len = inp.value.trim().length;
      if (done && len === 0) {
        done = false;
      }
    });
    return done;
  }

  window.utils.form = function(form, options) {
    options = options || {};

    // dont initialize form if it is in a modal and is not forced
    if (form.closest('.modal') && !options.force) {
      return false;
    }

    // dont initialize form if already initialized and should not be force-initialized
    if (form.classList.contains(JS_INITIALIZED) && !options.force) {
      return false;
    }

    var utilInstances = [];

    // reactive buttons
    utilInstances.push(window.utils.setup('reactiveButton', form));

    // conditonal fieldsets
    var fieldSets = Array.from(form.querySelectorAll('fieldset[data-conditional-id][data-conditional-value]'));
    utilInstances.push(window.utils.setup('interactiveFieldset', form, { fieldSets }));

    // hide autoSubmit submit button
    utilInstances.push(window.utils.setup('autoSubmit', form, options));

    // async form
    if (AJAX_SUBMIT_FLAG in form.dataset) {
      utilInstances.push(window.utils.setup('asyncForm', form, options));
    }

    // inputs
    utilInstances.push(window.utils.setup('inputs', form, options));

    // form group errors
    var formGroups = Array.from(form.querySelectorAll('.' + FORM_GROUP_CLASS));
    formGroups.forEach(function(formGroup) {
      utilInstances.push(window.utils.setup('errorRemover', formGroup, options));
    });

    form.classList.add(JS_INITIALIZED);

    function destroyUtils() {
      utilInstances.filter(function(utilInstance) {
        return !!utilInstance;
      }).forEach(function(utilInstance) {
        utilInstance.destroy();
      });
    }

    return {
      scope: form,
      destroy: destroyUtils,
    };
  };

  // registers input-listener for each element in <inputs> (array) and
  // enables <button> if <formValidator> for these inputs returns true
  window.utils.reactiveButton = function(form, options) {
    var button = form.querySelector(SUBMIT_BUTTON_SELECTOR);
    var requireds = Array.from(form.querySelectorAll('[required]'));

    if (!button || button.matches(AUTOSUBMIT_BUTTON_SELECTOR)) {
      return false;
    }

    if (requireds.length == 0) {
      return false;
    }

    if (typeof button.dataset.formnorequired !== 'undefined' && button.dataset.formnorequired !== null) {
      button.addEventListener('click', function() {
        form.submit();
      });
      return false;
    }
    updateButtonState();

    requireds.forEach(function(el) {
      var checkbox = el.getAttribute('type') === 'checkbox';
      var eventType = checkbox ? 'change' : 'input';
      el.addEventListener(eventType, function() {
        updateButtonState();
      });
    });

    function updateButtonState() {
      if (formValidator(requireds) === true) {
        button.removeAttribute('disabled');
      } else {
        button.setAttribute('disabled', 'true');
      }
    }

    return {
      scope: form,
      destroy: function() {},
    };
  };

  window.utils.interactiveFieldset = function(form, options) {
    options = options || {};
    var fieldSets = options.fieldSets;

    if (!fieldSets) {
      throw new Error('interactiveFieldset must be passed fieldSets via options');
    }

    var fields = fieldSets.map(function(fs) {
      return {
        fieldSet: fs,
        condId: fs.dataset.conditionalId,
        condValue: fs.dataset.conditionalValue,
        condEl: form.querySelector('#' + fs.dataset.conditionalId),
      };
    }).filter(function(field) {
      return !!field.condEl;
    });

    function updateFields() {
      fields.forEach(function(field) {
        field.fieldSet.classList.toggle('hidden', field.condEl.value !== field.condValue);
      });
    }

    function addEventListeners() {
      fields.forEach(function(field) {
        field.condEl.addEventListener('input', updateFields)
      });
    }

    if (fieldSets.length) {
      addEventListeners();
      updateFields();
    }

    return {
      scope: form,
      destroy: function() {},
    };
  };

  window.utils.autoSubmit = function(form, options) {
    var button = form.querySelector(AUTOSUBMIT_BUTTON_SELECTOR);
    if (button) {
      button.classList.add('hidden');
    }

    return {
      scope: form,
      destroy: function() {},
    };
  };

  // listens for focus events and removes any errors on an input
  window.utils.errorRemover = function(formGroup, options) {

    var inputElement = formGroup.querySelector('input:not([type="hidden"]), textarea, select');
    if (!inputElement) {
      return false;
    }

    inputElement.addEventListener('focus', focusListener);

    function focusListener() {
      var hasError = formGroup.classList.contains(FORM_GROUP_WITH_ERRORS_CLASS);
      if (hasError) {
        formGroup.classList.remove(FORM_GROUP_WITH_ERRORS_CLASS);
      }
    }

    return {
      scope: formGroup,
      destroy: function() {
        inputElement.removeEventListener('focus', focusListener);
      },
    };
  };
})();
