'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _extends2 = require('babel-runtime/helpers/extends');

var _extends3 = _interopRequireDefault(_extends2);

var _stringify = require('babel-runtime/core-js/json/stringify');

var _stringify2 = _interopRequireDefault(_stringify);

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

var _promise = require('babel-runtime/core-js/promise');

var _promise2 = _interopRequireDefault(_promise);

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _lodash = require('lodash');

var _lodash2 = _interopRequireDefault(_lodash);

var _error = require('../constants/error');

var errorConstants = _interopRequireWildcard(_error);

var _PromiseQueue = require('./PromiseQueue');

var _PromiseQueue2 = _interopRequireDefault(_PromiseQueue);

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }

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

/**
 * A wrapper for interacting with Kleros Store.
 */
var StoreProviderWrapper =
/**
 * Create a new instance of StoreProviderWrapper.
 * @param {string} storeProviderUri - The uri of kleros store.
 * @param {string} authToken - Signed token cooresponding to user profile address.
 */
function StoreProviderWrapper(storeProviderUri, authToken) {
  var _this = this;

  (0, _classCallCheck3.default)(this, StoreProviderWrapper);

  this._makeRequest = function (verb, uri) {
    var body = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

    if (verb !== 'GET' && !_this._token) {
      throw new Error('No auth token set. Cannot make writes to store. Please call setAuthToken or validateNewAuthToken.');
    }

    var httpRequest = new XMLHttpRequest();
    return new _promise2.default(function (resolve, reject) {
      try {
        httpRequest.open(verb, uri, true);
        if (body) {
          httpRequest.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
          httpRequest.setRequestHeader('Authorization', _this._token);
        }
        httpRequest.onreadystatechange = function () {
          if (httpRequest.readyState === 4) {
            var _body = null;
            try {
              _body = JSON.parse(httpRequest.responseText);
              // eslint-disable-next-line no-unused-vars
            } catch (err) {}
            // auth token error
            if (httpRequest.status === 401) reject(errorConstants.INVALID_AUTH_TOKEN(_body.error));

            resolve({
              body: _body,
              status: httpRequest.status
            });
          }
        };
        httpRequest.send(body);
      } catch (err) {
        reject(errorConstants.REQUEST_FAILED(err));
      }
    });
  };

  this.queueWriteRequest = function (getBodyFn, verb) {
    var uri = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
    return _this._storeQueue.fetch(function () {
      return getBodyFn().then(function (result) {
        return _this._makeRequest(verb, uri, result);
      });
    });
  };

  this.queueReadRequest = function (uri) {
    return _this._storeQueue.fetch(function () {
      return _this._makeRequest('GET', uri);
    });
  };

  this.setAuthToken = function (token) {
    _this._token = token;
  };

  this.newAuthToken = function () {
    var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee(userAddress) {
      var newTokenResponse;
      return _regenerator2.default.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              _context.next = 2;
              return _this._makeRequest('GET', _this._storeUri + '/' + userAddress + '/authToken');

            case 2:
              newTokenResponse = _context.sent;
              return _context.abrupt('return', newTokenResponse.body);

            case 4:
            case 'end':
              return _context.stop();
          }
        }
      }, _callee, _this);
    }));

    return function (_x3) {
      return _ref.apply(this, arguments);
    };
  }();

  this.isTokenValid = function () {
    var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2(userAddress, token) {
      var response;
      return _regenerator2.default.wrap(function _callee2$(_context2) {
        while (1) {
          switch (_context2.prev = _context2.next) {
            case 0:
              if (token) _this.setAuthToken(token);

              _context2.prev = 1;
              _context2.next = 4;
              return _this._makeRequest('POST', _this._storeUri + '/' + userAddress + '/authToken/verify', (0, _stringify2.default)({}));

            case 4:
              response = _context2.sent;
              return _context2.abrupt('return', response.status === 201);

            case 8:
              _context2.prev = 8;
              _context2.t0 = _context2['catch'](1);
              return _context2.abrupt('return', false);

            case 11:
            case 'end':
              return _context2.stop();
          }
        }
      }, _callee2, _this, [[1, 8]]);
    }));

    return function (_x4, _x5) {
      return _ref2.apply(this, arguments);
    };
  }();

  this.getUserProfile = function () {
    var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(userAddress) {
      var httpResponse;
      return _regenerator2.default.wrap(function _callee3$(_context3) {
        while (1) {
          switch (_context3.prev = _context3.next) {
            case 0:
              _context3.next = 2;
              return _this._makeRequest('GET', _this._storeUri + '/' + userAddress);

            case 2:
              httpResponse = _context3.sent;
              return _context3.abrupt('return', httpResponse.body);

            case 4:
            case 'end':
              return _context3.stop();
          }
        }
      }, _callee3, _this);
    }));

    return function (_x6) {
      return _ref3.apply(this, arguments);
    };
  }();

  this.getContractByAddress = function () {
    var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(userAddress, addressContract) {
      var userProfile, contract;
      return _regenerator2.default.wrap(function _callee4$(_context4) {
        while (1) {
          switch (_context4.prev = _context4.next) {
            case 0:
              _context4.next = 2;
              return _this.getUserProfile(userAddress);

            case 2:
              userProfile = _context4.sent;

              if (userProfile) {
                _context4.next = 5;
                break;
              }

              throw new Error(errorConstants.PROFILE_NOT_FOUND(userAddress));

            case 5:
              contract = _lodash2.default.filter(userProfile.contracts, function (contract) {
                return contract.address === addressContract;
              });
              return _context4.abrupt('return', contract[0]);

            case 7:
            case 'end':
              return _context4.stop();
          }
        }
      }, _callee4, _this);
    }));

    return function (_x7, _x8) {
      return _ref4.apply(this, arguments);
    };
  }();

  this.getDisputeDataForUser = function () {
    var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(userAddress, arbitratorAddress, disputeId) {
      var userProfile;
      return _regenerator2.default.wrap(function _callee5$(_context5) {
        while (1) {
          switch (_context5.prev = _context5.next) {
            case 0:
              _context5.next = 2;
              return _this.getUserProfile(userAddress);

            case 2:
              userProfile = _context5.sent;

              if (userProfile) {
                _context5.next = 5;
                break;
              }

              throw new Error(errorConstants.PROFILE_NOT_FOUND(userAddress));

            case 5:
              return _context5.abrupt('return', _lodash2.default.filter(userProfile.disputes, function (o) {
                return o.arbitratorAddress === arbitratorAddress && o.disputeId === disputeId;
              })[0]);

            case 6:
            case 'end':
              return _context5.stop();
          }
        }
      }, _callee5, _this);
    }));

    return function (_x9, _x10, _x11) {
      return _ref5.apply(this, arguments);
    };
  }();

  this.getDisputes = function () {
    var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6(userAddress) {
      var userProfile;
      return _regenerator2.default.wrap(function _callee6$(_context6) {
        while (1) {
          switch (_context6.prev = _context6.next) {
            case 0:
              _context6.next = 2;
              return _this.getUserProfile(userAddress);

            case 2:
              userProfile = _context6.sent;

              if (userProfile) {
                _context6.next = 5;
                break;
              }

              return _context6.abrupt('return', []);

            case 5:
              return _context6.abrupt('return', userProfile.disputes);

            case 6:
            case 'end':
              return _context6.stop();
          }
        }
      }, _callee6, _this);
    }));

    return function (_x12) {
      return _ref6.apply(this, arguments);
    };
  }();

  this.getLastBlock = function () {
    var _ref7 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee7(userAddress) {
      var userProfile;
      return _regenerator2.default.wrap(function _callee7$(_context7) {
        while (1) {
          switch (_context7.prev = _context7.next) {
            case 0:
              _context7.next = 2;
              return _this.setUpUserProfile(userAddress);

            case 2:
              userProfile = _context7.sent;
              return _context7.abrupt('return', userProfile.lastBlock || 0);

            case 4:
            case 'end':
              return _context7.stop();
          }
        }
      }, _callee7, _this);
    }));

    return function (_x13) {
      return _ref7.apply(this, arguments);
    };
  }();

  this.updateUserProfile = function (userAddress) {
    var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    var getBodyFn = function () {
      var _ref8 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee8() {
        var currentProfile;
        return _regenerator2.default.wrap(function _callee8$(_context8) {
          while (1) {
            switch (_context8.prev = _context8.next) {
              case 0:
                _context8.next = 2;
                return _this.getUserProfile(userAddress);

              case 2:
                _context8.t0 = _context8.sent;

                if (_context8.t0) {
                  _context8.next = 5;
                  break;
                }

                _context8.t0 = {};

              case 5:
                currentProfile = _context8.t0;

                delete currentProfile._id;
                delete currentProfile.created_at;

                params.address = userAddress;

                return _context8.abrupt('return', (0, _stringify2.default)((0, _extends3.default)({}, currentProfile, params)));

              case 10:
              case 'end':
                return _context8.stop();
            }
          }
        }, _callee8, _this);
      }));

      return function getBodyFn() {
        return _ref8.apply(this, arguments);
      };
    }();

    return _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress);
  };

  this.setUpUserProfile = function () {
    var _ref9 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee9(userAddress) {
      var userProfile, response;
      return _regenerator2.default.wrap(function _callee9$(_context9) {
        while (1) {
          switch (_context9.prev = _context9.next) {
            case 0:
              _context9.next = 2;
              return _this.getUserProfile(userAddress);

            case 2:
              userProfile = _context9.sent;

              if (!_lodash2.default.isNull(userProfile)) {
                _context9.next = 10;
                break;
              }

              _context9.next = 6;
              return _this.updateUserProfile(userAddress, {});

            case 6:
              response = _context9.sent;

              if (!(response.status !== 201)) {
                _context9.next = 9;
                break;
              }

              throw new Error(errorConstants.REQUEST_FAILED(response.responseText));

            case 9:
              userProfile = response.body;

            case 10:
              return _context9.abrupt('return', userProfile);

            case 11:
            case 'end':
              return _context9.stop();
          }
        }
      }, _callee9, _this);
    }));

    return function (_x15) {
      return _ref9.apply(this, arguments);
    };
  }();

  this.updateContract = function () {
    var _ref10 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee11(userAddress, contractAddress, params) {
      var getBodyFn, httpResponse;
      return _regenerator2.default.wrap(function _callee11$(_context11) {
        while (1) {
          switch (_context11.prev = _context11.next) {
            case 0:
              getBodyFn = function () {
                var _ref11 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee10() {
                  var currentContractData;
                  return _regenerator2.default.wrap(function _callee10$(_context10) {
                    while (1) {
                      switch (_context10.prev = _context10.next) {
                        case 0:
                          _context10.next = 2;
                          return _this.getContractByAddress(userAddress, contractAddress);

                        case 2:
                          currentContractData = _context10.sent;

                          if (!currentContractData) currentContractData = {};
                          delete currentContractData._id;

                          params.address = contractAddress;

                          return _context10.abrupt('return', (0, _stringify2.default)((0, _extends3.default)({}, currentContractData, params)));

                        case 7:
                        case 'end':
                          return _context10.stop();
                      }
                    }
                  }, _callee10, _this);
                }));

                return function getBodyFn() {
                  return _ref11.apply(this, arguments);
                };
              }();

              _context11.next = 3;
              return _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress + '/contracts/' + contractAddress);

            case 3:
              httpResponse = _context11.sent;

              if (!(httpResponse.status !== 201)) {
                _context11.next = 6;
                break;
              }

              throw new Error(errorConstants.REQUEST_FAILED(httpResponse.error));

            case 6:
              return _context11.abrupt('return', _lodash2.default.filter(httpResponse.body[0].contracts, function (contract) {
                return contract.address === contractAddress;
              })[0]);

            case 7:
            case 'end':
              return _context11.stop();
          }
        }
      }, _callee11, _this);
    }));

    return function (_x16, _x17, _x18) {
      return _ref10.apply(this, arguments);
    };
  }();

  this.addEvidenceContract = function (contractAddress, userAddress, name, description, url) {
    // get timestamp for submission
    var submittedAt = new Date().getTime();

    var getBodyFn = function getBodyFn() {
      return new _promise2.default(function (resolve) {
        return resolve((0, _stringify2.default)({
          name: name,
          description: description,
          url: url,
          submittedAt: submittedAt
        }));
      });
    };

    return _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress + '/contracts/' + contractAddress + '/evidence');
  };

  this.updateDisputeProfile = function (userAddress, arbitratorAddress, disputeId, params) {
    var getBodyFn = function () {
      var _ref12 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee12() {
        var userProfile, currentDisputeProfile;
        return _regenerator2.default.wrap(function _callee12$(_context12) {
          while (1) {
            switch (_context12.prev = _context12.next) {
              case 0:
                _context12.next = 2;
                return _this.setUpUserProfile(userAddress);

              case 2:
                userProfile = _context12.sent;
                currentDisputeProfile = _lodash2.default.filter(userProfile.disputes, function (dispute) {
                  return dispute.arbitratorAddress === arbitratorAddress && dispute.disputeId === disputeId;
                })[0] || {};


                delete currentDisputeProfile._id;
                // set these so if it is a new dispute they are included
                params.disputeId = disputeId;
                params.arbitratorAddress = arbitratorAddress;

                return _context12.abrupt('return', (0, _stringify2.default)((0, _extends3.default)({}, currentDisputeProfile, params)));

              case 8:
              case 'end':
                return _context12.stop();
            }
          }
        }, _callee12, _this);
      }));

      return function getBodyFn() {
        return _ref12.apply(this, arguments);
      };
    }();

    return _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress + '/arbitrators/' + arbitratorAddress + '/disputes/' + disputeId);
  };

  this.newNotification = function () {
    var _ref13 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee13(userAddress, txHash, logIndex, notificationType) {
      var message = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : '';
      var data = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
      var read = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false;
      var getBodyFn;
      return _regenerator2.default.wrap(function _callee13$(_context13) {
        while (1) {
          switch (_context13.prev = _context13.next) {
            case 0:
              getBodyFn = function getBodyFn() {
                return new _promise2.default(function (resolve) {
                  return resolve((0, _stringify2.default)({
                    notificationType: notificationType,
                    logIndex: logIndex,
                    read: read,
                    message: message,
                    data: data
                  }));
                });
              };

              return _context13.abrupt('return', _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress + '/notifications/' + txHash));

            case 2:
            case 'end':
              return _context13.stop();
          }
        }
      }, _callee13, _this);
    }));

    return function (_x19, _x20, _x21, _x22) {
      return _ref13.apply(this, arguments);
    };
  }();

  this.markNotificationAsRead = function () {
    var _ref14 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee15(userAddress, txHash, logIndex) {
      var isRead = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
      var getBodyFn, result;
      return _regenerator2.default.wrap(function _callee15$(_context15) {
        while (1) {
          switch (_context15.prev = _context15.next) {
            case 0:
              getBodyFn = function () {
                var _ref15 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee14() {
                  var userProfile, notificationIndex;
                  return _regenerator2.default.wrap(function _callee14$(_context14) {
                    while (1) {
                      switch (_context14.prev = _context14.next) {
                        case 0:
                          _context14.next = 2;
                          return _this.setUpUserProfile(userAddress);

                        case 2:
                          userProfile = _context14.sent;
                          _context14.next = 5;
                          return _lodash2.default.findIndex(userProfile.notifications, function (notification) {
                            return notification.txHash === txHash && notification.logIndex === logIndex;
                          });

                        case 5:
                          notificationIndex = _context14.sent;

                          if (!_lodash2.default.isNull(notificationIndex)) {
                            _context14.next = 8;
                            break;
                          }

                          throw new Error(errorConstants.NOTIFICATION_NOT_FOUND(txHash));

                        case 8:

                          userProfile.notifications[notificationIndex].read = isRead;
                          delete userProfile._id;
                          delete userProfile.created_at;
                          return _context14.abrupt('return', (0, _stringify2.default)(userProfile));

                        case 12:
                        case 'end':
                          return _context14.stop();
                      }
                    }
                  }, _callee14, _this);
                }));

                return function getBodyFn() {
                  return _ref15.apply(this, arguments);
                };
              }();

              _context15.next = 3;
              return _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress);

            case 3:
              result = _context15.sent;
              return _context15.abrupt('return', result.body.notifications);

            case 5:
            case 'end':
              return _context15.stop();
          }
        }
      }, _callee15, _this);
    }));

    return function (_x26, _x27, _x28) {
      return _ref14.apply(this, arguments);
    };
  }();

  this._storeUri = storeProviderUri;
  this._token = authToken;
  this._storeQueue = new _PromiseQueue2.default();
}

