import _extends from 'babel-runtime/helpers/extends';
import _JSON$stringify from 'babel-runtime/core-js/json/stringify';
import _Promise from 'babel-runtime/core-js/promise';
import _regeneratorRuntime from 'babel-runtime/regenerator';
import _asyncToGenerator from 'babel-runtime/helpers/asyncToGenerator';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _ from 'lodash';

import * as errorConstants from '../constants/error';

import PromiseQueue from './PromiseQueue';
import httpRequest from './httpRequest';

/**
 * A wrapper for interacting with Kleros Store.
 */

var StoreProviderWrapper =
/**
 * Create a new instance of StoreProviderWrapper.
 * @param {string} storeProviderUri - The uri of kleros store.
 */
function StoreProviderWrapper(storeProviderUri) {
  var _this = this;

  _classCallCheck(this, StoreProviderWrapper);

  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 httpRequest(verb, uri, result);
      });
    });
  };

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

  this.getMetaEvidenceUri = function (userAddress, contractAddress) {
    return _this._storeUri + '/' + userAddress + '/contracts/' + contractAddress + '/meta-evidence';
  };

  this.getEvidenceUri = function (userAddress, contractAddress, evidenceIndex) {
    return _this._storeUri + '/' + userAddress + '/contracts/' + contractAddress + '/evidence/' + evidenceIndex;
  };

  this.getUserProfile = function () {
    var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(userAddress) {
      var httpResponse;
      return _regeneratorRuntime.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              _context.next = 2;
              return httpRequest('GET', _this._storeUri + '/' + userAddress);

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

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

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

  this.getContractByAddress = function () {
    var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(userAddress, addressContract) {
      var userProfile, contract;
      return _regeneratorRuntime.wrap(function _callee2$(_context2) {
        while (1) {
          switch (_context2.prev = _context2.next) {
            case 0:
              _context2.next = 2;
              return _this.getUserProfile(userAddress);

            case 2:
              userProfile = _context2.sent;

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

              return _context2.abrupt('return', {});

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

            case 7:
            case 'end':
              return _context2.stop();
          }
        }
      }, _callee2, _this);
    }));

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

  this.getDispute = function () {
    var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(userAddress, arbitratorAddress, disputeId) {
      var userProfile;
      return _regeneratorRuntime.wrap(function _callee3$(_context3) {
        while (1) {
          switch (_context3.prev = _context3.next) {
            case 0:
              _context3.next = 2;
              return _this.getUserProfile(userAddress);

            case 2:
              userProfile = _context3.sent;

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

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

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

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

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

  this.getDisputes = function () {
    var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(userAddress) {
      var userProfile;
      return _regeneratorRuntime.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;
              }

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

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

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

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

  this.getLastBlock = function () {
    var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(userAddress) {
      var userProfile;
      return _regeneratorRuntime.wrap(function _callee5$(_context5) {
        while (1) {
          switch (_context5.prev = _context5.next) {
            case 0:
              userProfile = void 0;
              _context5.prev = 1;
              _context5.next = 4;
              return _this.newUserProfile(userAddress);

            case 4:
              userProfile = _context5.sent;
              _context5.next = 10;
              break;

            case 7:
              _context5.prev = 7;
              _context5.t0 = _context5['catch'](1);

              userProfile = {};

            case 10:
              return _context5.abrupt('return', userProfile.lastBlock || 0);

            case 11:
            case 'end':
              return _context5.stop();
          }
        }
      }, _callee5, _this, [[1, 7]]);
    }));

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

  this.newUserProfile = function () {
    var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(userAddress) {
      var userProfile, response;
      return _regeneratorRuntime.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 (!_.isNull(userProfile)) {
                _context6.next = 10;
                break;
              }

              _context6.next = 6;
              return httpRequest('POST', _this._storeUri + '/' + userAddress);

            case 6:
              response = _context6.sent;

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

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

            case 9:
              userProfile = response.body;

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

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

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

  this.updateLastBlock = function () {
    var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(userAddress, blockNumber) {
      var getBodyFn;
      return _regeneratorRuntime.wrap(function _callee7$(_context7) {
        while (1) {
          switch (_context7.prev = _context7.next) {
            case 0:
              getBodyFn = function getBodyFn() {
                return new _Promise(function (resolve) {
                  return resolve(_JSON$stringify({
                    lastBlock: blockNumber
                  }));
                });
              };

              return _context7.abrupt('return', _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress + '/lastBlock'));

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

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

  this.updateUserSession = function () {
    var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee8(userAddress, session) {
      var getBodyFn;
      return _regeneratorRuntime.wrap(function _callee8$(_context8) {
        while (1) {
          switch (_context8.prev = _context8.next) {
            case 0:
              getBodyFn = function getBodyFn() {
                return new _Promise(function (resolve) {
                  return resolve(_JSON$stringify({
                    session: session
                  }));
                });
              };

              return _context8.abrupt('return', _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress + '/session'));

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

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

  this.updateContract = function () {
    var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee10(userAddress, contractAddress, params) {
      var getBodyFn, httpResponse;
      return _regeneratorRuntime.wrap(function _callee10$(_context10) {
        while (1) {
          switch (_context10.prev = _context10.next) {
            case 0:
              getBodyFn = function () {
                var _ref10 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee9() {
                  var currentContractData;
                  return _regeneratorRuntime.wrap(function _callee9$(_context9) {
                    while (1) {
                      switch (_context9.prev = _context9.next) {
                        case 0:
                          _context9.next = 2;
                          return _this.getContractByAddress(userAddress, contractAddress);

                        case 2:
                          currentContractData = _context9.sent;

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

                          params.address = contractAddress;

                          return _context9.abrupt('return', _JSON$stringify(_extends({}, currentContractData, params)));

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

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

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

            case 3:
              httpResponse = _context10.sent;
              return _context10.abrupt('return', _.filter(httpResponse.body[0].contracts, function (contract) {
                return contract.address === contractAddress;
              })[0]);

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

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

  this.addEvidenceContract = function () {
    var _ref11 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee11(contractAddress, userAddress, name, description, url, hash) {
      var getBodyFn, response;
      return _regeneratorRuntime.wrap(function _callee11$(_context11) {
        while (1) {
          switch (_context11.prev = _context11.next) {
            case 0:
              getBodyFn = function getBodyFn() {
                return new _Promise(function (resolve) {
                  return resolve(_JSON$stringify({
                    name: name,
                    description: description,
                    URI: url,
                    hash: hash
                  }));
                });
              };

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

            case 3:
              response = _context11.sent;

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

              throw new Error(errorConstants.REQUEST_FAILED('Unable to submit evidence'));

            case 6:
              return _context11.abrupt('return', response.body.evidenceIndex);

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

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

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

              case 2:
                userProfile = _context12.sent;
                currentDisputeProfile = _.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', _JSON$stringify(_extends({}, 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.addNewDrawsDisputeProfile = function (userAddress, arbitratorAddress, disputeId, draws, appeal) {
    var getBodyFn = function getBodyFn() {
      return new _Promise(function (resolve) {
        return resolve(_JSON$stringify({
          draws: draws,
          appeal: appeal
        }));
      });
    };

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

  this.newNotification = function () {
    var _ref13 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.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 _regeneratorRuntime.wrap(function _callee13$(_context13) {
        while (1) {
          switch (_context13.prev = _context13.next) {
            case 0:
              getBodyFn = function getBodyFn() {
                return new _Promise(function (resolve) {
                  return resolve(_JSON$stringify({
                    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 (_x24, _x25, _x26, _x27) {
      return _ref13.apply(this, arguments);
    };
  }();

  this.markNotificationAsRead = function () {
    var _ref14 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee14(userAddress, txHash, logIndex) {
      var isRead = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
      var getBodyFn, result;
      return _regeneratorRuntime.wrap(function _callee14$(_context14) {
        while (1) {
          switch (_context14.prev = _context14.next) {
            case 0:
              getBodyFn = function getBodyFn() {
                return new _Promise(function (resolve) {
                  return resolve(_JSON$stringify({
                    logIndex: logIndex,
                    isRead: isRead
                  }));
                });
              };

              _context14.next = 3;
              return _this.queueWriteRequest(getBodyFn, 'POST', _this._storeUri + '/' + userAddress + '/notifications/' + txHash + '/read');

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

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

    return function (_x31, _x32, _x33) {
      return _ref14.apply(this, arguments);
    };
  }();

  this._storeUri = storeProviderUri;
  this._storeQueue = new PromiseQueue();
}

/**
 * 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
 */


// **************************** //
// *          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           * //
// **************************** //

/**
 * 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 users last block seen. This is the only item in user profile that can be overwritten.
 * @param {string} userAddress - User's address.
 * @param {string} blockNumber - The newest block number seen by user.
 * @returns {object} - HTTP response.
 */


/**
 * Update users last processed session.
 * @param {string} userAddress - User's address.
 * @param {string} session - The current session that the user has processed
 * @returns {object} - HTTP response.
 */


/**
 * Update the stored data on a contract for a user. Note that you cannot overwrite contract data.
 * @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 {number} - The index of the evidence
 */


/**
 * Update stored dispute data for a user. Note this will not overwrite data.
 * @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.
 */


/**
 * Adds draws for juror to dispute profile.
 * @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 {number[]} draws - The draws the juror has.
 * @param {number} appeal - The appeal for which it is for.
 * @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.
 */
;

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