'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.findVocabularyUses = exports.findFieldConfigInPart = exports.findField = exports.getDefaultSearchVocabulary = exports.getDefaultSearchRecordType = exports.validateLocation = exports.isUtility = exports.isAuthority = exports.getRequiredMessage = exports.getFieldComputer = exports.getFieldCustomValidator = exports.getFieldDataType = exports.isFieldRequired = exports.isFieldRepeating = exports.isFieldCloneable = exports.getDefaults = exports.getDefaultValue = exports.getVocabularyConfigByServicePath = exports.getVocabularyConfigByShortID = exports.getRecordTypeNameByUri = exports.getRecordTypeConfigByUri = exports.getRecordTypeConfigByServicePath = exports.getRecordTypeNameByServiceObjectName = exports.getRecordTypeConfigByServiceObjectName = exports.getRecordTypeConfigByServiceDocumentName = exports.normalizeConfig = exports.initConfig = exports.mergeConfig = exports.applyPlugins = exports.applyPlugin = exports.evaluatePlugin = exports.dataPathToFieldDescriptorPath = exports.configKey = undefined;

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _immutable = require('immutable');

var _immutable2 = _interopRequireDefault(_immutable);

var _mergeWith = require('lodash/mergeWith');

var _mergeWith2 = _interopRequireDefault(_mergeWith);

var _flatMap = require('lodash/flatMap');

var _flatMap2 = _interopRequireDefault(_flatMap);

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

var _get2 = _interopRequireDefault(_get);

var _set = require('lodash/set');

var _set2 = _interopRequireDefault(_set);

var _warning = require('warning');

var _warning2 = _interopRequireDefault(_warning);

var _errorCodes = require('../constants/errorCodes');

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

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

var _csidHelpers = require('./csidHelpers');

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

var onlyDigitsPattern = /^\d+$/;
var isNotNumeric = function isNotNumeric(string) {
  return !onlyDigitsPattern.test(string);
};

var configKey = exports.configKey = '[config]';

var dataPathToFieldDescriptorPath = exports.dataPathToFieldDescriptorPath = function dataPathToFieldDescriptorPath(dataPath) {
  return dataPath.filter(isNotNumeric);
};

var evaluatePlugin = exports.evaluatePlugin = function evaluatePlugin(plugin, pluginContext) {
  var pluginType = typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin);

  var isValidType = plugin && (pluginType === 'function' || pluginType === 'object' && !Array.isArray(plugin));

  process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(isValidType, 'A plugin must be an object or a function.') : void 0;

  if (!isValidType) {
    return {};
  }

  return pluginType === 'object' ? plugin : plugin(pluginContext);
};

var applyPlugin = exports.applyPlugin = function applyPlugin(targetConfig, plugin) {
  var pluginContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

  var pluginConfigContribution = evaluatePlugin(plugin, pluginContext);

  /* Gotta do this mutual recursion */
  /* eslint-disable no-use-before-define */
  return mergeConfig(targetConfig, pluginConfigContribution, pluginContext);
  /* eslint-enable no-use-before-define */
};

var applyPlugins = exports.applyPlugins = function applyPlugins(targetConfig, plugins) {
  var pluginContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

  var isArray = Array.isArray(plugins);

  process.env.NODE_ENV !== 'production' ? (0, _warning2.default)(isArray, 'Plugins must be an array.') : void 0;

  if (!isArray) {
    return targetConfig;
  }

  return plugins.reduce(function (updatedConfig, plugin) {
    return applyPlugin(updatedConfig, plugin, pluginContext);
  }, targetConfig);
};

var configMerger = function configMerger(objValue, srcValue, key) {
  if (Array.isArray(objValue)) {
    // Don't merge arrays. Just override with the source value.
    return srcValue;
  }

  if (key === 'advancedSearch') {
    // Don't merge advanced serach config. Just override with the source value.
    return srcValue;
  }

  if (_react2.default.isValidElement(objValue)) {
    // Don't merge React elements, e.g. in form templates. Just override with the source value.
    return srcValue;
  }

  return undefined;
};