/**
 * Helper method for sending an http request to kleros store.
 * @param {string} verb - HTTP verb to be used in request. E.g. GET, POST, PUT.
 * @param {string} uri - The uri to send the request to.
 * @param {string} body - json string of the body.
 * @returns {Promise} request promise that resolves to the HTTP response.
 */


/**
 * use the queue for write request. this allows a function to be passed so we can read immediately before we write
 * @param {fn} getBodyFn async function to call before we write. Should to reads and return JSON to be used as body.
 * @param {string} verb POST or PUT
 * @param {string} uri uri to call
 * @returns {promise} promise that returns result of request. wait on this if you need it to be syncronous
 */


/**
 * If we know we are waiting on some other write before we want to read we can add a read request to the end of the queue.
 * @param {string} uri uri to hit
 * @returns {Promise} promise of the result function
 */


// **************************** //
// *          Auth            * //
// **************************** //

/**
 * Set the auth token for write requests.
 * @param {string} token - Hex string of the signed data token.
 */


/**
 * Generate a new unsigned auth token.
 * @param {string} userAddress - Address of the user profile.
 * @returns {string} Hex encoded unsigned token.
 */


/**
 * Validate auth token
 * @param {string} userAddress - Address of user profile.
 * @param {string} token - <optional> token to use. Sets token.
 * @returns {bool} - True if token is valid.
 */


