'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getSubrecordSearchName = exports.getFirstItem = exports.getPreviousPageSearchDescriptor = exports.getNextPageSearchDescriptor = exports.getListType = exports.searchDescriptorToLocation = exports.advancedSearchConditionToNXQL = exports.fieldConditionToNXQL = exports.rangeFieldConditionToNXQL = exports.booleanConditionToNXQL = exports.valueToNXQL = exports.operatorToNXQL = exports.pathToNXQL = exports.normalizeCondition = exports.normalizeFieldCondition = exports.normalizeRangeFieldCondition = exports.normalizeBooleanCondition = exports.normalizeFieldValue = exports.normalizeListFieldValue = exports.normalizeStringFieldValue = exports.normalizeTimestampRangeEndValue = exports.normalizeTimestampRangeStartValue = undefined;

var _operatorToNXQLMap;

var _immutable = require('immutable');

var _immutable2 = _interopRequireDefault(_immutable);

var _get = require('lodash/get');

var _get2 = _interopRequireDefault(_get);

var _qs = require('qs');

var _qs2 = _interopRequireDefault(_qs);

var _configHelpers = require('./configHelpers');

var _recordDataHelpers = require('./recordDataHelpers');

var _dataTypes = require('../constants/dataTypes');

var _searchOperators = require('../constants/searchOperators');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }

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; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var getDataType = function getDataType(fieldDescriptor, path) {
  return (0, _get2.default)(fieldDescriptor, ['document'].concat(_toConsumableArray(path.split('/')), [_configHelpers.configKey, 'dataType']));
};

var normalizeTimestampRangeStartValue = exports.normalizeTimestampRangeStartValue = function normalizeTimestampRangeStartValue(value) {
  if (value) {
    var timestamp = value.trim();

    if (timestamp) {
      return timestamp.indexOf('T') < 0 ? timestamp + 'T00:00:00.000' : timestamp;
    }
  }

  return null;
};

var normalizeTimestampRangeEndValue = exports.normalizeTimestampRangeEndValue = function normalizeTimestampRangeEndValue(value) {
  if (value) {
    var timestamp = value.trim();

    if (timestamp) {
      return timestamp.indexOf('T') < 0 ? timestamp + 'T23:59:59.999' : timestamp;
    }
  }

  return null;
};

var normalizeStringFieldValue = exports.normalizeStringFieldValue = function normalizeStringFieldValue(value) {
  var trimmed = void 0;

  if (value) {
    trimmed = value.trim();
  }

  return trimmed || null;
};

var normalizeListFieldValue = exports.normalizeListFieldValue = function normalizeListFieldValue(list) {
  var filtered = void 0;

  if (list) {
    filtered = list.map(function (value) {
      return normalizeStringFieldValue(value);
    }).filter(function (value) {
      return !!value;
    });
  }

  if (!filtered || filtered.size === 0) {
    return null;
  }

  if (filtered.size === 1) {
    return filtered.first();
  }

  return filtered;
};

var normalizeFieldValue = exports.normalizeFieldValue = function normalizeFieldValue(value) {
  return _immutable2.default.List.isList(value) ? normalizeListFieldValue(value) : normalizeStringFieldValue(value);
};

var normalizeBooleanCondition = exports.normalizeBooleanCondition = function normalizeBooleanCondition(fieldDescriptor, condition) {
  var childConditions = condition.get('value');

  if (childConditions) {
    /* Gotta do this mutual recursion */
    /* eslint-disable no-use-before-define */
    childConditions = childConditions.map(function (childCondition) {
      return normalizeCondition(fieldDescriptor, childCondition);
    }).filter(function (childCondition) {
      return !!childCondition;
    });
    /* eslint-enable no-use-before-define */
  }

  if (childConditions && childConditions.size > 0) {
    if (childConditions.size > 1) {
      return condition.set('value', childConditions);
    }

    return childConditions.get(0);
  }

  return null;
};

var normalizeRangeFieldCondition = exports.normalizeRangeFieldCondition = function normalizeRangeFieldCondition(fieldDescriptor, condition) {
  var value = condition.get('value');

  if (value) {
    var path = condition.get('path');
    var dataType = getDataType(fieldDescriptor, path);

    var startValue = value.get(0);
    var endValue = value.get(1);

    if (dataType === _dataTypes.DATA_TYPE_DATETIME) {
      startValue = normalizeTimestampRangeStartValue(startValue);
      endValue = normalizeTimestampRangeEndValue(endValue);
    } else {
      startValue = normalizeStringFieldValue(startValue);
      endValue = normalizeStringFieldValue(endValue);
    }

    if (!startValue && !endValue) {
      return null;
    }

    if (!startValue) {
      return _immutable2.default.Map({
        op: _searchOperators.OP_LTE,
        path: condition.get('path'),
        value: endValue
      });
    }

    if (!endValue) {
      return _immutable2.default.Map({
        op: _searchOperators.OP_GTE,
        path: condition.get('path'),
        value: startValue
      });
    }

    return condition.set('value', _immutable2.default.List([startValue, endValue]));
  }

  return null;
};

