'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.checkForRelations = exports.createBidirectional = exports.create = exports.batchCreateBidirectional = exports.batchCreate = exports.batchUnrelateBidirectional = exports.batchUnrelate = exports.unrelateBidirectional = exports.unrelate = exports.deleteRelation = exports.find = exports.clearState = exports.SUBJECT_RELATIONS_UPDATED = exports.RELATION_SAVE_REJECTED = exports.RELATION_SAVE_FULFILLED = exports.RELATION_SAVE_STARTED = exports.RELATION_FIND_REJECTED = exports.RELATION_FIND_FULFILLED = exports.RELATION_FIND_STARTED = exports.RELATION_DELETE_REJECTED = exports.RELATION_DELETE_FULFILLED = exports.RELATION_DELETE_STARTED = exports.CLEAR_RELATION_STATE = undefined;

var _immutable = require('immutable');

var _immutable2 = _interopRequireDefault(_immutable);

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

var _get2 = _interopRequireDefault(_get);

var _cspace = require('./cspace');

var _cspace2 = _interopRequireDefault(_cspace);

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

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

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

var CLEAR_RELATION_STATE = exports.CLEAR_RELATION_STATE = 'CLEAR_RELATION_STATE';
var RELATION_DELETE_STARTED = exports.RELATION_DELETE_STARTED = 'RELATION_DELETE_STARTED';
var RELATION_DELETE_FULFILLED = exports.RELATION_DELETE_FULFILLED = 'RELATION_DELETE_FULFILLED';
var RELATION_DELETE_REJECTED = exports.RELATION_DELETE_REJECTED = 'RELATION_DELETE_REJECTED';
var RELATION_FIND_STARTED = exports.RELATION_FIND_STARTED = 'RELATION_FIND_STARTED';
var RELATION_FIND_FULFILLED = exports.RELATION_FIND_FULFILLED = 'RELATION_FIND_FULFILLED';
var RELATION_FIND_REJECTED = exports.RELATION_FIND_REJECTED = 'RELATION_FIND_REJECTED';
var RELATION_SAVE_STARTED = exports.RELATION_SAVE_STARTED = 'RELATION_SAVE_STARTED';
var RELATION_SAVE_FULFILLED = exports.RELATION_SAVE_FULFILLED = 'RELATION_SAVE_FULFILLED';
var RELATION_SAVE_REJECTED = exports.RELATION_SAVE_REJECTED = 'RELATION_SAVE_REJECTED';
var SUBJECT_RELATIONS_UPDATED = exports.SUBJECT_RELATIONS_UPDATED = 'SUBJECT_RELATIONS_UPDATED';

var clearState = exports.clearState = function clearState() {
  return {
    type: CLEAR_RELATION_STATE
  };
};

/*
 * Find a relation, given at least the subject csid and object csid, and optionally the subject
 * record type, object record type, and predicate. This function assumes that there will be very
 * few (typically zero or one) results, and retrieves them all without paginating.
 */
var find = exports.find = function find(config, subject, object, predicate) {
  return function (dispatch, getState) {
    if (!(subject.csid || object.csid)) {
      throw new Error('subject csid or object csid must be supplied');
    }

    if ((0, _reducers.getRelationFindResult)(getState(), subject, object, predicate)) {
      // Already have a result for this descriptor. Do nothing.
      // TODO: Also check for a pending find request.

      return null;
    }

    dispatch({
      type: RELATION_FIND_STARTED,
      meta: {
        subject: subject,
        object: object,
        predicate: predicate
      }
    });

    var params = {
      prd: predicate,
      wf_deleted: 'false',
      pgSz: '0'
    };

    if (subject) {
      var csid = subject.csid,
          recordType = subject.recordType;


      params.sbj = csid;

      if (recordType) {
        params.sbjType = (0, _get2.default)(config, ['recordTypes', recordType, 'serviceConfig', 'objectName']);
      }
    }

    if (object) {
      var _csid = object.csid,
          _recordType = object.recordType;


      params.obj = _csid;

      if (_recordType) {
        params.objType = (0, _get2.default)(config, ['recordTypes', _recordType, 'serviceConfig', 'objectName']);
      }
    }

    var requestConfig = {
      params: params
    };

    return (0, _cspace2.default)().read('/relations', requestConfig).then(function (response) {
      return dispatch({
        type: RELATION_FIND_FULFILLED,
        payload: response,
        meta: {
          subject: subject,
          object: object,
          predicate: predicate
        }
      });
    }).catch(function (error) {
      return dispatch({
        type: RELATION_FIND_REJECTED,
        payload: {
          code: _errorCodes.ERR_API,
          error: error
        },
        meta: {
          subject: subject,
          object: object,
          predicate: predicate
        }
      });
    });
  };
};