// **************************** //
// *          Read            * //
// **************************** //

/**
 * Fetch stored user profile.
 * @param {string} userAddress - Address of user.
 * @returns {object} - a response object.
 */


/**
 * Fetch stored data on a contract for a user.
 * @param {string} userAddress - Address of the user.
 * @param {string} addressContract - The address of the contract.
 * @returns {object} - Contact data.
 */


/**
 * Get all stored data for a dispute. Must exist in User Profile.
 * @param {string} userAddress - Address of user.
 * @param {string} arbitratorAddress - Address of arbitrator contract.
 * @param {number} disputeId - Index of the dispute.
 * @returns {object} - a response object.
 */


/**
 * Fetch stored disputes for a user.
 * @param {string} userAddress - Address of user.
 * @returns {object} - a response object.
 */


/**
 * Fetch the last block seen for a user. This is commonly used with EventListerer.
 * @param {string} userAddress - Address of user.
 * @returns {number} The last block number.
 */


// **************************** //
// *          Write           * //
// **************************** //

/**
 * Update user profile. WARNING: This should only be used for session and lastBlock.
 * Overwriting arrays of unstructured data can lead to data loss.
 * @param {string} userAddress - users userAddress
 * @param {object} params - object containing kwargs to update
 * @returns {promise} - resulting profile
 */


