((global) => {

  class IssuableBulkActions {
    constructor({ container, form, issues } = {}) {
      this.container = container || $('.content'),
      this.form = form || this.getElement('.bulk-update');
      this.issues = issues || this.getElement('.issues-list .issue');
      this.form.data('bulkActions', this);
      this.willUpdateLabels = false;
      this.bindEvents();
      // Fixes bulk-assign not working when navigating through pages
      Issuable.initChecks();
    }

    getElement(selector) {
      return this.container.find(selector);
    }

    bindEvents() {
      return this.form.off('submit').on('submit', this.onFormSubmit.bind(this));
    }

    onFormSubmit(e) {
      e.preventDefault();
      return this.submit();
    }

    submit() {
      const _this = this;
      const xhr = $.ajax({
        url: this.form.attr('action'),
        method: this.form.attr('method'),
        dataType: 'JSON',
        data: this.getFormDataAsObject()
      });
      xhr.done(() => window.location.reload());
      xhr.fail(() => new Flash("Issue update failed"));
      return xhr.always(this.onFormSubmitAlways.bind(this));
    }

    onFormSubmitAlways() {
      return this.form.find('[type="submit"]').enable();
    }

    getSelectedIssues() {
      return this.issues.has('.selected_issue:checked');
    }

    getLabelsFromSelection() {
      const labels = [];
      this.getSelectedIssues().map(function() {
        const labelsData = $(this).data('labels');
        if (labelsData) {
          return labelsData.map(function(labelId) {
            if (labels.indexOf(labelId) === -1) {
              return labels.push(labelId);
            }
          });
        }
      });
      return labels;
    }


    /**
     * Will return only labels that were marked previously and the user has unmarked
     * @return {Array} Label IDs
     */

    getUnmarkedIndeterminedLabels() {
      const result = [];
      const labelsToKeep = [];

      this.getElement('.labels-filter .is-indeterminate')
        .each((i, el) => labelsToKeep.push($(el).data('labelId')));

      this.getLabelsFromSelection().forEach((id) => {
        if (labelsToKeep.indexOf(id) === -1) {
          result.push(id);
        }
      });

      return result;
    }


    /**
     * Simple form serialization, it will return just what we need
     * Returns key/value pairs from form data
     */

    getFormDataAsObject() {
      const formData = {
        update: {
          state_event: this.form.find('input[name="update[state_event]"]').val(),
          assignee_id: this.form.find('input[name="update[assignee_id]"]').val(),
          milestone_id: this.form.find('input[name="update[milestone_id]"]').val(),
          issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(),
          subscription_event: this.form.find('input[name="update[subscription_event]"]').val(),
          add_label_ids: [],
          remove_label_ids: []
        }
      };
      if (this.willUpdateLabels) {
        this.getLabelsToApply().map(function(id) {
          return formData.update.add_label_ids.push(id);
        });
        this.getLabelsToRemove().map(function(id) {
          return formData.update.remove_label_ids.push(id);
        });
      }
      return formData;
    }

    getLabelsToApply() {
      const labelIds = [];
      const $labels = this.form.find('.labels-filter input[name="update[label_ids][]"]');
      $labels.each(function(k, label) {
        if (label) {
          return labelIds.push(parseInt($(label).val()));
        }
      });
      return labelIds;
    }


    /**
     * Returns Label IDs that will be removed from issue selection
     * @return {Array} Array of labels IDs
     */

    getLabelsToRemove() {
      const result = [];
      const indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
      const labelsToApply = this.getLabelsToApply();
      indeterminatedLabels.map(function(id) {
        // We need to exclude label IDs that will be applied
        // By not doing this will cause issues from selection to not add labels at all
        if (labelsToApply.indexOf(id) === -1) {
          return result.push(id);
        }
      });
      return result;
    }
  }

  global.IssuableBulkActions = IssuableBulkActions;

})(window.gl || (window.gl = {}));