var normalizeFieldCondition = exports.normalizeFieldCondition = function normalizeFieldCondition(fieldDescriptor, condition) {
  var value = normalizeFieldValue(condition.get('value'));

  if (value) {
    return condition.set('value', value);
  }

  return null;
};

var normalizeCondition = exports.normalizeCondition = function normalizeCondition(fieldDescriptor, condition) {
  if (condition) {
    var operator = condition.get('op');

    switch (operator) {
      case _searchOperators.OP_AND:
      case _searchOperators.OP_OR:
        return normalizeBooleanCondition(fieldDescriptor, condition);
      case _searchOperators.OP_RANGE:
        return normalizeRangeFieldCondition(fieldDescriptor, condition);
      default:
        return normalizeFieldCondition(fieldDescriptor, condition);
    }
  }

  return null;
};

var operatorToNXQLMap = (_operatorToNXQLMap = {}, _defineProperty(_operatorToNXQLMap, _searchOperators.OP_AND, 'AND'), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_OR, 'OR'), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_EQ, '='), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_LT, '<'), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_LTE, '<='), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_GT, '>'), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_GTE, '>='), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_MATCH, 'ILIKE'), _defineProperty(_operatorToNXQLMap, _searchOperators.OP_RANGE, 'BETWEEN'), _operatorToNXQLMap);

var pathToNXQL = exports.pathToNXQL = function pathToNXQL(fieldDescriptor, path) {
  var _path$split = path.split('/'),
      _path$split2 = _toArray(_path$split),
      partName = _path$split2[0],
      pathInPartArray = _path$split2.slice(1);

  var nxqlPartName = partName.substr(0, 4) === _recordDataHelpers.NS_PREFIX + ':' ? partName.substr(4) : partName;

  var partDescriptor = (0, _get2.default)(fieldDescriptor, ['document', partName]);
  var nxqlPathInPartArray = [];

  for (var i = 0; i < pathInPartArray.length; i += 1) {
    var fieldName = pathInPartArray[i];
    var fieldPath = pathInPartArray.slice(0, i + 1);
    var fieldConfig = (0, _get2.default)(partDescriptor, [].concat(_toConsumableArray(fieldPath), [_configHelpers.configKey]));
    var repeating = (0, _get2.default)(fieldConfig, 'repeating');

    nxqlPathInPartArray.push(repeating ? '*' : fieldName);
  }

  var nxqlPath = nxqlPathInPartArray.join('/');

  return nxqlPartName + ':' + nxqlPath;
};

var operatorToNXQL = exports.operatorToNXQL = function operatorToNXQL(operator) {
  return operatorToNXQLMap[operator];
};

var valueToNXQL = exports.valueToNXQL = function valueToNXQL(value) {
  var dataType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _dataTypes.DATA_TYPE_STRING;

  var nxqlValue = void 0;

  if (dataType === _dataTypes.DATA_TYPE_DATETIME) {
    // Timestamp values in searches must be given in UTC.

    nxqlValue = new Date(Date.parse(value)).toISOString();

    return 'TIMESTAMP "' + nxqlValue + '"';
  }

  if (dataType === _dataTypes.DATA_TYPE_INT) {
    nxqlValue = parseInt(value, 10);
  } else if (dataType === _dataTypes.DATA_TYPE_FLOAT) {
    nxqlValue = parseFloat(value);
  } else if (dataType === _dataTypes.DATA_TYPE_BOOL) {
    nxqlValue = value ? 1 : 0;
  } else {
    nxqlValue = value;
  }

  return JSON.stringify(nxqlValue);
};

var booleanConditionToNXQL = exports.booleanConditionToNXQL = function booleanConditionToNXQL(fieldDescriptor, condition) {
  var operator = condition.get('op');
  var nxqlOp = operatorToNXQL(operator);

  if (nxqlOp) {
    var childConditions = condition.get('value');

    var nxql = childConditions.map(function (childCondition) {
      return (
        /* Gotta do this mutual recursion */
        /* eslint-disable no-use-before-define */
        advancedSearchConditionToNXQL(fieldDescriptor, childCondition)
      );
    })
    /* eslint-enable no-use-before-define */
    .join(' ' + nxqlOp + ' ');

    return '(' + nxql + ')';
  }

  return '';
};

var rangeFieldConditionToNXQL = exports.rangeFieldConditionToNXQL = function rangeFieldConditionToNXQL(fieldDescriptor, condition) {
  var path = condition.get('path');
  var operator = condition.get('op');
  var dataType = getDataType(fieldDescriptor, path);
  var values = condition.get('value');

  var nxqlPath = pathToNXQL(fieldDescriptor, path);
  var nxqlOp = operatorToNXQL(operator);

  var startValue = values.get(0);
  var endValue = values.get(1);

  var nxqlValue = [startValue, endValue].map(function (value) {
    return valueToNXQL(value, dataType);
  }).join(' AND ');

  return nxqlPath + ' ' + nxqlOp + ' ' + nxqlValue;
};

