# frozen_string_literal: true

# Provides methods to manage wallets
class WalletService
  def initialize(user: nil, wallet: nil)
    @user = user
    @wallet = wallet
  end

  ## credit an amount to wallet, if credit success then return a wallet transaction and notify to admin
  def credit(amount)
    transaction = nil
    ActiveRecord::Base.transaction do
      if @wallet&.credit(amount)
        transaction = WalletTransaction.new(
          invoicing_profile: @user&.invoicing_profile,
          wallet: @wallet,
          transaction_type: 'credit',
          amount: amount
        )
        raise ActiveRecord::Rollback unless transaction.save

        NotificationCenter.call type: 'notify_user_wallet_is_credited',
                                receiver: @wallet&.user,
                                attached_object: transaction
        NotificationCenter.call type: 'notify_admin_user_wallet_is_credited',
                                receiver: User.admins_and_managers,
                                attached_object: transaction
      end
    end
    transaction
  end

  ## debit an amount to wallet, if debit success then return a wallet transaction
  def debit(amount)
    transaction = nil
    ActiveRecord::Base.transaction do
      if @wallet&.debit(amount)
        transaction = WalletTransaction.new(
          invoicing_profile: @user&.invoicing_profile,
          wallet: @wallet,
          transaction_type: 'debit',
          amount: amount
        )

        raise ActiveRecord::Rollback unless transaction.save
      end
    end
    transaction
  end

  ## create a refund invoice associated with the given wallet transaction
  def create_avoir(wallet_transaction, description)
    avoir = Avoir.new
    avoir.type = 'Avoir'
    avoir.avoir_date = Time.current
    avoir.description = description
    avoir.payment_method = 'wallet'
    avoir.subscription_to_expire = false
    avoir.invoicing_profile_id = wallet_transaction.wallet.user.invoicing_profile.id
    avoir.statistic_profile_id = wallet_transaction.wallet.user.statistic_profile.id
    avoir.total = wallet_transaction.amount * 100.0

    ii = InvoiceItem.new
    ii.amount = wallet_transaction.amount * 100.0
    ii.description = I18n.t('invoices.wallet_credit')
    ii.object = wallet_transaction
    ii.main = true

    avoir.invoice_items.push(ii)
    avoir.save!
  end

  ##
  # Compute the amount decreased from the user's wallet, if applicable
  # @param payment {Invoice|PaymentSchedule|Order}
  # @param user {User} the customer
  ##
  def self.wallet_amount_debit(payment, user)
    total = if payment.is_a? PaymentSchedule
              payment.payment_schedule_items.first.amount
            else
              payment.total
            end
    total = CouponService.new.apply(total, payment.coupon, user.id) if payment.coupon

    wallet_amount = (user.wallet.amount * 100).to_i

    wallet_amount >= total ? total : wallet_amount
  end

  ##
  # Subtract the amount of the payment document (Invoice|PaymentSchedule|Order) from the customer's wallet
  # @param transaction, if false: the wallet is not debited, the transaction is only simulated on the payment document
  ##
  def self.debit_user_wallet(payment, user, transaction: true)
    wallet_amount = WalletService.wallet_amount_debit(payment, user)
    return unless wallet_amount.present? && wallet_amount != 0

    amount = wallet_amount / 100.0
    if transaction
      wallet_transaction = WalletService.new(user: user, wallet: user.wallet).debit(amount)
      # wallet debit success
      raise DebitWalletError unless wallet_transaction

      payment.set_wallet_transaction(wallet_amount, wallet_transaction.id)
    else
      payment.set_wallet_transaction(wallet_amount, nil)
    end
  end
end
