(function() {
  'use strict';

  var formUtilities = [];

  /**
   *
   * Reactive Submit Button Utility
   *  disables a forms LAST sumit button as long as the required inputs are invalid
   *  (only checks if the value of the inputs are not empty)
   *
   * Attribute: [none]
   *  (automatically setup on all form tags)
   *
   * Params:
   *  data-formnorequired: string
   *    If present the submit button will never get disabled
   *
   * Example usage:
   *  <form uw-reactive-submit-button>
   *   <input type="text" required>
   *   <button type="submit">
   *  </form>
   */

  var REACTIVE_SUBMIT_BUTTON_UTIL_NAME = 'reactiveSubmitButton';
  var REACTIVE_SUBMIT_BUTTON_UTIL_SELECTOR = 'form';

  var REACTIVE_SUBMIT_BUTTON_INITIALIZED_CLASS = 'reactive-submit-button--initialized';

  var reactiveSubmitButtonUtil = function(element) {
    var requiredInputs;
    var submitButton;

    function init() {
      if (!element) {
        throw new Error('Reactive Submit Button utility cannot be setup without an element!');
      }

      if (element.classList.contains(REACTIVE_SUBMIT_BUTTON_INITIALIZED_CLASS)) {
        return false;
      }

      // abort if form has param data-formnorequired
      if (element.dataset.formnorequired !== undefined) {
        throw new Error('Form has formnorequired data attribute. Will skip setup of reactive submit button.');
      }

      requiredInputs = Array.from(element.querySelectorAll('[required]'));
      if (!requiredInputs) {
        // abort if form has no required inputs
        throw new Error('Submit button has formnorequired data attribute. Will skip setup of reactive submit button.');
      }

      var submitButtons = Array.from(element.querySelectorAll('[type="submit"]'));
      if (!submitButtons || !submitButtons.length) {
        throw new Error('Reactive Submit Button utility couldn\'t find any submit buttons!');
      }
      submitButton = submitButtons.reverse()[0];
      // abort if form has param data-formnorequired
      if (submitButton.dataset.formnorequired !== undefined) {
        return false;
      }

      setupInputs();
      updateButtonState();

      element.classList.add(REACTIVE_SUBMIT_BUTTON_INITIALIZED_CLASS);

      return {
        name: REACTIVE_SUBMIT_BUTTON_UTIL_NAME,
        element: element,
        destroy: function() {},
      };
    }

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

    function updateButtonState() {
      if (inputsValid()) {
        submitButton.removeAttribute('disabled');
      } else {
        submitButton.setAttribute('disabled', 'true');
      }
    }

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

    return init();
  };

  formUtilities.push({
    name: REACTIVE_SUBMIT_BUTTON_UTIL_NAME,
    selector: REACTIVE_SUBMIT_BUTTON_UTIL_SELECTOR,
    setup: reactiveSubmitButtonUtil,
  });

  /**
   *
   * Interactive Fieldset Utility
   *  shows/hides inputs based on value of particular input
   *
   * Attribute: uw-interactive-fieldset
   *
   * Params:
   *  data-conditional-input: string
   *    Selector for the input that this fieldset watches for changes
   *  data-conditional-value: string
   *    The value the conditional input needs to be set to for this fieldset to be shown
   *    Can be omitted if conditionalInput is a checkbox
   *
   * Example usage:
   * ## example with text input
   *  <input id="input-0" type="text">
   *  <fieldset uw-interactive-fieldset data-conditional-input="#input-0" data-conditional-value="yes">...</fieldset>
   *  <fieldset uw-interactive-fieldset data-conditional-input="#input-0" data-conditional-value="no">...</fieldset>
   * ## example with <select>
   *  <select id="select-0">
   *    <option value="0">Zero
   *    <option value="1">One
   *  <fieldset uw-interactive-fieldset data-conditional-input="#select-0" data-conditional-value="0">...</fieldset>
   *  <fieldset uw-interactive-fieldset data-conditional-input="#select-0" data-conditional-value="1">...</fieldset>
   * ## example with checkbox
   *  <input id="checkbox-0" type="checkbox">
   *  <input id="checkbox-1" type="checkbox">
   *  <fieldset uw-interactive-fieldset data-conditional-input="#checkbox-0">...</fieldset>
   *  <fieldset uw-interactive-fieldset data-conditional-input="#checkbox-1">...</fieldset>
   */

  var INTERACTIVE_FIELDSET_UTIL_NAME = 'interactiveFieldset';
  var INTERACTIVE_FIELDSET_UTIL_SELECTOR = '[uw-interactive-fieldset]';
  var INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR = '.interactive-fieldset__target';

  var INTERACTIVE_FIELDSET_INITIALIZED_CLASS = 'interactive-fieldset--initialized';
  var INTERACTIVE_FIELDSET_CHILD_SELECTOR = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled])';

  var interactiveFieldsetUtil = function(element) {
    var conditionalInput;
    var conditionalValue;
    var target;
    var childInputs;

    function init() {
      if (!element) {
        throw new Error('Interactive Fieldset utility cannot be setup without an element!');
      }

      if (element.classList.contains(INTERACTIVE_FIELDSET_INITIALIZED_CLASS)) {
        return false;
      }

      // param conditionalInput
      if (!element.dataset.conditionalInput) {
        throw new Error('Interactive Fieldset needs a selector for a conditional input!');
      }
      conditionalInput = document.querySelector('#' + element.dataset.conditionalInput);
      if (!conditionalInput) {
        // abort if form has no required inputs
        throw new Error('Couldn\'t find the conditional input. Aborting setup for interactive fieldset.');
      }

      // param conditionalValue
      if (!element.dataset.conditionalValue && !isCheckbox()) {
        throw new Error('Interactive Fieldset needs a conditional value!');
      }
      conditionalValue = element.dataset.conditionalValue;

      target = element.closest(INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR);
      if (!target || element.matches(INTERACTIVE_FIELDSET_UTIL_TARGET_SELECTOR)) {
        target = element;
      }

      childInputs = Array.from(element.querySelectorAll(INTERACTIVE_FIELDSET_CHILD_SELECTOR));

      // add event listener
      var observer = new MutationObserver(function(mutationsList, observer) {
        updateVisibility();
      });
      observer.observe(conditionalInput, { attributes: true, attributeFilter: ['disabled'] });
      conditionalInput.addEventListener('input', updateVisibility);

      // initial visibility update
      updateVisibility();

      // mark as initialized
      element.classList.add(INTERACTIVE_FIELDSET_INITIALIZED_CLASS);

      return {
        name: INTERACTIVE_FIELDSET_UTIL_NAME,
        element: element,
        destroy: function() {},
      };
    }

    function updateVisibility() {
      var active = matchesConditionalValue() && !conditionalInput.disabled;

      target.classList.toggle('hidden', !active);

      childInputs.forEach(function(el) {
        el.disabled = !active;
      });
    }

    function matchesConditionalValue() {
      if (isCheckbox()) {
        return conditionalInput.checked === true;
      }

      return conditionalInput.value === conditionalValue;
    }

    function isCheckbox() {
      return conditionalInput.getAttribute('type') === 'checkbox';
    }

    return init();
  };

  formUtilities.push({
    name: INTERACTIVE_FIELDSET_UTIL_NAME,
    selector: INTERACTIVE_FIELDSET_UTIL_SELECTOR,
    setup: interactiveFieldsetUtil,
  });

  /**
   *
   * Auto Submit Button Utility
   *  Hides submit buttons in forms that are submitted programmatically
   *  We hide the button using JavaScript so no-js users will still be able to submit the form
   *
   * Attribute: uw-auto-submit-button
   *
   * Example usage:
   *  <button type="submit" uw-auto-submit-button>Submit
   */

  var AUTO_SUBMIT_BUTTON_UTIL_NAME = 'autoSubmitButton';
  var AUTO_SUBMIT_BUTTON_UTIL_SELECTOR = '[uw-auto-submit-button]';

  var AUTO_SUBMIT_BUTTON_INITIALIZED_CLASS = 'auto-submit-button--initialized';
  var AUTO_SUBMIT_BUTTON_HIDDEN_CLASS = 'hidden';

  var autoSubmitButtonUtil = function(element) {
    if (!element) {
      throw new Error('Auto Submit Button utility needs to be passed an element!');
    }

    // hide and mark initialized
    element.classList.add(AUTO_SUBMIT_BUTTON_HIDDEN_CLASS, AUTO_SUBMIT_BUTTON_INITIALIZED_CLASS);

    return {
      name: AUTO_SUBMIT_BUTTON_UTIL_NAME,
      element: element,
      destroy: function() {},
    };
  };

  formUtilities.push({
    name: AUTO_SUBMIT_BUTTON_UTIL_NAME,
    selector: AUTO_SUBMIT_BUTTON_UTIL_SELECTOR,
    setup: autoSubmitButtonUtil,
  });

  /**
   *
   * Form Error Remover Utility
   *  Removes errors from inputs when they are focused
   *
   * Attribute: [none]
   *  (automatically setup on all form tags)
   *
   * Example usage:
   *  (any regular form that can show input errors)
   */

  var FORM_ERROR_REMOVER_UTIL_NAME = 'formErrorRemover';
  var FORM_ERROR_REMOVER_UTIL_SELECTOR = 'form';

  var FORM_ERROR_REMOVER_INITIALIZED_CLASS = 'form-error-remover--initialized';
  var FORM_ERROR_REMOVER_INPUTS_SELECTOR = 'input:not([type="hidden"]), textarea, select';

  var FORM_GROUP_SELECTOR = '.form-group';
  var FORM_GROUP_WITH_ERRORS_CLASS = 'form-group--has-error';


  var formErrorRemoverUtil = function(element) {
    var formGroups;

    function init() {
      if (!element) {
        throw new Error('Form Error Remover utility needs to be passed an element!');
      }

      // find form groups
      formGroups = Array.from(element.querySelectorAll(FORM_GROUP_SELECTOR));

      formGroups.forEach(function(formGroup) {
        if (!formGroup.classList.contains(FORM_GROUP_WITH_ERRORS_CLASS)) {
          return;
        }

        var inputElements = Array.from(formGroup.querySelectorAll(FORM_ERROR_REMOVER_INPUTS_SELECTOR));
        if (!inputElements) {
          return false;
        }

        inputElements.forEach(function(inputElement) {
          inputElement.addEventListener('input', function() {
            formGroup.classList.remove(FORM_GROUP_WITH_ERRORS_CLASS);
          });
        });
      });

      // mark initialized
      element.classList.add(FORM_ERROR_REMOVER_INITIALIZED_CLASS);

      return {
        name: FORM_ERROR_REMOVER_UTIL_NAME,
        element: element,
        destroy: function() {},
      };
    }

    return init();
  };

  formUtilities.push({
    name: FORM_ERROR_REMOVER_UTIL_NAME,
    selector: FORM_ERROR_REMOVER_UTIL_SELECTOR,
    setup: formErrorRemoverUtil,
  });

  // register the collected form utilities
  if (UtilRegistry) {
    formUtilities.forEach(UtilRegistry.register);
  }

})();