var fieldConditionToNXQL = exports.fieldConditionToNXQL = function fieldConditionToNXQL(fieldDescriptor, condition) {
  var path = condition.get('path');
  var dataType = getDataType(fieldDescriptor, path);

  var operator = condition.get('op');
  var value = condition.get('value');

  if (_immutable2.default.List.isList(value)) {
    // Expand or'ed values.

    return value.map(function (valueInstance) {
      return fieldConditionToNXQL(fieldDescriptor, condition.set('value', valueInstance));
    }).join(' OR ');
  }

  if (operator === _searchOperators.OP_CONTAIN) {
    operator = _searchOperators.OP_MATCH;
    value = '%' + value + '%';
  }

  var nxqlPath = pathToNXQL(fieldDescriptor, path);
  var nxqlOp = operatorToNXQL(operator);
  var nxqlValue = valueToNXQL(value, dataType);

  return nxqlPath + ' ' + nxqlOp + ' ' + nxqlValue;
};

var advancedSearchConditionToNXQL = exports.advancedSearchConditionToNXQL = function advancedSearchConditionToNXQL(fieldDescriptor, condition) {
  if (condition) {
    var operator = condition.get('op');

    switch (operator) {
      case _searchOperators.OP_AND:
      case _searchOperators.OP_OR:
        return booleanConditionToNXQL(fieldDescriptor, condition);
      case _searchOperators.OP_RANGE:
        return rangeFieldConditionToNXQL(fieldDescriptor, condition);
      default:
        return fieldConditionToNXQL(fieldDescriptor, condition);
    }
  }

  return null;
};

/**
 * Converts a search descriptor to a React Router location.
 */
var searchDescriptorToLocation = exports.searchDescriptorToLocation = function searchDescriptorToLocation(searchDescriptor) {
  var recordType = searchDescriptor.get('recordType');
  var vocabulary = searchDescriptor.get('vocabulary');
  var csid = searchDescriptor.get('csid');
  var subresource = searchDescriptor.get('subresource');
  var searchQuery = searchDescriptor.get('searchQuery');

  var pathParts = ['/list', recordType, vocabulary, csid, subresource];
  var pathname = pathParts.filter(function (part) {
    return !!part;
  }).join('/');

  var as = searchQuery.get('as');
  var p = searchQuery.get('p');

  var asParam = as ? JSON.stringify(as.toJS()) : undefined;

  var pParam = typeof p === 'number' ? (p + 1).toString() : undefined;

  var queryString = _qs2.default.stringify(searchQuery.set('as', asParam).set('p', pParam).toJS());

  return {
    pathname: pathname,
    search: '?' + queryString
  };
};

var getListType = exports.getListType = function getListType(config, searchDescriptor) {
  if (searchDescriptor) {
    var subresource = searchDescriptor.get('subresource');

    if (subresource) {
      return (0, _get2.default)(config, ['subresources', subresource, 'listType']);
    }
  }

  return 'common';
};

/**
 * Returns a search descriptor that describes the next page of the search described by a given
 * descriptor.
 */
var getNextPageSearchDescriptor = exports.getNextPageSearchDescriptor = function getNextPageSearchDescriptor(searchDescriptor) {
  var p = searchDescriptor.getIn(['searchQuery', 'p']) || 0;

  return searchDescriptor.setIn(['searchQuery', 'p'], p + 1);
};

/**
 * Returns a search descriptor that describes the previous page of the search described by a given
 * descriptor. Null is returned if the given descriptor is on the first page.
 */
var getPreviousPageSearchDescriptor = exports.getPreviousPageSearchDescriptor = function getPreviousPageSearchDescriptor(searchDescriptor) {
  var p = searchDescriptor.getIn(['searchQuery', 'p']) || 0;

  if (p <= 0) {
    return null;
  }

  return searchDescriptor.setIn(['searchQuery', 'p'], p - 1);
};

/**
 * Returns the first item in list result data.
 */
var getFirstItem = exports.getFirstItem = function getFirstItem(config, listData) {
  var listType = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'common';

  if (!listData) {
    return null;
  }

  var listTypeConfig = (0, _get2.default)(config, ['listTypes', listType]);

  if (!listTypeConfig) {
    return null;
  }

  var listNodeName = listTypeConfig.listNodeName,
      itemNodeName = listTypeConfig.itemNodeName;


  var items = listData.getIn([listNodeName, itemNodeName]);

  var item = _immutable2.default.List.isList(items) ? items.first() : items;

  return item;
};

/**
 * Returns a name for a search for subrecords.
 */
var getSubrecordSearchName = exports.getSubrecordSearchName = function getSubrecordSearchName(csid, subrecordName) {
  return 'subrecord/' + csid + '/' + subrecordName;
};