var mergeConfig = exports.mergeConfig = function mergeConfig(targetConfig, sourceConfig) {
  var pluginContext = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};

  // eslint-disable-next-line no-param-reassign
  pluginContext.config = targetConfig;

  var resolvedSourceConfig = typeof sourceConfig === 'function' ? sourceConfig(pluginContext) : sourceConfig;

  var pluginsAppliedConfig = resolvedSourceConfig && 'plugins' in resolvedSourceConfig ? applyPlugins(targetConfig, resolvedSourceConfig.plugins, pluginContext) : targetConfig;

  var mergedConfig = (0, _mergeWith2.default)({}, pluginsAppliedConfig, resolvedSourceConfig, configMerger);

  delete mergedConfig.plugins;

  return mergedConfig;
};

var initConfig = exports.initConfig = function initConfig(config, pluginContext) {
  return mergeConfig({}, config, pluginContext);
};

/*
 * Normalize a configuration object. This function mutates the argument configuration.
 * - Delete any record type or vocabulary that is disabled
 * - Set the name property of each recordTypes entry to its key
 * - Set the name property of each vocabularies entry to its key
 */
var normalizeConfig = exports.normalizeConfig = function normalizeConfig(config) {
  var recordTypes = config.recordTypes;

  Object.keys(recordTypes).forEach(function (recordTypeName) {
    var recordType = recordTypes[recordTypeName];

    if (recordType.disabled) {
      delete recordTypes[recordTypeName];
    } else {
      var vocabularies = recordType.vocabularies;

      recordType.name = recordTypeName;

      if (vocabularies) {
        Object.keys(vocabularies).forEach(function (vocabularyName) {
          var vocabulary = vocabularies[vocabularyName];

          if (vocabulary.disabled) {
            delete vocabularies[vocabularyName];
          } else {
            vocabularies[vocabularyName].name = vocabularyName;
          }
        });
      }
    }
  });

  return config;
};

var getRecordTypeConfigByServiceDocumentName = exports.getRecordTypeConfigByServiceDocumentName = function getRecordTypeConfigByServiceDocumentName(config, documentName) {
  if (!documentName) {
    return undefined;
  }

  if (!config.recordTypesByServiceDocumentName) {
    var recordTypesByServiceDocumentName = {};
    var recordTypes = config.recordTypes;


    Object.keys(recordTypes).forEach(function (recordType) {
      var recordTypeConfig = recordTypes[recordType];
      var serviceConfig = recordTypeConfig.serviceConfig;


      recordTypesByServiceDocumentName[serviceConfig.documentName] = recordTypeConfig;
    });

    /* eslint-disable no-param-reassign */
    config.recordTypesByServiceDocumentName = recordTypesByServiceDocumentName;
    /* eslint-enable no-param-reassign */
  }

  return config.recordTypesByServiceDocumentName[documentName];
};

var getRecordTypeConfigByServiceObjectName = exports.getRecordTypeConfigByServiceObjectName = function getRecordTypeConfigByServiceObjectName(config, objectName) {
  if (!objectName) {
    return undefined;
  }

  if (!config.recordTypesByServiceObjectName) {
    var recordTypesByServiceObjectName = {};
    var recordTypes = config.recordTypes;


    Object.keys(recordTypes).forEach(function (recordType) {
      var recordTypeConfig = recordTypes[recordType];

      recordTypesByServiceObjectName[recordTypeConfig.serviceConfig.objectName] = recordTypeConfig;
    });

    /* eslint-disable no-param-reassign */
    config.recordTypesByServiceObjectName = recordTypesByServiceObjectName;
    /* eslint-enable no-param-reassign */
  }

  return config.recordTypesByServiceObjectName[objectName];
};

var getRecordTypeNameByServiceObjectName = exports.getRecordTypeNameByServiceObjectName = function getRecordTypeNameByServiceObjectName(config, objectName) {
  var recordTypeConfig = getRecordTypeConfigByServiceObjectName(config, objectName);

  return recordTypeConfig ? recordTypeConfig.name : undefined;
};

var getRecordTypeConfigByServicePath = exports.getRecordTypeConfigByServicePath = function getRecordTypeConfigByServicePath(config, servicePath) {
  if (!servicePath) {
    return undefined;
  }

  if (!config.recordTypesByServicePath) {
    var recordTypesByServicePath = {};
    var recordTypes = config.recordTypes;


    Object.keys(recordTypes).forEach(function (recordType) {
      var recordTypeConfig = recordTypes[recordType];

      recordTypesByServicePath[recordTypeConfig.serviceConfig.servicePath] = recordTypeConfig;
    });

    /* eslint-disable no-param-reassign */
    config.recordTypesByServicePath = recordTypesByServicePath;
    /* eslint-enable no-param-reassign */
  }

  return config.recordTypesByServicePath[servicePath];
};

