import ITransaction from '@/models/ITransaction'
import IBalance from '@/models/IBalance'
import ICurrency from '@/models/ICurrency'
import IRefund from '@/models/IRefund'
import IUser from '@/models/IUser'
import exchangeService from '@/services/ExchangeService'
import transactionService from '@/services/TransactionService'
import ISplit from '@/models/ISplit'

class BalanceService {
  public getBalanceByUser(
    user: IUser,
    currency: ICurrency,
    transactions: ITransaction[]
  ): IBalance {
    const convert = exchangeService.getAmountInBase
    const transactionPayedAmount: number = transactions
      .filter((transaction: ITransaction) => transaction.payBy === user.alias)
      .reduce((amount: number, transaction: ITransaction) => {
        const convertedAmount = convert(
          transaction.amount,
          currency.code,
          transaction.exchange
        )
        return amount + convertedAmount
      }, 0)
    const transactionToRefundAmout: number = transactions
      .filter((transaction: ITransaction) =>
        transaction.payFor
          .map((p: ISplit) => p.alias)
          .includes(user.alias || '')
      )
      .reduce((amount: number, transaction: ITransaction) => {
        const convertedAmount = convert(
          transaction.amount *
            transactionService.getMultiply(transaction, user.alias || ''),
          currency.code,
          transaction.exchange
        )
        const totalWeight = transaction.payFor
          .map((p: ISplit) => p.weight)
          .reduce((w, weight) => w + weight)
        return amount + convertedAmount / totalWeight
      }, 0)
    return {
      user,
      amount: this.round(transactionPayedAmount - transactionToRefundAmout)
    }
  }

  public getRefund(balances: IBalance[]): IRefund[] {
    const giver: IBalance | undefined = balances.find(
      (balance: IBalance) => balance.amount < 0
    )
    const receiver: IBalance | undefined = balances.find(
      (balance: IBalance) => balance.amount > 0
    )
    if (!giver || !receiver) {
      return []
    }
    const minAmount: number = this.round(
      Math.min(Math.abs(giver.amount), Math.abs(receiver.amount))
    )
    const refund: IRefund = {
      from: giver.user,
      to: receiver.user,
      amount: minAmount
    }
    return [
      refund,
      ...this.getRefund(
        balances.map((balance: IBalance) => {
          const newBalance: IBalance = { ...balance }
          if (newBalance.user === giver.user) {
            newBalance.amount = this.round(newBalance.amount + minAmount)
          } else if (newBalance.user === receiver.user) {
            newBalance.amount = this.round(newBalance.amount - minAmount)
          }
          return newBalance
        })
      )
    ]
  }

  private round(n: number): number {
    return Math.round(n * 100) / 100
  }
}

export default new BalanceService()
