"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.clearRecord = exports.detachSubrecord = exports.saveRecordWithTransition = exports.transitionRecord = exports.deleteRecord = exports.revertRecord = exports.setFieldValue = exports.moveFieldValue = exports.deleteFieldValue = exports.sortFieldInstances = exports.addFieldInstance = exports.saveRecord = exports.createNewSubrecord = exports.createNewRecord = exports.readRecord = exports.validateRecordData = exports.validateFieldValue = exports.computeRecordData = exports.computeFieldValue = void 0;

var _reactIntl = require("react-intl");

var _get = _interopRequireDefault(require("lodash/get"));

var _merge = _interopRequireDefault(require("lodash/merge"));

var _immutable = _interopRequireDefault(require("immutable"));

var _cspace = _interopRequireDefault(require("./cspace"));

var _notificationHelpers = _interopRequireDefault(require("../helpers/notificationHelpers"));

var _getErrorDescription = _interopRequireDefault(require("../helpers/getErrorDescription"));

var _HierarchyReparentNotifier = _interopRequireDefault(require("../components/record/HierarchyReparentNotifier"));

var _notification = require("./notification");

var _search = require("./search");

var _reducers = require("../reducers");

var _configHelpers = require("../helpers/configHelpers");

var _recordDataHelpers = require("../helpers/recordDataHelpers");

var _searchHelpers = require("../helpers/searchHelpers");

var _actionCodes = require("../constants/actionCodes");

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

var _notificationStatusCodes = require("../constants/notificationStatusCodes");

var _prefs = require("./prefs");

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

/* global window */
const deleteMessages = (0, _reactIntl.defineMessages)({
  deleting: {
    "id": "action.record.deleting",
    "defaultMessage": "{hasTitle, select, yes {Deleting {title}\u2026} other {Deleting record\u2026}}"
  },
  errorDeleting: {
    "id": "action.record.errorDeleting",
    "defaultMessage": "{hasTitle, select, yes {Error deleting {title}: {error}} other {Error deleting record: {error}}}"
  },
  deleted: {
    "id": "action.record.deleted",
    "defaultMessage": "{hasTitle, select, yes {Deleted {title}} other {Deleted record}}"
  }
});
const saveMessages = (0, _reactIntl.defineMessages)({
  saving: {
    "id": "action.record.saving",
    "defaultMessage": "{hasTitle, select, yes {Saving {title}\u2026} other {Saving record\u2026}}"
  },
  errorSaving: {
    "id": "action.record.errorSaving",
    "defaultMessage": "{hasTitle, select, yes {Error saving {title}: {error}} other {Error saving record: {error}}}"
  },
  saved: {
    "id": "action.record.saved",
    "defaultMessage": "{hasTitle, select, yes {Saved {title}} other {Saved record}}"
  },
  errorDupRoleName: {
    "id": "action.record.errorDupRoleName",
    "defaultMessage": "Error saving {title}: A role already exists with this name. Please choose a different name."
  }
});
const transitionMessages = {
  delete: (0, _reactIntl.defineMessages)({
    transitioning: {
      "id": "action.record.transition.delete.transitioning",
      "defaultMessage": "{hasTitle, select, yes {Deleting {title}\u2026} other {Deleting record\u2026}}"
    },
    errorTransitioning: {
      "id": "action.record.transition.delete.errorTransitioning",
      "defaultMessage": "{hasTitle, select, yes {Error deleting {title}: {error}} other {Error deleting record: {error}}}"
    },
    transitioned: {
      "id": "action.record.transition.delete.transitioned",
      "defaultMessage": "{hasTitle, select, yes {Deleted {title}} other {Deleted record}}"
    }
  }),
  lock: (0, _reactIntl.defineMessages)({
    transitioning: {
      "id": "action.record.transition.lock.transitioning",
      "defaultMessage": "{hasTitle, select, yes {Locking {title}\u2026} other {Locking record\u2026}}"
    },
    errorTransitioning: {
      "id": "action.record.transition.lock.errorTransitioning",
      "defaultMessage": "{hasTitle, select, yes {Error locking {title}: {error}} other {Error locking record: {error}}}"
    },
    transitioned: {
      "id": "action.record.transition.lock.transitioned",
      "defaultMessage": "{hasTitle, select, yes {Locked {title}} other {Locked record}}"
    }
  })
};

