import { calculateDecay } from './decay'
import Decimal from 'decimal.js-light'
import { Transaction } from '@entity/Transaction'
import { Decay } from '@model/Decay'
import { getCustomRepository } from '@dbTools/typeorm'
import { TransactionLinkRepository } from '@repository/TransactionLink'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { decimalSubtraction, decimalAddition } from './utilities'
import { backendLogger as logger } from '@/server/logger'

function isStringBoolean(value: string): boolean {
  const lowerValue = value.toLowerCase()
  if (lowerValue === 'true' || lowerValue === 'false') {
    return true
  }
  return false
}

function isHexPublicKey(publicKey: string): boolean {
  return /^[0-9A-Fa-f]{64}$/i.test(publicKey)
}

async function calculateBalance(
  userId: number,
  amount: Decimal,
  time: Date,
  transactionLink?: dbTransactionLink | null,
): Promise<{ balance: Decimal; decay: Decay; lastTransactionId: number }> {
  // negative or empty amount should not be allowed
  if (amount.lessThanOrEqualTo(0)) {
    logger.error(`Transaction amount must be greater than 0: ${amount}`)
    throw new Error('Transaction amount must be greater than 0')
  }

  // check if user has prior transactions
  const lastTransaction = await Transaction.findOne({ userId }, { order: { balanceDate: 'DESC' } })

  if (!lastTransaction) {
    logger.error(`No prior transaction found for user with id: ${userId}`)
    throw new Error('User has not received any GDD yet')
  }

  const decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, time)

  // new balance is the old balance minus the amount used
  const balance = decimalSubtraction(decay.balance, amount)

  const transactionLinkRepository = getCustomRepository(TransactionLinkRepository)
  const { sumHoldAvailableAmount } = await transactionLinkRepository.summary(userId, time)

  // If we want to redeem a link we need to make sure that the link amount is not considered as blocked
  // else we cannot redeem links which are more or equal to half of what an account actually owns
  const releasedLinkAmount = transactionLink ? transactionLink.holdAvailableAmount : new Decimal(0)

  const availableBalance = decimalSubtraction(balance, sumHoldAvailableAmount)

  if (decimalAddition(availableBalance, releasedLinkAmount).lessThan(0)) {
    logger.error(
      `Not enough funds for a transaction of ${amount} GDD, user with id: ${userId} has only ${balance} GDD available`,
    )
    throw new Error('Not enough funds for transaction')
  }

  logger.debug(`calculated Balance=${balance}`)
  return { balance, lastTransactionId: lastTransaction.id, decay }
}

export { isHexPublicKey, calculateBalance, isStringBoolean }