var getRecordTypeConfigByUri = exports.getRecordTypeConfigByUri = function getRecordTypeConfigByUri(config, uri) {
  if (!uri) {
    return undefined;
  }

  var servicePath = uri.split('/', 2)[1];

  return getRecordTypeConfigByServicePath(config, servicePath);
};

var getRecordTypeNameByUri = exports.getRecordTypeNameByUri = function getRecordTypeNameByUri(config, uri) {
  var recordTypeConfig = getRecordTypeConfigByUri(config, uri);

  return recordTypeConfig ? recordTypeConfig.name : undefined;
};

var getVocabularyConfigByShortID = exports.getVocabularyConfigByShortID = function getVocabularyConfigByShortID(recordTypeConfig, shortID) {
  if (!shortID) {
    return undefined;
  }

  if (!recordTypeConfig.vocabulariesByShortID) {
    var vocabulariesByShortID = {};
    var vocabularies = recordTypeConfig.vocabularies;


    if (vocabularies) {
      Object.keys(vocabularies).forEach(function (vocabulary) {
        var vocabularyConfig = vocabularies[vocabulary];
        var servicePath = vocabularyConfig.serviceConfig.servicePath;


        if (servicePath && servicePath.indexOf('urn:cspace:name(') === 0 && servicePath.lastIndexOf(')') === servicePath.length - 1) {
          var vocabularyShortID = servicePath.substring(16, servicePath.length - 1);

          vocabulariesByShortID[vocabularyShortID] = vocabularyConfig;
        }
      });

      /* eslint-disable no-param-reassign */
      recordTypeConfig.vocabulariesByShortID = vocabulariesByShortID;
      /* eslint-enable no-param-reassign */
    }
  }

  return recordTypeConfig.vocabulariesByShortID[shortID];
};

var vocabularyServicePathPattern = /^urn:cspace:name\((.*?)\)$/;

var getVocabularyConfigByServicePath = exports.getVocabularyConfigByServicePath = function getVocabularyConfigByServicePath(recordTypeConfig, servicePath) {
  var match = vocabularyServicePathPattern.exec(servicePath);

  if (!match) {
    return undefined;
  }

  var shortID = match[1];

  return getVocabularyConfigByShortID(recordTypeConfig, shortID);
};

var getDefaultValue = exports.getDefaultValue = function getDefaultValue(fieldDescriptor) {
  var config = fieldDescriptor[configKey];

  if (config) {
    var defaultValue = config.defaultValue;


    if ((typeof defaultValue === 'undefined' ? 'undefined' : _typeof(defaultValue)) === 'object' && !_immutable2.default.Map.isMap(defaultValue)) {
      // If an object is supplied as a default value, convert it to an immutable map.

      return _immutable2.default.fromJS(defaultValue);
    }

    return defaultValue;
  }

  return undefined;
};

var getDefaults = exports.getDefaults = function getDefaults(fieldDescriptor) {
  var currentPath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

  var results = [];

  var defaultValue = getDefaultValue(fieldDescriptor);

  if (typeof defaultValue !== 'undefined') {
    results = results.concat({
      path: currentPath,
      value: defaultValue
    });
  }

  var childKeys = Object.keys(fieldDescriptor).filter(function (key) {
    return key !== configKey;
  });

  childKeys.forEach(function (childKey) {
    var childPath = currentPath.concat(childKey);
    var childfieldDescriptor = fieldDescriptor[childKey];
    var childResults = getDefaults(childfieldDescriptor, childPath);

    results = results.concat(childResults);
  });

  return results;
};

var isFieldCloneable = exports.isFieldCloneable = function isFieldCloneable(fieldDescriptor) {
  var config = fieldDescriptor[configKey];

  if (config && 'cloneable' in config) {
    return config.cloneable;
  }

  return true;
};

var isFieldRepeating = exports.isFieldRepeating = function isFieldRepeating(fieldDescriptor) {
  var config = fieldDescriptor[configKey];

  if (config && 'repeating' in config) {
    return config.repeating;
  }

  return false;
};