const getSaveErrorNotificationItem = (error, title) => {
  const data = (0, _get.default)(error, ['error', 'response', 'data']);

  if (typeof data === 'string' && data.includes('unique constraint "roles_rolename_tenant_id_key"')) {
    return {
      message: saveMessages.errorDupRoleName,
      values: {
        title
      }
    };
  }

  return {
    message: saveMessages.errorSaving,
    values: {
      title,
      hasTitle: title ? 'yes' : '',
      error: (0, _getErrorDescription.default)(error)
    }
  };
};

const computeFieldValue = (recordTypeConfig, csid, path, value) => (dispatch, getState) => {
  const fieldDescriptor = (0, _get.default)(recordTypeConfig, ['fields', ...(0, _configHelpers.dataPathToFieldDescriptorPath)(path)]);
  const recordData = (0, _reducers.getRecordData)(getState(), csid);
  const subrecordData = (0, _reducers.getSubrecordData)(getState(), csid);
  return (0, _recordDataHelpers.computeField)(value, [], recordData, subrecordData, fieldDescriptor, true).then(computedValue => {
    if (typeof computedValue !== 'undefined') {
      dispatch({
        type: _actionCodes.FIELD_COMPUTE_FULFILLED,
        payload: computedValue,
        meta: {
          csid,
          path
        }
      });
    }
  }).catch(error => {
    dispatch({
      type: _actionCodes.FIELD_COMPUTE_REJECTED,
      payload: {
        code: _errorCodes.ERR_COMPUTE,
        error
      },
      meta: {
        csid,
        path
      }
    }); // TODO: Show an error notification?
  });
};

exports.computeFieldValue = computeFieldValue;

const computeRecordData = (recordTypeConfig, csid) => (dispatch, getState) => {
  const recordData = (0, _reducers.getRecordData)(getState(), csid);
  return dispatch(computeFieldValue(recordTypeConfig, csid, [], recordData));
};

exports.computeRecordData = computeRecordData;

const validateFieldValue = (recordTypeConfig, csid, path, value) => (dispatch, getState) => {
  const fieldDescriptor = (0, _get.default)(recordTypeConfig, ['fields', ...(0, _configHelpers.dataPathToFieldDescriptorPath)(path)]);
  const recordData = (0, _reducers.getRecordData)(getState(), csid);
  return (0, _recordDataHelpers.validateField)(value, [], recordData, fieldDescriptor, true).then(errors => {
    if (errors) {
      dispatch({
        type: _actionCodes.VALIDATION_FAILED,
        payload: errors,
        meta: {
          csid,
          path
        }
      });
      dispatch((0, _notification.showValidationNotification)(recordTypeConfig.name, csid));
    } else {
      dispatch({
        type: _actionCodes.VALIDATION_PASSED,
        meta: {
          csid,
          path
        }
      });
      dispatch((0, _notification.removeValidationNotification)());
    }
  });
};

exports.validateFieldValue = validateFieldValue;

const validateRecordData = (recordTypeConfig, csid) => (dispatch, getState) => {
  const recordData = (0, _reducers.getRecordData)(getState(), csid);
  return dispatch(validateFieldValue(recordTypeConfig, csid, [], recordData));
};

exports.validateRecordData = validateRecordData;

