import _slicedToArray from 'babel-runtime/helpers/slicedToArray';
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 arbitratorConstants from '../constants/arbitrator';
import * as disputeConstants from '../constants/dispute';
import isRequired from '../utils/isRequired';

/**
 * Disputes API. Provides cross arbitrator and arbitrable contracts functionality.
 * Requires Store Provider to be set.
 */

var Disputes = function Disputes() {
  var arbitratorInstance = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : isRequired('arbitratorInstance');
  var arbitrableInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : isRequired('arbitrableInstance');
  var storeProviderInstance = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : isRequired('storeProviderInstance');

  _classCallCheck(this, Disputes);

  _initialiseProps.call(this);

  this._ArbitratorInstance = arbitratorInstance;
  this._ArbitrableInstance = arbitrableInstance;
  this._StoreProviderInstance = storeProviderInstance;
}
/**
 * Set arbitrator instance.
 * @param {object} arbitratorInstance - instance of an arbitrator contract.
 */

/**
 * Set arbitrable instance.
 * @param {object} arbitrableInstance - instance of an arbitrable contract.
 */

/**
 * Set store provider instance.
 * @param {object} storeProviderInstance - instance of store provider wrapper.
 */


// **************************** //
// *         Events           * //
// **************************** //

/**
 * Method to register all dispute handlers to an EventListener.
 * @param {string} account - The address of the user.
 * @param {object} eventListener - The EventListener instance. See utils/EventListener.js.
 */


/**
 * Event listener handler that stores dispute in store upon creation
 * @param {string} event - The event log.
 * @param {string} account - Account of user to update store data for
 */


/**
 * Event listener handler that add or substract the stored Net PNK won/lost for a juror.
 * @param {string} event - The event log.
 * @param {string} account - The account.
 */


/**
 * Event listener handler that updates ruled at timestamp
 * @param {object} event - The event log.
 * @param {string} account - The users eth account.
 */


/**
 * Event listener handler that sets the deadline for an appeal
 * @param {object} _ - The event log. Unused in function.
 * @param {string} account - The users eth account.
 */


// **************************** //
// *          Public          * //
// **************************** //
/**
 * Fetch the shared dispute data from the store.
 * @param {string} disputeId - The index of the dispute.
 * @returns {Promise} The dispute data in the store.
 */


/**
 * Get data for a dispute. This method provides data from the store as well as both
 * arbitrator and arbitrable contracts. Used to get all relevant data on a dispute.
 * @param {number} disputeId - The dispute's ID.
 * @param {string} account - The juror's address.
 * @returns {object} - Data object for the dispute that uses data from the contract and the store.
 */
;

