import models from '../../models';
import roles from '../../constants/roles';
import * as libpayments from '../../lib/payments';
import * as libtransactions from '../../lib/transactions';
import { TransactionTypes, OC_FEE_PERCENT } from '../../constants/transactions';
import { get } from 'lodash';

/** Get the balance of a prepaid credit card
 *
 * When a card is created by a host (by adding funds to an
 * organization for example) the card is created with an initial
 * balance. This function subtracts the amount from transactions made
 * with this card from the initial balance.
 *
 * @param {models.PaymentMethod} paymentMethod is the instance of the
 *  prepaid credit card payment method.
 * @return {Object} with amount & currency from the payment method.
 */
async function getBalance(paymentMethod) {
  if (!libpayments.isProvider('opencollective.prepaid', paymentMethod)) {
    throw new Error(
      `Expected opencollective.prepaid but got ${paymentMethod.service}.${
        paymentMethod.type
      }`,
    );
  }
  /* Result will be negative (We're looking for DEBIT transactions) */
  const spent = await libtransactions.sum({
    PaymentMethodId: paymentMethod.id,
    currency: paymentMethod.currency,
    type: 'DEBIT',
  });
  return {
    amount: paymentMethod.initialBalance + spent,
    currency: paymentMethod.currency,
  };
}

/** Process a pre paid card order
 *
 * @param {models.Order} order The order instance to be processed.
 * @return {models.Transaction} As any other payment method, after
 *  processing Giftcard orders, the transaction generated from it is
 *  returned.
 */
async function processOrder(order) {
  const user = order.createdByUser;
  const {
    paymentMethod: { data },
  } = order;
  // Making sure the paymentMethod has the information we need to
  // process a prepaid card
  if (!get(data, 'HostCollectiveId'))
    throw new Error(
      'Prepaid payment method must have a value for `data.HostCollectiveId`',
    );

  // Check that target Collective's Host is same as gift card issuer
  const hostCollective = await order.collective.getHostCollective();
  if (hostCollective.id !== data.HostCollectiveId)
    throw new Error(
      'Prepaid method can only be used in collectives from the same host',
    );

  // Use the above payment method to donate to Collective
  const hostFeeInHostCurrency = libpayments.calcFee(
    order.totalAmount,
    order.collective.hostFeePercent,
  );
  const platformFeeInHostCurrency = libpayments.calcFee(
    order.totalAmount,
    OC_FEE_PERCENT,
  );
  const transactions = await models.Transaction.createFromPayload({
    CreatedByUserId: user.id,
    FromCollectiveId: order.FromCollectiveId,
    CollectiveId: order.CollectiveId,
    PaymentMethodId: order.paymentMethod.id,
    transaction: {
      type: TransactionTypes.CREDIT,
      OrderId: order.id,
      amount: order.totalAmount,
      amountInHostCurrency: order.totalAmount,
      currency: order.currency,
      hostCurrency: order.currency,
      hostCurrencyFxRate: 1,
      hostFeeInHostCurrency,
      platformFeeInHostCurrency,
      paymentProcessorFeeInHostCurrency: 0,
      description: order.description,
    },
  });

  // add roles
  await order.collective.findOrAddUserWithRole(
    { id: user.id, CollectiveId: order.fromCollective.id },
    roles.BACKER,
    {
      CreatedByUserId: user.id,
      TierId: order.TierId,
    },
  );

  // Mark order row as processed
  await order.update({ processedAt: new Date() });

  // Mark paymentMethod as confirmed
  order.paymentMethod.update({ confirmedAt: new Date() });

  return transactions;
}

/* Expected API of a Payment Method Type */
export default {
  features: {
    recurring: true,
    waitToCharge: false,
  },
  getBalance,
  processOrder,
};

@@ -134,31 +134,24 @@
 rt * as 
-libtransactions
+currency
  from '.
@@ -159,28 +159,24 @@
 /../lib/
-transactions
+currency
 ';%0Aimpor
@@ -1093,21 +1093,31 @@
   const 
-spent
+allTransactions
  = await
@@ -1121,12 +1121,16 @@
 ait 
-libt
+models.T
 rans
@@ -1139,67 +1139,262 @@
 tion
-s.sum(%7B%0A    PaymentMethodId: paymentMethod.id,%0A    
+.findAll(%7B%0A    attributes: %5B'netAmountInCollectiveCurrency', 'currency'%5D,%0A    where: %7B%0A      PaymentMethodId: paymentMethod.id,%0A      type: 'DEBIT',%0A    %7D%0A  %7D);%0A  let spent = 0;%0A  for ( const transaction of allTransactions) %7B%0A    if (transaction.
 currency
 : pa
@@ -1381,33 +1381,35 @@
 saction.currency
-:
+ !=
  paymentMethod.c
@@ -1419,34 +1419,252 @@
 ency
-,%0A    type: 'DEBIT',%0A  %7D);
+) %7B%0A      const fxRate = await currency.getFxRate(transaction.currency, paymentMethod.currency);%0A      spent += transaction.netAmountInCollectiveCurrency * fxRate;%0A    %7D else %7B%0A      spent += transaction.netAmountInCollectiveCurrency;%0A    %7D%0A  %7D
 %0A  r

fix getBalance query