/**
 * Set up a new user profile if one does not exist.
 * @param {string} userAddress - user's address
 * @returns {object} - users existing or created profile
 */


/**
 * Update the stored data on a contract for a user.
 * @param {string} userAddress - The user's address.
 * @param {string} contractAddress - The address of the contract.
 * @param {object} params - Params we want to update.
 * @returns {Promise} - The resulting contract data.
 */


/**
 * Adds new evidence to the store for a users contract. NOTE this will only update the
 * stored evidence for the specified user, not all parties of the dispute.
 * @param {string} contractAddress - Address of the contract
 * @param {string} userAddress - Address of the user.
 * @param {string} name - Name of evidence.
 * @param {string} description - Description of evidence.
 * @param {string} url - A link to the evidence.
 * @returns {Promise} - The resulting evidence data.
 */


/**
 * Update stored dispute data for a user.
 * @param {string} userAddress - The address of the user.
 * @param {string} arbitratorAddress - The address of the arbitrator contract.
 * @param {number} disputeId - The index of the dispute.
 * @param {object} params - The dispute data we are updating.
 * @returns {Promise} The resulting dispute data.
 */


/**
 * Create a new notification in the store.
 * @param {string} userAddress - The address of the user.
 * @param {string} txHash - The transaction hash which produced this event log. Used as an identifier.
 * @param {number} logIndex - The index of the log in the transaction. Used as an identifier.
 * @param {number} notificationType - The type of the notification. See constants/notification.
 * @param {string} message - The message to be stored with the notification.
 * @param {object} data - Any extra data stored with the notification.
 * @param {boolean} read - If the notification has been read or not.
 * @returns {Promise} - The resulting notification.
 */


/**
 * Create a new notification in the store.
 * @param {string} userAddress - The address of the user.
 * @param {string} txHash - The transaction hash which produced this event log. Used as an identifier.
 * @param {number} logIndex - The index of the log in the transaction. Used as an identifier.
 * @param {boolean} isRead - If the notification has been read or not.
 * @returns {Promise} - The resulting notification.
 */
;

exports.default = StoreProviderWrapper;
//# sourceMappingURL=StoreProviderWrapper.js.map