"use strict";

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var _ = require('lodash');

var FastBitSet = require('fastbitset');

var booleanParser = require('boolean-parser');

var clone = function clone(val) {
  try {
    return JSON.parse(JSON.stringify(val));
  } catch (e) {
    return val;
  }
};

var humanize = function humanize(str) {
  return str.replace(/^[\s_]+|[\s_]+$/g, '').replace(/[_\s]+/g, ' ').replace(/^[a-z]/, function (m) {
    return m.toUpperCase();
  });
};

var combination_indexes = function combination_indexes(facets, filters) {
  var indexes = {};

  _.mapValues(filters, function (filter) {
    // filter is still array so disjunctive
    if (Array.isArray(filter[0])) {
      var facet_union = new FastBitSet([]);
      var filter_keys = [];

      _.mapValues(filter, function (disjunctive_filter) {
        var filter_key = disjunctive_filter[0];
        var filter_val = disjunctive_filter[1];
        filter_keys.push(filter_key);
        facet_union = facet_union.new_union(facets['bits_data'][filter_key][filter_val] || new FastBitSet([]));
        indexes[filter_key] = facet_union;
      });
    }
  });

  return indexes;
};

var filters_matrix = function filters_matrix(facets, query_filters) {
  var temp_facet = _.clone(facets);

  if (!temp_facet['is_temp_copied']) {
    _.mapValues(temp_facet['bits_data'], function (values, key) {
      _.mapValues(temp_facet['bits_data'][key], function (facet_indexes, key2) {
        temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data'][key][key2];
      });
    });
  }

  var union = null;
  ;
  /**
   * process only conjunctive filters
   */

  _.mapValues(query_filters, function (conjunction) {
    var conjunctive_index = null;

    _.mapValues(conjunction, function (filter) {
      var filter_key = filter[0];
      var filter_val = filter[1];

      if (!temp_facet['bits_data_temp'][filter_key]) {
        throw new Error('Panic. The key does not exist in facets lists.');
      }

      if (conjunctive_index && temp_facet['bits_data_temp'][filter_key][filter_val]) {
        conjunctive_index = temp_facet['bits_data_temp'][filter_key][filter_val].new_intersection(conjunctive_index);
      } else if (conjunctive_index && !temp_facet['bits_data_temp'][filter_key][filter_val]) {
        conjunctive_index = new FastBitSet([]);
      } else {
        conjunctive_index = temp_facet['bits_data_temp'][filter_key][filter_val];
      }
    });

    union = (union || new FastBitSet([])).new_union(conjunctive_index || new FastBitSet([]));
  });

  if (union !== null) {
    _.mapValues(temp_facet['bits_data_temp'], function (values, key) {
      _.mapValues(temp_facet['bits_data_temp'][key], function (facet_indexes, key2) {
        temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data_temp'][key][key2].new_intersection(union);
      });
    });
  }

  return temp_facet;
};
/*
 * returns facets and ids
 */


var matrix = function matrix(facets, filters) {
  var temp_facet = _.clone(facets);

  filters = filters || [];

  _.mapValues(temp_facet['bits_data'], function (values, key) {
    _.mapValues(temp_facet['bits_data'][key], function (facet_indexes, key2) {
      temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data'][key][key2];
    });
  });

  temp_facet['is_temp_copied'] = true;
  var conjunctive_index;
  var disjunctive_indexes = combination_indexes(facets, filters);
  /**
   * process only conjunctive filters
   */

  _.mapValues(filters, function (filter) {
    if (!Array.isArray(filter[0])) {
      var filter_key = filter[0];
      var filter_val = filter[1];

      if (conjunctive_index && temp_facet['bits_data_temp'][filter_key][filter_val]) {
        conjunctive_index = temp_facet['bits_data_temp'][filter_key][filter_val].new_intersection(conjunctive_index);
      } else if (conjunctive_index && !temp_facet['bits_data_temp'][filter_key][filter_val]) {
        conjunctive_index = new FastBitSet([]);
      } else {
        conjunctive_index = temp_facet['bits_data_temp'][filter_key][filter_val];
      }
    }
  }); // cross all facets with conjunctive index


  if (conjunctive_index) {
    _.mapValues(temp_facet['bits_data_temp'], function (values, key) {
      _.mapValues(temp_facet['bits_data_temp'][key], function (facet_indexes, key2) {
        temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data_temp'][key][key2].new_intersection(conjunctive_index);
      });
    });
  }
  /**
   * process only negative filters
   */


  _.mapValues(filters, function (filter) {
    if (filter.length === 3 && filter[1] === '-') {
      var filter_key = filter[0];
      var filter_val = filter[2];
      var negative_bits = temp_facet['bits_data_temp'][filter_key][filter_val].clone();

      _.mapValues(temp_facet['bits_data_temp'], function (values, key) {
        _.mapValues(temp_facet['bits_data_temp'][key], function (facet_indexes, key2) {
          temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data_temp'][key][key2].new_difference(negative_bits);
        });
      });
    }
  }); // cross all facets with disjunctive index


  _.mapValues(temp_facet['bits_data_temp'], function (values, key) {
    _.mapValues(temp_facet['bits_data_temp'][key], function (facet_indexes, key2) {
      _.mapValues(disjunctive_indexes, function (disjunctive_index, disjunctive_key) {
        if (disjunctive_key !== key) {
          temp_facet['bits_data_temp'][key][key2] = temp_facet['bits_data_temp'][key][key2].new_intersection(disjunctive_index);
        }
      });
    });
  });

  return temp_facet;
};

