(function () {
  'use strict';

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

  var ASYNC_TABLE_FILTER_LOADING_CLASS = 'async-table-filter--loading';
  var JS_INITIALIZED_CLASS = 'js-async-table-filter-initialized';
  var INPUT_DEBOUNCE = 600;

  // debounce function, taken from Underscore.js
  function debounce(func, wait, immediate) {
    var timeout;
    return function() {
      var context = this, args = arguments;
      var later = function() {
        timeout = null;
        if (!immediate) func.apply(context, args);
      };
      var callNow = immediate && !timeout;
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
      if (callNow) func.apply(context, args);
    };
  };

  window.utils.asyncTableFilter = function(formElement, options) {
    if (!options || !options.updateTableFrom) {
      return false;
    }

    if (formElement.matches('.' + JS_INITIALIZED_CLASS)) {
      return false;
    }

    var formIdElement = formElement.querySelector('[name="form-identifier"]');
    if (!formIdElement) {
      return;
    }

    options = options || {};
    var tableIdent = options.dbtIdent;
    var formId = formIdElement.value;
    var inputs = {
      search: [],
      input: [],
      change: [],
      select: [],
    }

    function setup() {
      gatherInputs();
      addEventListeners();
    }

    function gatherInputs() {
      Array.from(formElement.querySelectorAll('input[type="search"]')).forEach(function(input) {
        inputs.search.push(input);
      });

      Array.from(formElement.querySelectorAll('input[type="text"]')).forEach(function(input) {
        inputs.input.push(input);
      });

      Array.from(formElement.querySelectorAll('input:not([type="text"]):not([type="search"])')).forEach(function(input) {
        inputs.change.push(input);
      });

      Array.from(formElement.querySelectorAll('select')).forEach(function(input) {
        inputs.select.push(input);
      });
    }

    function addEventListeners() {
      inputs.search.forEach(function(input) {
        var debouncedInput = debounce(function() {
          if (input.value.length === 0 || input.value.length > 2) {
            updateTable();
          }
        }, INPUT_DEBOUNCE);
        input.addEventListener('input', debouncedInput);
      });

      inputs.input.forEach(function(input) {
        var debouncedInput = debounce(function() {
          if (input.value.length === 0 || input.value.length > 2) {
            updateTable();
          }
        }, INPUT_DEBOUNCE);
        input.addEventListener('input', debouncedInput);
      });

      inputs.change.forEach(function(input) {
        input.addEventListener('change', function() {
          updateTable();
        });
      });

      inputs.select.forEach(function(input) {
        input.addEventListener('change', function() {
          updateTable();
        });
      });

      formElement.addEventListener('submit', function(event) {
        event.preventDefault();
        updateTable();
      });
    }

    function updateTable() {
      var url = serializeFormToURL();
      var callback = null;

      formElement.classList.add(ASYNC_TABLE_FILTER_LOADING_CLASS);

      var focusedSearch = inputs.search.reduce(function(acc, input) {
        return acc || (input.matches(':focus') && input);
      }, null);
      // focus search input
      if (focusedSearch) {
        var selectionStart = focusedSearch.selectionStart;
        callback = function(wrapper) {
          var search = wrapper.querySelector('input[type="search"]');
          if (search) {
            search.focus();
            search.selectionStart = selectionStart;
          }
        };
      }
      options.updateTableFrom(url, options, callback);
    }

    function serializeFormToURL() {
      var url = new URL(options.currentUrl || window.location.href);
      url.searchParams.set('form-identifier', formId);
      url.searchParams.set('_hasdata', 'true');
      url.searchParams.set(tableIdent + '-page', '0');

      inputs.search.forEach(function(input) {
        url.searchParams.set(input.name, input.value);
      });

      inputs.input.forEach(function(input) {
        url.searchParams.set(input.name, input.value);
      });

      inputs.change.forEach(function(input) {
        if (input.checked) {
          url.searchParams.set(input.name, input.value);
        }
      });

      inputs.select.forEach(function(select) {
        var options = Array.from(select.querySelectorAll('option'));
        var selected = options.find(function(option) { return option.selected });
        if (selected) {
          url.searchParams.set(select.name, selected.value);
        }
      });

      return url;
    }

    setup();

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