var isFieldRequired = exports.isFieldRequired = function isFieldRequired(fieldDescriptor, recordData) {
  var required = (0, _get2.default)(fieldDescriptor, [configKey, 'required']);

  return !!(typeof required === 'function' ? required(recordData) : required);
};

var getFieldDataType = exports.getFieldDataType = function getFieldDataType(fieldDescriptor) {
  var type = void 0;

  var fieldConfig = fieldDescriptor[configKey];

  if (fieldConfig) {
    type = fieldConfig.dataType;
  }

  if (!type) {
    // Check if there are child field descriptors. If so, default to map.

    var keys = Object.keys(fieldDescriptor);

    for (var i = 0; i < keys.length; i += 1) {
      if (keys[i] !== configKey) {
        type = _dataTypes.DATA_TYPE_MAP;
        break;
      }
    }
  }

  if (!type) {
    // Default to string.

    type = _dataTypes.DATA_TYPE_STRING;
  }

  return type;
};

var getFieldCustomValidator = exports.getFieldCustomValidator = function getFieldCustomValidator(fieldDescriptor) {
  var validator = void 0;

  var fieldConfig = fieldDescriptor[configKey];

  if (fieldConfig) {
    validator = fieldConfig.validate;
  }

  return validator;
};

var getFieldComputer = exports.getFieldComputer = function getFieldComputer(fieldDescriptor) {
  var computer = void 0;

  var fieldConfig = fieldDescriptor[configKey];

  if (fieldConfig) {
    computer = fieldConfig.compute;
  }

  return computer;
};

var getRequiredMessage = exports.getRequiredMessage = function getRequiredMessage(fieldDescriptor) {
  return (0, _get2.default)(fieldDescriptor, [configKey, 'messages', 'required']);
};

var isAuthority = exports.isAuthority = function isAuthority(recordTypeConfig) {
  return (0, _get2.default)(recordTypeConfig, ['serviceConfig', 'serviceType']) === 'authority';
};

var isUtility = exports.isUtility = function isUtility(recordTypeConfig) {
  return (0, _get2.default)(recordTypeConfig, ['serviceConfig', 'serviceType']) === 'utility';
};

var validateLocation = exports.validateLocation = function validateLocation(config, location) {
  var recordType = location.recordType,
      vocabulary = location.vocabulary,
      csid = location.csid,
      subresource = location.subresource,
      relatedRecordType = location.relatedRecordType,
      relatedCsid = location.relatedCsid;


  var recordTypeConfig = (0, _get2.default)(config, ['recordTypes', recordType]);

  if (!recordTypeConfig || recordTypeConfig.disabled) {
    return {
      error: {
        recordType: recordType,
        code: _errorCodes.ERR_UNKNOWN_RECORD_TYPE
      }
    };
  }

  if (isAuthority(recordTypeConfig)) {
    if (!vocabulary) {
      return {
        error: {
          recordType: recordType,
          code: _errorCodes.ERR_MISSING_VOCABULARY
        }
      };
    }

    var vocabularyConfig = (0, _get2.default)(recordTypeConfig, ['vocabularies', vocabulary]);

    if (!vocabularyConfig || vocabularyConfig.disabled) {
      return {
        error: {
          recordType: recordType,
          vocabulary: vocabulary,
          code: _errorCodes.ERR_UNKNOWN_VOCABULARY
        }
      };
    }
  } else if (vocabulary) {
    return {
      error: {
        recordType: recordType,
        vocabulary: vocabulary,
        code: _errorCodes.ERR_UNKNOWN_VOCABULARY
      }
    };
  }

  if (csid && !(0, _csidHelpers.isCsid)(csid) && !(0, _csidHelpers.isUrnCsid)(csid)) {
    return {
      error: {
        csid: csid,
        code: _errorCodes.ERR_INVALID_CSID
      }
    };
  }

  if (subresource) {
    var subresourceConfig = (0, _get2.default)(config, ['subresources', subresource]);

    if (!subresourceConfig) {
      return {
        error: {
          subresource: subresource,
          code: _errorCodes.ERR_UNKNOWN_SUBRESOURCE
        }
      };
    }
  }

  if (relatedRecordType) {
    var serviceType = (0, _get2.default)(recordTypeConfig, ['serviceConfig', 'serviceType']);
    var relatedServiceType = (0, _get2.default)(config, ['recordTypes', relatedRecordType, 'serviceConfig', 'serviceType']);

    if (serviceType !== 'procedure' && serviceType !== 'object' || relatedServiceType !== 'procedure' && relatedServiceType !== 'object') {
      return {
        error: {
          recordType: recordType,
          relatedRecordType: relatedRecordType,
          code: _errorCodes.ERR_INVALID_RELATED_TYPE
        }
      };
    }
  }

  if (relatedCsid && !(0, _csidHelpers.isCsid)(relatedCsid)) {
    return {
      error: {
        csid: relatedCsid,
        code: _errorCodes.ERR_INVALID_CSID
      }
    };
  }

  return {};
};