const initializeSubrecords = (config, recordTypeConfig, vocabularyConfig, csid) => (dispatch, getState) => {
  const {
    subrecords
  } = recordTypeConfig;

  if (!subrecords) {
    return Promise.resolve();
  }

  const data = (0, _reducers.getRecordData)(getState(), csid);
  return Promise.all(Object.entries(subrecords).map(entry => {
    const [subrecordName, subrecordConfig] = entry;
    const {
      csidField,
      subresource,
      recordType: subrecordType,
      vocabulary: subrecordVocabulary
    } = subrecordConfig;
    let subrecordCsidPromise = null;

    if (csidField) {
      subrecordCsidPromise = Promise.resolve((0, _recordDataHelpers.deepGet)(data, csidField));
    } else if (subresource) {
      const searchDescriptor = _immutable.default.fromJS({
        csid,
        subresource,
        recordType: recordTypeConfig.name,
        vocabulary: vocabularyConfig.name,
        searchQuery: {
          // Set page size to 1, and page to 0. This assumes we'll only ever care about the first
          // result.
          p: 0,
          size: 1
        }
      });

      const searchName = (0, _searchHelpers.getSubrecordSearchName)(csid, subrecordName);
      const listType = subresource ? (0, _get.default)(config, ['subresources', subresource, 'listType']) : null;
      subrecordCsidPromise = dispatch((0, _search.search)(config, searchName, searchDescriptor, listType)).then(() => {
        const result = (0, _reducers.getSearchResult)(getState(), searchName, searchDescriptor);
        let subrecordCsid;

        if (result) {
          // Read the csid of the first item.
          const firstItem = (0, _searchHelpers.getFirstItem)(config, result, listType);

          if (firstItem) {
            subrecordCsid = firstItem.get('csid');
          }
        }

        return subrecordCsid;
      });
    }

    if (subrecordCsidPromise) {
      return subrecordCsidPromise.then(subrecordCsid => {
        const subrecordTypeConfig = (0, _get.default)(config, ['recordTypes', subrecordType]);
        const subrecordVocabularyConfig = (0, _get.default)(subrecordTypeConfig, ['vocabularies', subrecordVocabulary]);

        if (subrecordCsid) {
          return (// eslint-disable-next-line no-use-before-define
            dispatch(readRecord(config, subrecordTypeConfig, subrecordVocabularyConfig, subrecordCsid)).then(() => dispatch({
              type: _actionCodes.SUBRECORD_READ_FULFILLED,
              meta: {
                csid,
                subrecordCsid,
                subrecordName
              }
            }))
          );
        } // No existing subrecord. Create one as a default.
        // eslint-disable-next-line no-use-before-define


        return dispatch(createNewSubrecord(config, csid, csidField, subrecordName, subrecordTypeConfig, subrecordVocabularyConfig, undefined, true));
      });
    }

    return Promise.resolve();
  }));
};

const doRead = (recordTypeConfig, vocabularyConfig, csid) => {
  const {
    serviceType,
    servicePath: recordServicePath
  } = recordTypeConfig.serviceConfig;
  const vocabularyServicePath = vocabularyConfig ? vocabularyConfig.serviceConfig.servicePath : null;
  const pathParts = [recordServicePath];

  if (vocabularyServicePath) {
    pathParts.push(vocabularyServicePath);
    pathParts.push('items');
  }

  pathParts.push(csid);
  const path = pathParts.join('/');
  const requestConfig = {
    params: {
      wf_deleted: false
    }
  };

  if (serviceType === 'authority' || serviceType === 'object') {
    requestConfig.params.showRelations = true;
    requestConfig.params.pgSz = 0;
  }

  if (recordTypeConfig.requestConfig) {
    (0, _merge.default)(requestConfig, recordTypeConfig.requestConfig('read'));
  }

  return (0, _cspace.default)().read(path, requestConfig);
};

const readRecord = (config, recordTypeConfig, vocabularyConfig, csid) => (dispatch, getState) => {
  const existingData = (0, _reducers.getRecordData)(getState(), csid);

  if (existingData) {
    return Promise.resolve(existingData);
  }

  if ((0, _reducers.isRecordReadPending)(getState(), csid)) {
    return Promise.resolve();
  }

  dispatch({
    type: _actionCodes.RECORD_READ_STARTED,
    meta: {
      recordTypeConfig,
      csid
    }
  });
  return doRead(recordTypeConfig, vocabularyConfig, csid).then(response => dispatch({
    type: _actionCodes.RECORD_READ_FULFILLED,
    payload: response,
    meta: {
      config,
      recordTypeConfig,
      csid
    }
  })).then(() => dispatch(initializeSubrecords(config, recordTypeConfig, vocabularyConfig, csid))).then(() => (0, _reducers.getRecordData)(getState(), csid)).catch(error => dispatch({
    type: _actionCodes.RECORD_READ_REJECTED,
    payload: {
      code: _errorCodes.ERR_API,
      error
    },
    meta: {
      recordTypeConfig,
      csid
    }
  }));
};

exports.readRecord = readRecord;

