# TODO: place to the right folder
# TODO: extract to separate model / library / strategy
module Account::Billing
  extend ActiveSupport::Concern

  included do
    has_many :invoices, :foreign_key => 'buyer_account_id'
    after_save :update_invoices_vat_rates
    before_destroy :check_unresolved_invoices
  end

  # MODEL: Buyer
  def is_billed?
    provider_account && provider_account.is_billing_buyers?
  end

  protected

  def update_invoices_vat_rates
    if previously_changed?(:vat_rate) || changes.key?(:vat_rate)
      self.invoices.not_frozen.reorder('').update_all(:vat_rate => self.vat_rate)
    end
  end

  # Will prevent the buyer from destroying if there are unresolved
  # invoices present.
  #
  def check_unresolved_invoices
    if self.invoices.unresolved.present?
      self.errors.add(:invoices, :unresolved_invoices)
      false
    else
      true
    end
  end

  def save_in_payment!
    @in_payment = true
    save!
  ensure
    @in_payment = false
  end

  public

  # This returns true only if account is in the middle of executing a payment (the pay! method,
  # account can be either the payer or the payee).
  #
  # This is used by observers, to differentiate between saving an account and just calling the
  # pay! method.
  def in_payment?
    @in_payment
  end

  # MODEL: Buyer
  # Charge this account the given amount via payment gateway.
  #
  # REFACTOR: move to billing strategy!
  #
  # options has :invoice
  def charge!(amount, options = {})
    # should not be even called for zeros...
    if amount.zero?
      logger.info("Buyer #{self.id} was not charged (amount == 0)")
      return nil
    end
    unless paying_monthly?
      logger.info("Buyer #{self.id} was not charged (not paying monthly)")
      return nil
    end

    unless credit_card_stored?
      logger.info("Buyer #{self.id} was not charged: credit card missing")
      raise Finance::Payment::CreditCardMissing
    end

    gateway_options= options.delete(:gateway_options) || {}

    fix_gateway_options(gateway_options)

    transaction = payment_transactions.build(options.reverse_merge(:action => :purchase,
                                                                   :amount => amount))

    # add order_id
    if options[:invoice].is_a?(Invoice) && gateway_options.include?(:order_id)== false
      gateway_options[:order_id]= options[:invoice].id
    end

    logger.info("Processing transaction for buyer #{self.id} with code #{credit_card_auth_code}, gateway #{provider_payment_gateway} & options #{gateway_options}")

    transaction.process!(credit_card_auth_code, provider_payment_gateway, gateway_options)
  end


  # MODEL: Buyer
  # TODO: Remove?
  #
  def current_invoice
    invoices.opened.first
  end


  # XXX: MeS cannot store cvv2 data nor the expiration date
  # XXX: MeS requires moto_ecommerce_ind flag to 2 for recurring
  # payments
  #
  # TODO: what about putting this stuff into the gateway directly? This breaks the
  #       abstraction.
  #
  def fix_gateway_options(gateway_options)
    if provider_payment_gateway.is_a?(ActiveMerchant::Billing::MerchantESolutionsGateway)
      gateway_options.merge!({
                               :card_exp_date => credit_card_expires_on.strftime("%m%y"),
                               :moto_ecommerce_ind => 2,
                               :billing_address => billing_address.data
                             })
    end
  end
end
