'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.showRelationNotification = 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 _reactIntl = require('react-intl');

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

var _get2 = _interopRequireDefault(_get);

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

var _cspace2 = _interopRequireDefault(_cspace);

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

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

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

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

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

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

const clearState = exports.clearState = () => ({
  type: CLEAR_RELATION_STATE
});

const messages = (0, _reactIntl.defineMessages)({
  related: {
    'id': 'action.relation.related',
    'defaultMessage': '{objectCount, plural, =0 {No records} one {# record} other {# records}} related to {subjectTitle}.'
  }
});

const notificationID = 'action.relation';

const showRelationNotification = exports.showRelationNotification = (message, values) => (0, _notification.showNotification)({
  items: [{
    message,
    values
  }],
  date: new Date(),
  status: _notificationStatusCodes.STATUS_SUCCESS,
  autoClose: true
}, notificationID);

/*
 * 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.
 */
const find = exports.find = (config, subject, object, predicate) => (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,
      object,
      predicate
    }
  });

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

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


    params.sbj = csid;

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

  if (object) {
    const csid = object.csid,
          recordType = object.recordType;


    params.obj = csid;

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

  const requestConfig = {
    params
  };

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

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

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

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

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

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

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

  let promise;

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

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

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

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

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

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

const unrelateBidirectional = exports.unrelateBidirectional = (config, subject, object, predicate) => dispatch => dispatch(unrelate(config, subject, object, predicate)).then(() => dispatch(unrelate(config, object, subject, predicate))).catch(() => {});

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

const batchUnrelateBidirectional = exports.batchUnrelateBidirectional = (config, subject, objects, predicate) => dispatch =>
// 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(object => dispatch(doUnrelate(config, subject, object, predicate)).then(() => dispatch(unrelate(config, object, subject, predicate))))).then(() => dispatch({
  type: SUBJECT_RELATIONS_UPDATED,
  meta: {
    subject,
    updatedTime: new Date().toISOString()
  }
})).catch(() => {});

const doCreate = (subject, object, predicate) => dispatch => {
  dispatch({
    type: RELATION_SAVE_STARTED,
    meta: {
      subject,
      object,
      predicate
    }
  });

  const 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(response => dispatch({
    type: RELATION_SAVE_FULFILLED,
    payload: response,
    meta: {
      subject,
      object,
      predicate
    }
  })).catch(error => {
    dispatch({
      type: RELATION_SAVE_REJECTED,
      payload: {
        code: _errorCodes.ERR_API,
        error
      },
      meta: {
        subject,
        object,
        predicate
      }
    });

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

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

const batchCreateBidirectional = exports.batchCreateBidirectional = (subject, objects, predicate) => dispatch => Promise.all(objects.map(object => dispatch(doCreate(subject, object, predicate)).then(() => dispatch(doCreate(object, subject, predicate))))).then(() => {
  dispatch(showRelationNotification(messages.related, {
    objectCount: objects.length,
    subjectTitle: subject.title
  }));

  dispatch({
    type: SUBJECT_RELATIONS_UPDATED,
    meta: {
      subject,
      updatedTime: new Date().toISOString()
    }
  });
}).catch(() => {});

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

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

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

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

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