var _initialiseProps = function _initialiseProps() {
  var _this = this;

  this.setArbitratorInstance = function (arbitratorInstance) {
    _this._ArbitratorInstance = arbitratorInstance;
  };

  this.setArbitrableInstance = function (arbitrableInstance) {
    _this._ArbitrableInstance = arbitrableInstance;
  };

  this.setStoreProviderInstance = function (storeProviderInstance) {
    _this._StoreProviderInstance = storeProviderInstance;
  };

  this.registerStoreUpdateEventListeners = function () {
    var account = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : isRequired('account');
    var eventListener = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : isRequired('eventListener');

    var eventHandlerMap = {
      DisputeCreation: [_this._storeNewDisputeHandler],
      TokenShift: [_this._storeTokensMovedForJuror],
      NewPeriod: [_this._storeDisputeRuledAtTimestamp, _this._storeAppealDeadline]
    };

    var _loop = function _loop(event) {
      if (eventHandlerMap.hasOwnProperty(event)) {
        eventHandlerMap[event].forEach(function (handler) {
          eventListener.addEventHandler(_this._ArbitratorInstance, event, function (args) {
            return handler(args, account);
          });
        });
      }
    };

    for (var event in eventHandlerMap) {
      _loop(event);
    }
  };

  this._storeNewDisputeHandler = function () {
    var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(event, account) {
      var disputeId, arbitratorAddress, disputeData, arbitrableContractData, blockTimestamp, storedDispute, appealCreatedAt;
      return _regeneratorRuntime.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              // There is no need to handle this event if we are not using the store
              disputeId = event.args._disputeID.toNumber();
              arbitratorAddress = _this._ArbitratorInstance.getContractAddress();
              // arbitrator data

              _context.next = 4;
              return _this._ArbitratorInstance.getDispute(disputeId);

            case 4:
              disputeData = _context.sent;
              _context.next = 7;
              return _this._ArbitrableInstance.setContractInstance(disputeData.arbitrableContractAddress);

            case 7:
              _context.next = 9;
              return _this._ArbitrableInstance.getData(account);

            case 9:
              arbitrableContractData = _context.sent;

              if (!(account === arbitrableContractData.partyA || account === arbitrableContractData.partyB)) {
                _context.next = 19;
                break;
              }

              _context.next = 13;
              return _this._ArbitratorInstance.getBlock(event.blockNumber);

            case 13:
              blockTimestamp = _context.sent.timestamp;


              // Check if dispute has already been stored. This can happen if there was an appeal
              storedDispute = _this._StoreProviderInstance.getDisputeDataForUser(arbitrableContractData.partyA, arbitratorAddress, disputeId);
              appealCreatedAt = storedDispute.appealCreatedAt || [];

              appealCreatedAt[disputeData.numberOfAppeals] = blockTimestamp * 1000;

              _context.next = 19;
              return _this._StoreProviderInstance.updateDisputeProfile(account, arbitratorAddress, disputeId, {
                contractAddress: disputeData.arbitrableContractAddress,
                partyA: arbitrableContractData.partyA,
                partyB: arbitrableContractData.partyB,
                appealCreatedAt: appealCreatedAt
              });

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

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

  this._storeTokensMovedForJuror = function () {
    var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(event, account) {
      var disputeId, address, amountShift, userProfile, contractAddress, disputeIndex, dispute;
      return _regeneratorRuntime.wrap(function _callee2$(_context2) {
        while (1) {
          switch (_context2.prev = _context2.next) {
            case 0:
              disputeId = event.args._disputeID.toNumber();
              address = event.args._account;
              amountShift = event.args._amount.toNumber();
              // juror won/lost tokens

              if (!(address === account)) {
                _context2.next = 14;
                break;
              }

              _context2.next = 6;
              return _this._StoreProviderInstance.setUpUserProfile(account);

            case 6:
              userProfile = _context2.sent;
              contractAddress = _this._ArbitratorInstance.getContractAddress();
              disputeIndex = _.findIndex(userProfile.disputes, function (dispute) {
                return dispute.disputeId === disputeId && dispute.arbitratorAddress === contractAddress;
              });

              // if dispute is not in store ignore

              if (!(disputeIndex < 0)) {
                _context2.next = 11;
                break;
              }

              return _context2.abrupt('return');

            case 11:
              dispute = userProfile.disputes[disputeIndex];
              _context2.next = 14;
              return _this._StoreProviderInstance.updateDisputeProfile(account, dispute.arbitratorAddress, dispute.disputeId, {
                netPNK: (dispute.netPNK || 0) + amountShift
              });

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

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

  this._storeDisputeRuledAtTimestamp = function () {
    var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(event, account) {
      var newPeriod, disputes, openDisputes, contractAddress;
      return _regeneratorRuntime.wrap(function _callee4$(_context4) {
        while (1) {
          switch (_context4.prev = _context4.next) {
            case 0:
              _context4.next = 2;
              return _this._ArbitratorInstance.getPeriod();

            case 2:
              newPeriod = _context4.sent;

              if (!(newPeriod === arbitratorConstants.PERIOD.APPEAL)) {
                _context4.next = 13;
                break;
              }

              _context4.next = 6;
              return _this._StoreProviderInstance.getDisputes(account);

            case 6:
              disputes = _context4.sent;
              _context4.next = 9;
              return _this._ArbitratorInstance.getOpenDisputesForSession();

            case 9:
              openDisputes = _context4.sent;
              contractAddress = _this._ArbitratorInstance.getContractAddress();
              _context4.next = 13;
              return _Promise.all(openDisputes.map(function () {
                var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(openDispute) {
                  var disputeIndex, blockNumber, blockTimestamp, appealRuledAt;
                  return _regeneratorRuntime.wrap(function _callee3$(_context3) {
                    while (1) {
                      switch (_context3.prev = _context3.next) {
                        case 0:
                          disputeIndex = _.findIndex(disputes, function (dispute) {
                            return dispute.disputeId === openDispute.disputeId && dispute.arbitratorAddress === contractAddress;
                          });

                          if (!(disputeIndex >= 0)) {
                            _context3.next = 9;
                            break;
                          }

                          // get ruledAt from block timestamp
                          blockNumber = event.blockNumber;
                          _context3.next = 5;
                          return _this._ArbitratorInstance.getBlock(blockNumber);

                        case 5:
                          blockTimestamp = _context3.sent.timestamp;
                          appealRuledAt = disputes[disputeIndex].appealRuledAt || [];

                          appealRuledAt[openDispute.numberOfAppeals] = blockTimestamp * 1000;

                          _this._StoreProviderInstance.updateDisputeProfile(account, contractAddress, openDispute.disputeId, {
                            appealRuledAt: appealRuledAt
                          });

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

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

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

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

  this._storeAppealDeadline = function () {
    var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee6(_, account) {
      var newPeriod, disputes, openDisputes, contractAddress;
      return _regeneratorRuntime.wrap(function _callee6$(_context6) {
        while (1) {
          switch (_context6.prev = _context6.next) {
            case 0:
              _context6.next = 2;
              return _this._ArbitratorInstance.getPeriod();

            case 2:
              newPeriod = _context6.sent;

              if (!(newPeriod === arbitratorConstants.PERIOD.VOTE)) {
                _context6.next = 13;
                break;
              }

              _context6.next = 6;
              return _this._StoreProviderInstance.getDisputes(account);

            case 6:
              disputes = _context6.sent;
              _context6.next = 9;
              return _this._ArbitratorInstance.getOpenDisputesForSession();

            case 9:
              openDisputes = _context6.sent;
              contractAddress = _this._ArbitratorInstance.getContractAddress();
              _context6.next = 13;
              return _Promise.all(openDisputes.map(function () {
                var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(openDispute) {
                  var disputeIndex, deadline, appealDeadlines;
                  return _regeneratorRuntime.wrap(function _callee5$(_context5) {
                    while (1) {
                      switch (_context5.prev = _context5.next) {
                        case 0:
                          disputeIndex = _.findIndex(disputes, function (dispute) {
                            return dispute.disputeId === openDispute.disputeId && dispute.arbitratorAddress === contractAddress;
                          });

                          if (!(disputeIndex >= 0)) {
                            _context5.next = 8;
                            break;
                          }

                          _context5.next = 4;
                          return _this._ArbitratorInstance.getDeadlineForOpenDispute();

                        case 4:
                          deadline = _context5.sent;
                          appealDeadlines = disputes[disputeIndex].appealDeadlines || [];

                          appealDeadlines[openDispute.numberOfAppeals] = deadline;

                          _this._StoreProviderInstance.updateDisputeProfile(account, contractAddress, openDispute.disputeId, {
                            appealDeadlines: appealDeadlines
                          });

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

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

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

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

  this.getDisputeFromStore = function (disputeId) {
    var arbitratorAddress = _this._ArbitratorInstance.getContractAddress();

    return _this._StoreProviderInstance.getDispute(arbitratorAddress, disputeId);
  };

  this.getDataForDispute = function () {
    var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee7(disputeId, account) {
      var arbitratorAddress, _ref8, _ref9, dispute, period, session, arbitrableContractAddress, _ref10, _ref11, arbitrableContractData, evidence, contractStoreData, appealDraws, appealCreatedAt, appealDeadlines, appealRuledAt, netPNK, userData, lastSession, appealJuror, appealRulings, appeal, isLastAppeal, draws, canRule, canRepartition, canExecute, ruling, rulingPromises, _ref12, _ref13;

      return _regeneratorRuntime.wrap(function _callee7$(_context7) {
        while (1) {
          switch (_context7.prev = _context7.next) {
            case 0:
              arbitratorAddress = _this._ArbitratorInstance.getContractAddress();
              // Get dispute data from contract. Also get the current session and period.

              _context7.next = 3;
              return _Promise.all([_this._ArbitratorInstance.getDispute(disputeId), _this._ArbitratorInstance.getPeriod(), _this._ArbitratorInstance.getSession()]);

            case 3:
              _ref8 = _context7.sent;
              _ref9 = _slicedToArray(_ref8, 3);
              dispute = _ref9[0];
              period = _ref9[1];
              session = _ref9[2];


              // Get arbitrable contract data and evidence
              arbitrableContractAddress = dispute.arbitrableContractAddress;
              _context7.next = 11;
              return _this._ArbitrableInstance.setContractInstance(arbitrableContractAddress);

            case 11:
              _context7.next = 13;
              return _Promise.all([_this._ArbitrableInstance.getData(account), _this._ArbitrableInstance.getEvidenceForArbitrableContract(arbitrableContractAddress)]);

            case 13:
              _ref10 = _context7.sent;
              _ref11 = _slicedToArray(_ref10, 2);
              arbitrableContractData = _ref11[0];
              evidence = _ref11[1];
              _context7.next = 19;
              return _this._StoreProviderInstance.getContractByAddress(arbitrableContractData.partyA, arbitrableContractAddress);

            case 19:
              contractStoreData = _context7.sent;


              // Get dispute data from the store
              appealDraws = [];
              appealCreatedAt = [];
              appealDeadlines = [];
              appealRuledAt = [];
              netPNK = 0;
              _context7.prev = 25;
              _context7.next = 28;
              return _this._StoreProviderInstance.getDisputeDataForUser(account, arbitratorAddress, disputeId);

            case 28:
              userData = _context7.sent;

              if (userData.appealDraws) appealDraws = userData.appealDraws || [];
              if (userData.appealCreatedAt) appealCreatedAt = userData.appealCreatedAt || [];
              if (userData.appealDeadlines) appealDeadlines = userData.appealDeadlines || [];
              if (userData.appealRuledAt) appealRuledAt = userData.appealRuledAt || [];
              if (userData.netPNK) netPNK = userData.netPNK;
              // eslint-disable-next-line no-unused-vars
              _context7.next = 38;
              break;

            case 36:
              _context7.prev = 36;
              _context7.t0 = _context7['catch'](25);

            case 38:

              // Build juror info and ruling arrays, indexed by appeal number
              lastSession = dispute.firstSession + dispute.numberOfAppeals;
              appealJuror = [];
              appealRulings = [];
              appeal = 0;

            case 42:
              if (!(appeal <= dispute.numberOfAppeals)) {
                _context7.next = 63;
                break;
              }

              isLastAppeal = dispute.firstSession + appeal === lastSession;

              // Get appeal data

              draws = appealDraws[appeal] || [];
              canRule = false;
              canRepartition = false;
              canExecute = false;
              ruling = void 0;
              rulingPromises = [_this._ArbitratorInstance.currentRulingForDispute(disputeId, appeal)];

              // Extra info for the last appeal

              if (isLastAppeal) {
                if (draws.length > 0) rulingPromises.push(_this._ArbitratorInstance.canRuleDispute(disputeId, draws, account));

                if (session && period) canRepartition = lastSession <= session && // Not appealed to the next session
                period === arbitratorConstants.PERIOD.EXECUTE && // Executable period
                dispute.state === disputeConstants.STATE.OPEN; // Open dispute
                canExecute = dispute.state === disputeConstants.STATE.EXECUTABLE; // Executable state
              }

              // Wait for parallel requests to complete
              ;_context7.next = 54;
              return _Promise.all(rulingPromises);

            case 54:
              _ref12 = _context7.sent;
              _ref13 = _slicedToArray(_ref12, 2);
              ruling = _ref13[0];
              canRule = _ref13[1];


              appealJuror[appeal] = {
                createdAt: appealCreatedAt[appeal],
                fee: dispute.arbitrationFeePerJuror * draws.length,
                draws: draws,
                canRule: canRule
              };
              appealRulings[appeal] = {
                voteCounter: dispute.voteCounters[appeal],
                deadline: appealDeadlines[appeal],
                ruledAt: appealRuledAt[appeal],
                ruling: ruling,
                canRepartition: canRepartition,
                canExecute: canExecute
              };

            case 60:
              appeal++;
              _context7.next = 42;
              break;

            case 63:
              return _context7.abrupt('return', {
                // Arbitrable Contract Data
                arbitrableContractAddress: arbitrableContractAddress,
                arbitrableContractStatus: arbitrableContractData.status,
                arbitratorAddress: arbitratorAddress,
                partyA: arbitrableContractData.partyA,
                partyB: arbitrableContractData.partyB,

                // Dispute Data
                disputeId: disputeId,
                firstSession: dispute.firstSession,
                lastSession: lastSession,
                numberOfAppeals: dispute.numberOfAppeals,
                disputeState: dispute.state,
                disputeStatus: dispute.status,
                appealJuror: appealJuror,
                appealRulings: appealRulings,

                // Store Data
                description: contractStoreData ? contractStoreData.description : undefined,
                email: contractStoreData ? contractStoreData.email : undefined,
                evidence: evidence,
                netPNK: netPNK,

                // Deprecated Data // TODO: Remove
                appealCreatedAt: appealCreatedAt,
                appealDeadlines: appealDeadlines,
                appealRuledAt: appealRuledAt
              });

            case 64:
            case 'end':
              return _context7.stop();
          }
        }
      }, _callee7, _this, [[25, 36]]);
    }));

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

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