# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
module InsertRecurringDonation

  # Create a recurring_donation, donation, payment, charge, and activity
  # See controllers/nonprofits/recurring_donations_controller#create for the data params to pass in
  def self.with_stripe(data)
    data = data.with_indifferent_access

    ParamValidation.new(data, InsertDonation.common_param_validations
                                  .merge(token: {required: true, format: UUID::Regex}))

    unless data[:recurring_donation].nil?

      ParamValidation.new(data[:recurring_donation], {
          interval: {is_integer: true},
          start_date: {can_be_date: true},
          time_unit: {included_in: %w(month day week year)},
          paydate: {is_integer:true}
      })
      if (data[:recurring_donation][:paydate])
        data[:recurring_donation][:paydate] = data[:recurring_donation][:paydate].to_i
      end

      ParamValidation.new(data[:recurring_donation], {
          paydate: {min:1, max:28}
      })

    else
      data[:recurring_donation] = {}
    end

    source_token = QuerySourceToken.get_and_increment_source_token(data[:token], nil)
    tokenizable = source_token.tokenizable
    QuerySourceToken.validate_source_token_type(source_token)

    entities =  RetrieveActiveRecordItems.retrieve_from_keys(data, {Supporter => :supporter_id, Nonprofit => :nonprofit_id})

    entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, {Campaign => :campaign_id, Event => :event_id, Profile => :profile_id}, true))

    InsertDonation.validate_entities(entities)

    ## does the card belong to the supporter?
    if tokenizable.holder != entities[:supporter_id]
      raise ParamValidation::ValidationError.new("Supporter #{entities[:supporter_id].id} does not own card #{tokenizable.id}", key: :token)
    end

    data['card_id'] = tokenizable.id

    result = {}
    data[:date] = Time.now
    data = data.merge(payment_provider: payment_provider(data))
    data = data.except(:old_donation).except('old_donation')
    # if start date is today, make initial charge first
    test_start_date = get_test_start_date(data)
    if test_start_date == nil || Time.current >= test_start_date
      result = result.merge(InsertDonation.insert_charge(data))
      if result['charge']['status'] == 'failed'
        raise ChargeError.new(result['charge']['failure_message'])
      end
    end

    # Create the donation record
    result['donation'] = InsertDonation.insert_donation(data, entities)
    entities[:donation_id] = result['donation']
    # Create the recurring_donation record
    result['recurring_donation'] = insert_recurring_donation(data,entities)
    # Update charge foreign keys
    if result['payment']
      InsertDonation.update_donation_keys(result)
      # Create the activity record
      result['activity'] = InsertActivities.for_recurring_donations([result['payment'].id])
    end
    # Send receipts
    EmailJobQueue.queue(JobTypes::NonprofitPaymentNotificationJob, result['donation'].id)
    EmailJobQueue.queue(JobTypes::DonorPaymentNotificationJob, result['donation'].id, entities[:supporter_id].locale)
    QueueDonations.delay.execute_for_donation(result['donation']['id'])
    return result
  end

def self.with_sepa(data)
    data = set_defaults(data)
    data = data.merge(payment_provider: payment_provider(data))
    result = {}

    if Time.current >= data[:recurring_donation][:start_date]
      result = result.merge(InsertDonation.insert_charge(data))
    end

    result['donation'] = Psql.execute(Qexpr.new.insert(:donations, [
      data.except(:recurring_donation)
    ]).returning('*')).first

    result['recurring_donation'] = Psql.execute(Qexpr.new.insert(:recurring_donations, [
      data[:recurring_donation].merge(donation_id: result['donation']['id'])
    ]).returning('*')).first

    if result['payment']
      InsertDonation.update_donation_keys(result)
    end

    DonationMailer.delay.nonprofit_payment_notification(result['donation']['id'])
    DonationMailer.delay.donor_direct_debit_notification(result['donation']['id'], locale_for_supporter(result['donation']['supporter_id']))

    QueueDonations.delay.execute_for_donation(result['donation']['id'])

    { status: 200, json: result }
  end


 # the data model here is brutal. This needs to get cleaned up.
  def self.convert_donation_to_recurring_donation(donation_id)
    ParamValidation.new({donation_id: donation_id}, {donation_id: {:required => true, :is_integer => true}})
    don = Donation.where('id = ? ', donation_id).first
    if !don
      raise ParamValidation::ValidationError.new("#{donation_id} is not a valid donation", {:key => :donation_id, :val => donation_id})
    end
    rd = insert_recurring_donation({amount:don.amount, email: don.supporter.email, anonymous: don.anonymous, origin_url: don.origin_url, recurring_donation: { start_date: don.created_at, :paydate => convert_date_to_valid_paydate(don.created_at)}, date: don.created_at}, {supporter_id: don.supporter, nonprofit_id: don.nonprofit, donation_id: don})
    don.recurring_donation = rd
    don.recurring = true

    don.payment.kind = "RecurringDonation"
    don.payment.save!
    rd.save!
    don.save!

    rd
  end

 def self.insert_recurring_donation(data, entities)
    rd = RecurringDonation.new
    rd.amount = data[:amount]
    rd.nonprofit = entities[:nonprofit_id]
    rd.donation = entities[:donation_id]
    rd.supporter_id = entities[:supporter_id].id
    rd.active = true
    rd.edit_token = SecureRandom.uuid
    rd.n_failures= 0
    rd.email= entities[:supporter_id].email
    rd.interval = data[:recurring_donation][:interval].blank? ?  1 : data[:recurring_donation][:interval]
    rd.time_unit= data[:recurring_donation][:time_unit].blank? ? 'month' : data[:recurring_donation][:time_unit]
    if data[:recurring_donation][:start_date].blank?
      rd.start_date= Time.current.beginning_of_day
    elsif data[:recurring_donation][:start_date].is_a? Time
      rd.start_date = data[:recurring_donation][:start_date]
    else
      rd.start_date = Chronic.parse(data[:recurring_donation][:start_date])
    end

    if rd.time_unit == 'month' && rd.interval == 1 && data[:recurring_donation][:paydate].nil?
      rd.paydate = convert_date_to_valid_paydate(rd.start_date)
    else
      rd.paydate = data[:recurring_donation][:paydate]
    end

    rd.save!
    rd
  end
def self.get_test_start_date(data)
    unless data[:recurring_donation] && data[:recurring_donation][:start_date]
      return nil
    end

    return Chronic.parse(data[:recurring_donation][:start_date])


  end


  def self.locale_for_supporter(supporter_id)
    Psql.execute(
      Qexpr.new.select(:locale).from(:supporters)
        .where("id=$id", id: supporter_id)
    ).first['locale']
  end

  def self.payment_provider(data)
    if data[:card_id]
      :credit_card
    elsif data[:direct_debit_detail_id]
      :sepa
    end
  end

  def self.convert_date_to_valid_paydate(date)
    day = date.day
    return day > 28 ? 28 : day
  end
end