const createNewRecord = (config, recordTypeConfig, vocabularyConfig, cloneCsid) => (dispatch, getState) => {
  let readClone;

  if (cloneCsid) {
    const data = (0, _reducers.getRecordData)(getState(), cloneCsid);

    if (!data) {
      // We don't have data for the record to be cloned. Read it first.
      readClone = dispatch(readRecord(config, recordTypeConfig, vocabularyConfig, cloneCsid));
    }
  }

  if (!readClone) {
    // There's nothing to clone, or we already have the record data to be cloned. Perform an
    // async noop, so this function will be consistently async.
    readClone = new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, 0);
    });
  }

  return readClone.then(() => dispatch({
    type: _actionCodes.CREATE_NEW_RECORD,
    meta: {
      config,
      recordTypeConfig,
      cloneCsid,
      stickyFields: (0, _reducers.getStickyFields)(getState())
    }
  }));
};

exports.createNewRecord = createNewRecord;

const createNewSubrecord = (config, csid, csidField, subrecordName, subrecordTypeConfig, subrecordVocabularyConfig, cloneCsid, isDefault) => (dispatch, getState) => {
  let readClone;

  if (cloneCsid) {
    const data = (0, _reducers.getRecordData)(getState(), cloneCsid);

    if (!data) {
      // We don't have data for the record to be cloned. Read it first.
      readClone = dispatch(readRecord(config, subrecordTypeConfig, subrecordVocabularyConfig, cloneCsid));
    }
  }

  if (!readClone) {
    // There's nothing to clone, or we already have the record data to be cloned. Perform an
    // async noop, so this function will be consistently async.
    readClone = new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, 0);
    });
  }

  return readClone.then(() => dispatch({
    type: _actionCodes.CREATE_NEW_SUBRECORD,
    meta: {
      config,
      csid,
      csidField,
      subrecordName,
      subrecordTypeConfig,
      cloneCsid,
      isDefault,
      stickyFields: (0, _reducers.getStickyFields)(getState())
    }
  }));
};

exports.createNewSubrecord = createNewSubrecord;

const saveSubrecords = (config, recordTypeConfig, vocabularyConfig, csid, saveStage) => (dispatch, getState) => {
  const {
    subrecords
  } = recordTypeConfig;

  if (!subrecords) {
    return Promise.resolve();
  }

  return Promise.all(Object.entries(subrecords).filter(entry => entry[1].saveStage === saveStage).map(entry => {
    const [subrecordName, subrecordConfig] = entry;
    const subrecordCsid = (0, _reducers.getRecordSubrecordCsid)(getState(), csid, subrecordName);

    if (subrecordCsid) {
      const {
        csidField,
        saveCondition,
        subresource
      } = subrecordConfig;

      if (saveCondition) {
        const subrecordData = (0, _reducers.getRecordData)(getState(), subrecordCsid);

        if (!saveCondition(subrecordData)) {
          return Promise.resolve();
        }
      }

      if (csidField) {
        const subrecordTypeConfig = (0, _get.default)(config, ['recordTypes', subrecordConfig.recordType]);
        const subrecordVocabularyConfig = (0, _get.default)(subrecordTypeConfig, ['vocabularies', subrecordConfig.vocabulary]); // eslint-disable-next-line no-use-before-define

        return dispatch(saveRecord(config, subrecordTypeConfig, subrecordVocabularyConfig, subrecordCsid, undefined, undefined, undefined, newRecordCsid => {
          dispatch({
            type: _actionCodes.SUBRECORD_CREATED,
            meta: {
              csid,
              csidField,
              subrecordName,
              subrecordCsid: newRecordCsid
            }
          });
        }, false));
      }

      if (subresource) {
        const searchName = (0, _searchHelpers.getSubrecordSearchName)(csid, subrecordName);
        const subrecordSubresourceConfig = (0, _get.default)(config, ['subresources', subresource]);

        if (subrecordSubresourceConfig) {
          // eslint-disable-next-line no-use-before-define
          return dispatch(saveRecord(config, recordTypeConfig, vocabularyConfig, csid, subrecordSubresourceConfig, subrecordCsid, undefined, newRecordCsid => {
            dispatch({
              type: _actionCodes.SUBRECORD_CREATED,
              meta: {
                csid,
                searchName,
                subrecordName,
                subrecordCsid: newRecordCsid
              }
            });
          }, false));
        }
      }
    }

    return Promise.resolve();
  }));
};

