# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
# Create a new payout

require 'psql'
require 'qexpr'
require 'query/query_payments'
require 'update/update_charges'
require 'update/update_refunds'
require 'update/update_disputes'
require 'param_validation'

module InsertPayout

  # Pass in the following inside the data hash:
  # - stripe_account_id
  # - email
  # - user_ip
  # - bank_name
  # options hash can have a :date (before date) for only paying out funds before a certain date (useful for only disbursing the prev month)
  def self.with_stripe(np_id, data, options={})
    bigger_data = (data ? data : {}).merge(np_id: np_id)
    ParamValidation.new(bigger_data, {
      np_id: {required: true, is_integer: true},
      stripe_account_id: {not_blank: true, required: true},
      email: {not_blank: true, required: true},
      user_ip: {not_blank: true, required: true},
      bank_name: {not_blank: true, required: true}
    })
    options ||= {}
    entities = RetrieveActiveRecordItems.retrieve_from_keys(bigger_data, Nonprofit => :np_id)
    payment_ids = QueryPayments.ids_for_payout(np_id, options)
    if payment_ids.count < 1
      raise ArgumentError.new("No payments are available for disbursal on this account.")
    end
    totals = QueryPayments.get_payout_totals(payment_ids)
    nonprofit_currency = entities[:np_id].currency
    now = Time.current
    begin
      stripe_transfer = StripeUtils.create_transfer(totals['net_amount'], data[:stripe_account_id], nonprofit_currency)
    Psql.transaction do
      # Create the Transfer on Stripe

      # Retrieve all payments with available charges and undisbursed refunds
      # Mark all the above payments as disbursed
      UpdateCharges.disburse_all_with_payments(payment_ids)
      # Mark all the above refunds as disbursed
      UpdateRefunds.disburse_all_with_payments(payment_ids)
      # Mark all disputes as lost_and_paid
      UpdateDisputes.disburse_all_with_payments(payment_ids)
      # Get gross total, total fees, net total, and total count
      # Create the payout record (whether it succeeded on Stripe or not)
      payout = Psql.execute(
        Qexpr.new.insert(:payouts, [{
          net_amount: totals['net_amount'],
          nonprofit_id: np_id,
            failure_message: stripe_transfer['failure_message'],
          status: stripe_transfer.status,
          fee_total: totals['fee_total'],
          gross_amount: totals['gross_amount'],
          email: data[:email],
          count: totals['count'],
          stripe_transfer_id: stripe_transfer.id,
          user_ip: data[:user_ip],
          ach_fee: 0,
          bank_name: data[:bank_name]}])
        .returning('id', 'net_amount', 'nonprofit_id', 'created_at', 'updated_at', 'status', 'fee_total', 'gross_amount', 'email', 'count', 'stripe_transfer_id', 'user_ip', 'ach_fee', 'bank_name')
      ).first
      # Create PaymentPayout records linking all the payments to the payout
      pps = Psql.execute(Qexpr.new.insert('payment_payouts', payment_ids.map{|id| {payment_id: id.to_i}}, {common_data: {payout_id: payout['id'].to_i}}))
      NonprofitMailer.delay.pending_payout_notification(payout['id'].to_i)
        return payout
      end
    rescue Stripe::StripeError => e
      payout = Psql.execute(
          Qexpr.new.insert(:payouts, [{
                                          net_amount: totals['net_amount'],
                                          nonprofit_id: np_id,
                                          failure_message: e.message,
                                          status: 'failed',
                                          fee_total: totals['fee_total'],
                                          gross_amount: totals['gross_amount'],
                                          email: data[:email],
                                          count: totals['count'],
                                          stripe_transfer_id: nil,
                                          user_ip: data[:user_ip],
                                          ach_fee: 0,
                                          bank_name: data[:bank_name]}])
              .returning('id', 'net_amount', 'nonprofit_id', 'created_at', 'updated_at', 'status', 'fee_total', 'gross_amount', 'email', 'count', 'stripe_transfer_id', 'user_ip', 'ach_fee', 'bank_name')
      ).first
      return payout
    end
  end
end