var deleteRelation = exports.deleteRelation = function deleteRelation(csid) {
  return function (dispatch) {
    if (!csid) {
      throw new Error('csid must be supplied');
    }

    // return Promise.resolve();
    dispatch({
      type: RELATION_DELETE_STARTED,
      meta: {
        csid: csid
      }
    });

    return (0, _cspace2.default)().delete('/relations/' + csid).then(function (response) {
      return dispatch({
        type: RELATION_DELETE_FULFILLED,
        payload: response,
        meta: {
          csid: csid
        }
      });
    }).catch(function (error) {
      dispatch({
        type: RELATION_DELETE_REJECTED,
        payload: {
          code: _errorCodes.ERR_API,
          error: error
        },
        meta: {
          csid: csid
        }
      });

      return Promise.reject(error);
    });
  };
};

var doUnrelate = function doUnrelate(config, subject, object, predicate) {
  return function (dispatch, getState) {
    if (!(subject.csid && object.csid)) {
      throw new Error('subject csid and object csid must be supplied');
    }

    var existingResult = (0, _reducers.getRelationFindResult)(getState(), subject, object, predicate);

    var promise = void 0;

    if (existingResult) {
      promise = Promise.resolve(existingResult);
    } else {
      promise = dispatch(find(config, subject, object, predicate)).then(function () {
        return (0, _reducers.getRelationFindResult)(getState(), subject, object, predicate);
      });
    }

    return promise.then(function (findResult) {
      var list = findResult.get('rel:relations-common-list');

      var items = list.get('relation-list-item');

      if (!_immutable2.default.List.isList(items)) {
        items = _immutable2.default.List.of(items);
      }

      return Promise.all(items.map(function (item) {
        return dispatch(deleteRelation(item.get('csid')));
      }));
    });
  };
};

var unrelate = exports.unrelate = function unrelate(config, subject, object, predicate) {
  return function (dispatch) {
    return dispatch(doUnrelate(config, subject, object, predicate)).then(function () {
      return dispatch({
        type: SUBJECT_RELATIONS_UPDATED,
        meta: {
          subject: subject,
          updatedTime: new Date().toISOString()
        }
      });
    }).catch(function () {});
  };
};

var unrelateBidirectional = exports.unrelateBidirectional = function unrelateBidirectional(config, subject, object, predicate) {
  return function (dispatch) {
    return dispatch(unrelate(config, subject, object, predicate)).then(function () {
      return dispatch(unrelate(config, object, subject, predicate));
    }).catch(function () {});
  };
};

var batchUnrelate = exports.batchUnrelate = function batchUnrelate(config, subject, objects, predicate) {
  return function (dispatch) {
    return Promise.all(objects.map(function (object) {
      return dispatch(doUnrelate(config, subject, object, predicate));
    })).then(function () {
      return dispatch({
        type: SUBJECT_RELATIONS_UPDATED,
        meta: {
          subject: subject,
          updatedTime: new Date().toISOString()
        }
      });
    }).catch(function () {});
  };
};