const saveRecord = function saveRecord(config, recordTypeConfig, vocabularyConfig, csid, subresourceConfig, subresourceCsid, relatedSubjectCsid, onRecordCreated) {
  let showNotifications = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : true;
  return (dispatch, getState, intl) => {
    let currentRecordTypeConfig;
    let currentVocabularyConfig;
    let currentCsid;

    if (subresourceConfig) {
      currentRecordTypeConfig = (0, _get.default)(config, ['recordTypes', subresourceConfig.recordType]);
      currentVocabularyConfig = (0, _get.default)(currentRecordTypeConfig, ['vocabularies', subresourceConfig.vocabulary]);
      currentCsid = subresourceCsid;
    } else {
      currentRecordTypeConfig = recordTypeConfig;
      currentVocabularyConfig = vocabularyConfig;
      currentCsid = csid;
    }

    return dispatch(computeRecordData(currentRecordTypeConfig, currentCsid)).then(() => dispatch(validateRecordData(currentRecordTypeConfig, currentCsid))).then(() => {
      if ((0, _reducers.getRecordValidationErrors)(getState(), currentCsid)) {
        return null;
      }

      dispatch({
        type: _actionCodes.RECORD_SAVE_STARTED,
        meta: {
          csid: currentCsid
        }
      });
      const title = currentRecordTypeConfig.title ? currentRecordTypeConfig.title((0, _reducers.getRecordData)(getState(), currentCsid), {
        config,
        intl
      }) : null;
      const notificationID = (0, _notificationHelpers.default)();

      if (showNotifications) {
        dispatch((0, _notification.showNotification)({
          items: [{
            message: saveMessages.saving,
            values: {
              title,
              hasTitle: title ? 'yes' : ''
            }
          }],
          date: new Date(),
          status: _notificationStatusCodes.STATUS_PENDING
        }, notificationID));
      }

      return dispatch(saveSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, currentCsid, 'before')).then(() => dispatch((0, _prefs.setStickyFields)(currentRecordTypeConfig, currentCsid))).then(() => {
        const data = (0, _reducers.getRecordData)(getState(), currentCsid);
        const isExisting = (0, _recordDataHelpers.isExistingRecord)(data);
        const recordServicePath = (0, _get.default)(recordTypeConfig, ['serviceConfig', 'servicePath']);
        const vocabularyServicePath = (0, _get.default)(vocabularyConfig, ['serviceConfig', 'servicePath']);
        const pathParts = [recordServicePath];

        if (vocabularyServicePath) {
          pathParts.push(vocabularyServicePath);
          pathParts.push('items');
        }

        if (subresourceConfig) {
          if (csid) {
            pathParts.push(csid);
          }

          const subresourceServicePath = (0, _get.default)(subresourceConfig, ['serviceConfig', 'servicePath']);

          if (subresourceServicePath) {
            pathParts.push(subresourceServicePath);
          }
        }

        if (isExisting && currentCsid) {
          pathParts.push(currentCsid);
        }

        const path = pathParts.join('/');
        const requestConfig = {
          data: (0, _recordDataHelpers.prepareForSending)(data, currentRecordTypeConfig).toJS()
        };

        if (recordTypeConfig.requestConfig) {
          (0, _merge.default)(requestConfig, recordTypeConfig.requestConfig('save', data));
        }

        if (isExisting) {
          return (0, _cspace.default)().update(path, requestConfig).then(response => currentRecordTypeConfig.refetchAfterUpdate ? doRead(currentRecordTypeConfig, currentVocabularyConfig, currentCsid) : response).then(response => dispatch(saveSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, currentCsid, 'after')).then(() => {
            if (showNotifications) {
              dispatch((0, _notification.showNotification)({
                items: [{
                  message: saveMessages.saved,
                  values: {
                    title,
                    hasTitle: title ? 'yes' : ''
                  }
                }],
                date: new Date(),
                status: _notificationStatusCodes.STATUS_SUCCESS,
                autoClose: true
              }, notificationID));
            }

            dispatch({
              type: _actionCodes.RECORD_SAVE_FULFILLED,
              payload: response,
              meta: {
                relatedSubjectCsid,
                recordTypeConfig: currentRecordTypeConfig,
                csid: currentCsid,
                recordPagePrimaryCsid: (0, _reducers.getRecordPagePrimaryCsid)(getState())
              }
            });
          }).then(() => dispatch(initializeSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, currentCsid))).then(() => currentCsid).catch(error => {
            throw error;
          })).catch(error => Promise.reject({
            code: _errorCodes.ERR_API,
            error
          }));
        }

        return (0, _cspace.default)().create(path, requestConfig).then(response => {
          if (response.status === 201 && response.headers.location) {
            const location = response.headers.location;
            const newRecordCsid = location.substring(location.lastIndexOf('/') + 1);
            dispatch({
              type: _actionCodes.RECORD_CREATED,
              meta: {
                currentCsid,
                newRecordCsid,
                recordTypeConfig: currentRecordTypeConfig
              }
            });
            return dispatch(saveSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, newRecordCsid, 'after')).then(() => doRead(currentRecordTypeConfig, currentVocabularyConfig, newRecordCsid)).then(readResponse => {
              if (showNotifications) {
                dispatch((0, _notification.showNotification)({
                  items: [{
                    message: saveMessages.saved,
                    values: {
                      title,
                      hasTitle: title ? 'yes' : ''
                    }
                  }],
                  date: new Date(),
                  status: _notificationStatusCodes.STATUS_SUCCESS,
                  autoClose: true
                }, notificationID));
              }

              return dispatch({
                type: _actionCodes.RECORD_SAVE_FULFILLED,
                payload: readResponse,
                meta: {
                  relatedSubjectCsid,
                  recordTypeConfig: currentRecordTypeConfig,
                  csid: newRecordCsid,
                  recordPagePrimaryCsid: (0, _reducers.getRecordPagePrimaryCsid)(getState())
                }
              });
            }).then(() => dispatch(initializeSubrecords(config, currentRecordTypeConfig, currentVocabularyConfig, newRecordCsid))).then(() => Promise.resolve(onRecordCreated ? onRecordCreated(newRecordCsid) : null)).then(() => newRecordCsid);
          }

          const error = new Error('Expected response with status 201 and a location header');
          error.response = response;
          throw error;
        }).catch(error => Promise.reject({
          code: _errorCodes.ERR_API,
          error
        }));
      }).catch(error => {
        const notificationItem = getSaveErrorNotificationItem(error, title);

        if (showNotifications) {
          dispatch((0, _notification.showNotification)({
            items: [notificationItem],
            date: new Date(),
            status: _notificationStatusCodes.STATUS_ERROR
          }, notificationID));
        }

        dispatch({
          type: _actionCodes.RECORD_SAVE_REJECTED,
          payload: error,
          meta: {
            csid: currentCsid
          }
        });
        throw error;
      });
    });
  };
};