var index = function index(items, fields) {
  fields = fields || [];
  var facets = {
    data: {},
    bits_data: {},
    bits_data_temp: {}
  };
  var i = 1;
  items = _.map(items, function (item) {
    if (!item['_id']) {
      item['_id'] = i;
      ++i;
    }

    return item;
  }); // replace chain with forEach

  _.chain(items).map(function (item) {
    fields.forEach(function (field) {
      //if (!item || !item[field]) {
      if (!item) {
        return;
      }

      if (!facets['data'][field]) {
        facets['data'][field] = {};
      }

      if (Array.isArray(item[field])) {
        item[field].forEach(function (v) {
          if (!item[field]) {
            return;
          }

          if (!facets['data'][field][v]) {
            facets['data'][field][v] = [];
          }

          facets['data'][field][v].push(parseInt(item._id));
        });
      } else if (typeof item[field] !== 'undefined') {
        var v = item[field];

        if (!facets['data'][field][v]) {
          facets['data'][field][v] = [];
        }

        facets['data'][field][v].push(parseInt(item._id));
      }
    });
    return item;
  }).value();

  facets['data'] = _.mapValues(facets['data'], function (values, field) {
    if (!facets['bits_data'][field]) {
      facets['bits_data'][field] = {};
      facets['bits_data_temp'][field] = {};
    } //console.log(values);


    return _.mapValues(values, function (indexes, filter) {
      var sorted_indexes = _.sortBy(indexes);

      facets['bits_data'][field][filter] = new FastBitSet(sorted_indexes);
      return sorted_indexes;
    });
  });
  return facets;
};
/**
 * calculates ids for filters
 */


var filters_ids = function filters_ids(facets_data) {
  var output = new FastBitSet([]);

  _.mapValues(facets_data, function (values, key) {
    _.mapValues(facets_data[key], function (facet_indexes, key2) {
      output = output.new_union(facets_data[key][key2]);
    });
  });

  return output;
};
/**
 * calculates ids for facets
 * if there is no facet input then return null to not save resources for OR calculation
 * null means facets haven't matched searched items
 */


var facets_ids = function facets_ids(facets_data, filters) {
  var output = new FastBitSet([]);
  var i = 0;

  _.mapValues(filters, function (filters, field) {
    filters.forEach(function (filter) {
      ++i;
      output = output.new_union(facets_data[field][filter] || new FastBitSet([]));
    });
  });

  if (i === 0) {
    return null;
  }

  return output;
};