var getDefaultSearchRecordType = exports.getDefaultSearchRecordType = function getDefaultSearchRecordType(config) {
  var recordTypes = config.recordTypes;

  var names = Object.keys(recordTypes);

  var defaultSearchRecordType = void 0;

  for (var i = 0; i < names.length; i += 1) {
    var name = names[i];

    if (recordTypes[name].defaultForSearch) {
      defaultSearchRecordType = name;
      break;
    }
  }

  return defaultSearchRecordType || names[0];
};

var getDefaultSearchVocabulary = exports.getDefaultSearchVocabulary = function getDefaultSearchVocabulary(recordTypeConfig) {
  var vocabularies = recordTypeConfig.vocabularies;

  var names = Object.keys(vocabularies);

  var defaultSearchVocabulary = void 0;

  for (var i = 0; i < names.length; i += 1) {
    var name = names[i];

    if (vocabularies[name].defaultForSearch) {
      defaultSearchVocabulary = name;
      break;
    }
  }

  return defaultSearchVocabulary || names[0];
};

var findField = exports.findField = function findField(parentFieldDescriptor, fieldName) {
  var keys = Object.keys(parentFieldDescriptor);

  for (var i = 0; i < keys.length; i += 1) {
    var key = keys[i];
    var value = parentFieldDescriptor[key];

    if (key === fieldName) {
      return value;
    }

    if (key !== configKey) {
      var fieldDescriptor = findField(value, fieldName);

      if (fieldDescriptor) {
        return fieldDescriptor;
      }
    }
  }

  return null;
};

var findFieldConfigInPart = exports.findFieldConfigInPart = function findFieldConfigInPart(recordTypeConfig, partName, fieldName) {
  var partDescriptor = (0, _get2.default)(recordTypeConfig, ['fields', 'document', _recordDataHelpers.NS_PREFIX + ':' + partName]);

  if (!partDescriptor) {
    return null;
  }

  // 1. Look for the field as a direct child of the part.

  var fieldDescriptor = partDescriptor[fieldName];

  // 2. Check for a memoized result.

  if (!fieldDescriptor) {
    fieldDescriptor = (0, _get2.default)(recordTypeConfig, ['fieldsByPart', partName, fieldName]);
  }

  // 3. Search for a nested field.

  if (typeof fieldDescriptor === 'undefined') {
    fieldDescriptor = findField(partDescriptor, fieldName);

    // Memoize the result.

    (0, _set2.default)(recordTypeConfig, ['fieldsByPart', partName, fieldName], fieldDescriptor);
  }

  return fieldDescriptor ? fieldDescriptor[configKey] : null;
};

var findFieldsWithSource = function findFieldsWithSource(fieldDescriptor, shortId) {
  var fieldsWithSource = (0, _flatMap2.default)(Object.keys(fieldDescriptor).filter(function (key) {
    return key !== configKey;
  }), function (childFieldName) {
    return findFieldsWithSource(fieldDescriptor[childFieldName], shortId);
  });

  var fieldConfig = fieldDescriptor[configKey];
  var source = (0, _get2.default)(fieldConfig, ['view', 'props', 'source']);

  if (source === shortId) {
    fieldsWithSource.push(fieldConfig);
  }

  return fieldsWithSource;
};

var findVocabularyUses = exports.findVocabularyUses = function findVocabularyUses(config, shortId) {
  if (!shortId) {
    return null;
  }

  var uses = {};

  Object.values(config.recordTypes).forEach(function (recordTypeConfig) {
    var fieldDescriptor = recordTypeConfig.fields;

    if (fieldDescriptor) {
      var fields = findFieldsWithSource(recordTypeConfig.fields, shortId);

      if (fields.length > 0) {
        uses[recordTypeConfig.name] = fields;
      }
    }
  });

  return uses;
};