exports.saveRecord = saveRecord;

const addFieldInstance = (recordTypeConfig, csid, path, position) => dispatch => {
  dispatch({
    type: _actionCodes.ADD_FIELD_INSTANCE,
    meta: {
      csid,
      path,
      position,
      recordTypeConfig
    }
  });
  return dispatch(computeRecordData(recordTypeConfig, csid)).then(() => dispatch(validateRecordData(recordTypeConfig, csid)));
};

exports.addFieldInstance = addFieldInstance;

const sortFieldInstances = (config, recordTypeConfig, csid, path, byField) => dispatch => {
  dispatch({
    type: _actionCodes.SORT_FIELD_INSTANCES,
    meta: {
      config,
      csid,
      path,
      byField,
      recordTypeConfig
    }
  });
  return dispatch(computeRecordData(recordTypeConfig, csid)).then(() => dispatch(validateRecordData(recordTypeConfig, csid)));
};

exports.sortFieldInstances = sortFieldInstances;

const deleteFieldValue = (recordTypeConfig, csid, path) => dispatch => {
  dispatch({
    type: _actionCodes.DELETE_FIELD_VALUE,
    meta: {
      csid,
      path
    }
  });
  return dispatch(computeRecordData(recordTypeConfig, csid)).then(() => dispatch(validateRecordData(recordTypeConfig, csid)));
};

exports.deleteFieldValue = deleteFieldValue;