var getBuckets = function getBuckets(data, input, aggregations) {
  var position = 1;
  return _.mapValues(data['bits_data_temp'], function (v, k) {
    var order;
    var sort;
    var size;
    var title;
    var show_facet_stats;
    var chosen_filters_on_top;
    var hide_zero_doc_count;

    if (aggregations[k]) {
      order = aggregations[k].order;
      sort = aggregations[k].sort;
      size = aggregations[k].size;
      title = aggregations[k].title;
      show_facet_stats = aggregations[k].show_facet_stats || false;
      chosen_filters_on_top = aggregations[k].chosen_filters_on_top !== false;
      hide_zero_doc_count = aggregations[k].hide_zero_doc_count || false;
    }

    var buckets = _.chain(v).toPairs().map(function (v2) {
      var filters = [];

      if (input && input.filters && input.filters[k]) {
        filters = input.filters[k];
      }

      var doc_count = v2[1].array().length; //hide zero_doc_count facet only if it is not selected

      if (hide_zero_doc_count && doc_count === 0 && filters.indexOf(v2[0]) === -1) {
        return;
      }

      return {
        key: v2[0],
        doc_count: doc_count,
        selected: filters.indexOf(v2[0]) !== -1
      };
    }).compact().value();

    var iteratees;
    var sort_order;

    if (_.isArray(sort)) {
      iteratees = sort || ['key'];
      sort_order = order || ['asc'];
    } else {
      if (sort === 'term' || sort === 'key') {
        iteratees = ['key'];
        sort_order = [order || 'asc'];
      } else {
        iteratees = ['doc_count', 'key'];
        sort_order = [order || 'desc', 'asc'];
      }

      if (chosen_filters_on_top) {
        iteratees.unshift('selected');
        sort_order.unshift('desc');
      }
    }

    buckets = _.orderBy(buckets, iteratees, sort_order);
    buckets = buckets.slice(0, size || 10); // Calculate the facet_stats

    var facet_stats;
    var calculated_facet_stats;

    if (show_facet_stats) {
      facet_stats = [];

      _.chain(v).toPairs().forEach(function (v2) {
        if (isNaN(v2[0])) {
          throw new Error("You cant use chars to calculate the facet_stats.");
        } // Doc_count


        if (v2[1].array().length > 0) {
          v2[1].forEach(function (doc_count) {
            facet_stats.push(parseInt(v2[0]));
          });
        }
      }).value();

      calculated_facet_stats = {
        min: _.minBy(facet_stats),
        max: _.maxBy(facet_stats),
        avg: _.meanBy(facet_stats),
        sum: _.sumBy(facet_stats)
      };
    }

    return _objectSpread({
      name: k,
      title: title || humanize(k),
      position: position++,
      buckets: buckets
    }, show_facet_stats && {
      facet_stats: calculated_facet_stats
    });
  });
};

var mergeAggregations = function mergeAggregations(aggregations, input) {
  return _.mapValues(clone(aggregations), function (val, key) {
    if (!val.field) {
      val.field = key;
    }

    var filters = [];

    if (input.filters && input.filters[key]) {
      filters = input.filters[key];
    }

    val.filters = filters;
    var not_filters = [];

    if (input.not_filters && input.not_filters[key]) {
      not_filters = input.not_filters[key];
    }

    if (input.exclude_filters && input.exclude_filters[key]) {
      not_filters = input.exclude_filters[key];
    }

    val.not_filters = not_filters;
    return val;
  });
};

var input_to_facet_filters = function input_to_facet_filters(input, config) {
  var filters = [];

  _.mapValues(input.filters, function (values, key) {
    if (values && values.length) {
      if (config[key].conjunction !== false) {
        _.mapValues(values, function (values2) {
          filters.push([key, values2]);
        });
      } else {
        var temp = [];

        _.mapValues(values, function (values2) {
          temp.push([key, values2]);
        });

        filters.push(temp);
      }
    }
  });

  _.mapValues(input.not_filters, function (values, key) {
    if (values && values.length) {
      _.mapValues(values, function (values2) {
        filters.push([key, '-', values2]);
      });
    }
  });

  return filters;
};

var parse_boolean_query = function parse_boolean_query(query) {
  var result = parse_boolean_query_temp(query);
  return result;
};

var parse_boolean_query_temp = function parse_boolean_query_temp(query) {
  var result = booleanParser.parseBooleanQuery(query);
  return _.map(result, function (v1) {
    if (Array.isArray(v1)) {
      return _.map(v1, function (v2) {
        if (Array.isArray(v2)) {
          return _.map(v2, function (v3) {
            return v3;
          });
        } else {
          return v2.split(':');
        }
      });
    } else {
      return v1.split(':');
    }
  });
};

module.exports.parse_boolean_query = parse_boolean_query;
module.exports.input_to_facet_filters = input_to_facet_filters;
module.exports.facets_ids = facets_ids;
module.exports.filters_ids = filters_ids;
module.exports.clone = clone;
module.exports.humanize = humanize;
module.exports.index = index;
module.exports.combination_indexes = combination_indexes;
module.exports.matrix = matrix;
module.exports.filters_matrix = filters_matrix;
module.exports.getBuckets = getBuckets;
module.exports.getFacets = getBuckets;
module.exports.mergeAggregations = mergeAggregations;