# frozen_string_literal: true

# 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 || {}).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, '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