const moveFieldValue = (recordTypeConfig, csid, path, newPosition) => dispatch => {
  dispatch({
    type: _actionCodes.MOVE_FIELD_VALUE,
    meta: {
      csid,
      path,
      newPosition
    }
  });
  return dispatch(computeRecordData(recordTypeConfig, csid)).then(() => dispatch(validateRecordData(recordTypeConfig, csid)));
};

exports.moveFieldValue = moveFieldValue;

const setFieldValue = (recordTypeConfig, csid, path, value) => dispatch => {
  dispatch({
    type: _actionCodes.SET_FIELD_VALUE,
    payload: value,
    meta: {
      csid,
      path
    }
  });
  return dispatch(computeRecordData(recordTypeConfig, csid)).then(() => dispatch(validateRecordData(recordTypeConfig, csid)));
};

exports.setFieldValue = setFieldValue;

const revertRecord = (recordTypeConfig, csid) => dispatch => {
  dispatch({
    type: _actionCodes.REVERT_RECORD,
    meta: {
      recordTypeConfig,
      csid
    }
  }); // Clear validation errors. Could maybe revalidate here, but to be consistent, we should also
  // validate when a record is first loaded.

  dispatch({
    type: _actionCodes.VALIDATION_PASSED,
    meta: {
      csid,
      path: []
    }
  });
  dispatch((0, _notification.removeValidationNotification)());
  dispatch((0, _notification.removeNotification)(_HierarchyReparentNotifier.default.notificationID));
};

exports.revertRecord = revertRecord;

const deleteRecord = (config, recordTypeConfig, vocabularyConfig, csid, relatedSubjectCsid) => (dispatch, getState, intl) => {
  const data = (0, _reducers.getRecordData)(getState(), csid);
  const title = recordTypeConfig.title(data, {
    config,
    intl
  });
  const notificationID = (0, _notificationHelpers.default)();
  dispatch((0, _notification.showNotification)({
    items: [{
      message: deleteMessages.deleting,
      values: {
        title,
        hasTitle: title ? 'yes' : ''
      }
    }],
    date: new Date(),
    status: _notificationStatusCodes.STATUS_PENDING
  }, notificationID));
  dispatch({
    type: _actionCodes.RECORD_DELETE_STARTED,
    meta: {
      recordTypeConfig,
      csid
    }
  });
  const recordServicePath = recordTypeConfig.serviceConfig.servicePath;
  const vocabularyServicePath = vocabularyConfig ? vocabularyConfig.serviceConfig.servicePath : null;
  const pathParts = [recordServicePath];

  if (vocabularyServicePath) {
    pathParts.push(vocabularyServicePath);
    pathParts.push('items');
  }

  if (csid) {
    pathParts.push(csid);
  }

  const path = pathParts.join('/');
  return (0, _cspace.default)().delete(path).then(response => {
    dispatch((0, _notification.showNotification)({
      items: [{
        message: deleteMessages.deleted,
        values: {
          title,
          hasTitle: title ? 'yes' : ''
        }
      }],
      date: new Date(),
      status: _notificationStatusCodes.STATUS_SUCCESS,
      autoClose: true
    }, notificationID));
    return dispatch({
      type: _actionCodes.RECORD_DELETE_FULFILLED,
      payload: response,
      meta: {
        recordTypeConfig,
        csid,
        relatedSubjectCsid
      }
    });
  }).catch(error => {
    dispatch((0, _notification.showNotification)({
      items: [{
        message: deleteMessages.errorDeleting,
        values: {
          title,
          hasTitle: title ? 'yes' : '',
          error: (0, _getErrorDescription.default)(error)
        }
      }],
      date: new Date(),
      status: _notificationStatusCodes.STATUS_ERROR
    }, notificationID));
    return dispatch({
      type: _actionCodes.RECORD_DELETE_REJECTED,
      payload: {
        code: _errorCodes.ERR_API,
        error
      },
      meta: {
        recordTypeConfig,
        csid
      }
    });
  });
};

exports.deleteRecord = deleteRecord;