var batchUnrelateBidirectional = exports.batchUnrelateBidirectional = function batchUnrelateBidirectional(config, subject, objects, predicate) {
  return function (dispatch) {
    return (
      // For the passed subject, we only want to dispatch SUBJECT_RELATIONS_UPDATED once at the end, so
      // doUnrelate is used. The passed objects should be unique; for the reverse relations (where the
      // object becomes the subject), SUBJECT_RELATIONS_UPDATED may be dispatched immediately, so
      // unrelate is used.

      Promise.all(objects.map(function (object) {
        return dispatch(doUnrelate(config, subject, object, predicate)).then(function () {
          return dispatch(unrelate(config, object, subject, predicate));
        });
      })).then(function () {
        return dispatch({
          type: SUBJECT_RELATIONS_UPDATED,
          meta: {
            subject: subject,
            updatedTime: new Date().toISOString()
          }
        });
      }).catch(function () {})
    );
  };
};

var doCreate = function doCreate(subject, object, predicate) {
  return function (dispatch) {
    dispatch({
      type: RELATION_SAVE_STARTED,
      meta: {
        subject: subject,
        object: object,
        predicate: predicate
      }
    });

    var config = {
      data: {
        document: {
          'rel:relations_common': {
            '@xmlns:rel': 'http://collectionspace.org/services/relation',
            subjectCsid: subject.csid,
            objectCsid: object.csid,
            relationshipType: predicate
          }
        }
      }
    };

    return (0, _cspace2.default)().create('/relations', config).then(function (response) {
      return dispatch({
        type: RELATION_SAVE_FULFILLED,
        payload: response,
        meta: {
          subject: subject,
          object: object,
          predicate: predicate
        }
      });
    }).catch(function (error) {
      dispatch({
        type: RELATION_SAVE_REJECTED,
        payload: {
          code: _errorCodes.ERR_API,
          error: error
        },
        meta: {
          subject: subject,
          object: object,
          predicate: predicate
        }
      });

      return Promise.reject(error);
    });
  };
};

var batchCreate = exports.batchCreate = function batchCreate(subject, objects, predicate) {
  return function (dispatch) {
    return Promise.all(objects.map(function (object) {
      return dispatch(doCreate(subject, object, predicate));
    })).then(function () {
      return dispatch({
        type: SUBJECT_RELATIONS_UPDATED,
        meta: {
          subject: subject,
          updatedTime: new Date().toISOString()
        }
      });
    }).catch(function () {});
  };
};

var batchCreateBidirectional = exports.batchCreateBidirectional = function batchCreateBidirectional(subject, objects, predicate) {
  return function (dispatch) {
    return Promise.all(objects.map(function (object) {
      return dispatch(doCreate(subject, object, predicate)).then(function () {
        return dispatch(doCreate(object, subject, predicate));
      });
    })).then(function () {
      return dispatch({
        type: SUBJECT_RELATIONS_UPDATED,
        meta: {
          subject: subject,
          updatedTime: new Date().toISOString()
        }
      });
    }).catch(function () {});
  };
};

var create = exports.create = function create(subject, object, predicate) {
  return function (dispatch) {
    return dispatch(doCreate(subject, object, predicate)).then(function () {
      return dispatch({
        type: SUBJECT_RELATIONS_UPDATED,
        meta: {
          subject: subject,
          updatedTime: new Date().toISOString()
        }
      });
    }).catch(function () {});
  };
};

var createBidirectional = exports.createBidirectional = function createBidirectional(subject, object, predicate) {
  return function (dispatch) {
    return dispatch(doCreate(subject, object, predicate)).then(function () {
      return dispatch(doCreate(object, subject, predicate));
    }).then(function () {
      return dispatch({
        type: SUBJECT_RELATIONS_UPDATED,
        meta: {
          subject: subject,
          updatedTime: new Date().toISOString()
        }
      });
    }).catch(function () {});
  };
};

/**
 * Check if any relations exist for the given record csid and predicate. Resolves to true or false.
 */
var checkForRelations = exports.checkForRelations = function checkForRelations(csid, predicate) {
  return function () {
    var requestConfig = {
      params: {
        prd: predicate,
        sbj: csid,
        andReciprocal: 'true',
        wf_deleted: 'false',
        pgSz: '1'
      }
    };

    return (0, _cspace2.default)().read('/relations', requestConfig).then(function (response) {
      var totalItems = (0, _get2.default)(response, ['data', 'rel:relations-common-list', 'totalItems']);

      return totalItems && parseInt(totalItems, 10) > 0;
    });
  };
};