const transitionRecord = (config, recordTypeConfig, vocabularyConfig, csid, transitionName, relatedSubjectCsid) => (dispatch, getState, intl) => {
  const data = (0, _reducers.getRecordData)(getState(), csid);
  const title = recordTypeConfig.title(data, {
    config,
    intl
  });
  const notificationID = (0, _notificationHelpers.default)();
  const messages = transitionMessages[transitionName];

  if (messages) {
    dispatch((0, _notification.showNotification)({
      items: [{
        message: messages.transitioning,
        values: {
          title,
          hasTitle: title ? 'yes' : ''
        }
      }],
      date: new Date(),
      status: _notificationStatusCodes.STATUS_PENDING
    }, notificationID));
  }

  dispatch({
    type: _actionCodes.RECORD_TRANSITION_STARTED,
    meta: {
      recordTypeConfig,
      csid,
      transitionName
    }
  });
  const recordServicePath = recordTypeConfig.serviceConfig.servicePath;
  const vocabularyServicePath = vocabularyConfig ? vocabularyConfig.serviceConfig.servicePath : null;
  const pathParts = [recordServicePath];

  if (vocabularyServicePath) {
    pathParts.push(vocabularyServicePath);
    pathParts.push('items');
  }

  if (csid) {
    pathParts.push(csid);
  }

  pathParts.push('workflow');
  pathParts.push(transitionName);
  const path = pathParts.join('/');
  return (0, _cspace.default)().update(path).then(response => transitionName === 'delete' ? response // For all transitions other than delete, re-read the record to obtain the new workflow
  // state.
  : doRead(recordTypeConfig, vocabularyConfig, csid)).then(response => {
    if (messages) {
      dispatch((0, _notification.showNotification)({
        items: [{
          message: messages.transitioned,
          values: {
            transitionName,
            title,
            hasTitle: title ? 'yes' : ''
          }
        }],
        date: new Date(),
        status: _notificationStatusCodes.STATUS_SUCCESS,
        autoClose: true
      }, notificationID));
    }

    return dispatch({
      type: _actionCodes.RECORD_TRANSITION_FULFILLED,
      payload: response,
      meta: {
        recordTypeConfig,
        csid,
        transitionName,
        relatedSubjectCsid,
        recordPagePrimaryCsid: (0, _reducers.getRecordPagePrimaryCsid)(getState()),
        // We don't get data back from the transition request. Rather than making a separate
        // request to get the actual updated time of the record, just make it the current time.
        updatedTimestamp: new Date().toISOString()
      }
    });
  }).catch(error => {
    if (messages) {
      dispatch((0, _notification.showNotification)({
        items: [{
          message: messages.errorTransitioning,
          values: {
            transitionName,
            title,
            hasTitle: title ? 'yes' : '',
            error: (0, _getErrorDescription.default)(error)
          }
        }],
        date: new Date(),
        status: _notificationStatusCodes.STATUS_ERROR
      }, notificationID));
    }

    return dispatch({
      type: _actionCodes.RECORD_TRANSITION_REJECTED,
      payload: {
        code: _errorCodes.ERR_API,
        error
      },
      meta: {
        recordTypeConfig,
        csid,
        transitionName
      }
    });
  });
};

exports.transitionRecord = transitionRecord;

const saveRecordWithTransition = function saveRecordWithTransition(config, recordTypeConfig, vocabularyConfig, csid, subresourceConfig, subresourceCsid, relatedSubjectCsid, transitionName, onRecordCreated) {
  let showNotifications = arguments.length > 9 && arguments[9] !== undefined ? arguments[9] : true;
  return dispatch => dispatch(saveRecord(config, recordTypeConfig, vocabularyConfig, csid, subresourceConfig, subresourceCsid, relatedSubjectCsid, onRecordCreated, showNotifications)).then(savedCsid => dispatch(transitionRecord(config, recordTypeConfig, vocabularyConfig, savedCsid, transitionName, relatedSubjectCsid)));
};

exports.saveRecordWithTransition = saveRecordWithTransition;

const detachSubrecord = (config, csid, csidField, subrecordName, subrecordTypeConfig) => ({
  type: _actionCodes.DETACH_SUBRECORD,
  meta: {
    config,
    csid,
    csidField,
    subrecordName,
    subrecordTypeConfig
  }
});

exports.detachSubrecord = detachSubrecord;

const clearRecord = csid => ({
  type: _actionCodes.CLEAR_RECORD,
  meta: {
    csid
  }
});

exports.clearRecord = clearRecord;