# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
require 'rails_helper'
require 'support/payments_for_a_payout'

describe QueryPayments do

  before :each do
    @nonprofit = force_create(:nonprofit, name: "npo1");
    @supporters = [ force_create(:supporter, name: "supporter-0", nonprofit: @nonprofit),
                    force_create(:supporter, name: "supporter-1", nonprofit: @nonprofit)]

    @payments = [force_create(:payment, gross_amount: 1000, fee_total: 99, net_amount: 901, supporter: @supporters[0], nonprofit:@nonprofit),
                 force_create(:payment, gross_amount: 2000, fee_total: 22, net_amount: 1978, supporter: @supporters[1], nonprofit:@nonprofit)]
    @bank_account = force_create(:bank_account, name: 'bank1', nonprofit: @nonprofit)
  end

  describe '.ids_for_payout' do
    before(:each) {
      Timecop.freeze(2020,5,5)
    }
    after(:each) {
      Timecop.return
    }



    describe 'no date provided' do
      include_context 'payments for a payout' do
        let(:np) { @nonprofit }
        let(:date_for_marking) {Time.now}

      end

      it 'np is invalid' do
        expect(QueryPayments.ids_for_payout(686826812658102751098754)).to eq []
      end

      it 'works without a date provided' do
        all_payments

        result = QueryPayments.ids_for_payout(np.id)

        payments_for_payout = Set.new()
        @expect_marked[:charges].each {|c|
          c.reload()
          payments_for_payout.add(c.payment.id)
        }

        @expect_marked[:disputes].each {|d|
          d.reload()
          payments_for_payout.add(d.payment.id)
        }

        @expect_marked[:refunds].each {|r|
          r.reload()
          payments_for_payout.add(r.payment.id)
        }

        expect(result).to match_array(payments_for_payout)
      end
    end

    describe 'with date provided' do
      include_context 'payments for a payout' do
        let(:np) { @nonprofit }
        let(:date_for_marking) {Time.now - 1.day}

      end

      it 'np is invalid' do
        expect(QueryPayments.ids_for_payout(686826812658102751098754)).to eq []
      end

      it 'works with a date provided' do
        all_payments

        result = QueryPayments.ids_for_payout(np.id, {date: Time.now - 1.day})

        payments_for_payout = Set.new()
        @expect_marked[:charges].each {|c|
          c.reload()
          payments_for_payout.add(c.payment.id)
        }

        @expect_marked[:disputes].each {|d|
          d.reload()
          payments_for_payout.add(d.payment.id)
        }

        @expect_marked[:refunds].each {|r|
          r.reload()
          payments_for_payout.add(r.payment.id)
        }

        expect(result).to match_array(payments_for_payout)
      end
    end



  end

  describe '.get_payout_total'do
    include_context 'payments for a payout' do
      let(:np) { @nonprofit }
      let(:date_for_marking) {Time.now}

    end
    it 'gives empty payout result if no payments provided' do
      result = QueryPayments.get_payout_totals([])

      expected = {'gross_amount' => 0, 'fee_total' => 0, 'net_amount' => 0}
      expect(result).to eq expected
    end

    it 'gives correct payout info' do
      all_payments
      result = QueryPayments.get_payout_totals(QueryPayments.ids_for_payout(np.id))
      expected  = {gross_amount: 5500, fee_total: -1200, net_amount: 4300, count: 8}.with_indifferent_access

      expect(result.with_indifferent_access).to eq expected
    end


  end

  describe '.for_payout' do

    before(:each) do
      gross = @payments.map{|h| h['gross_amount']}.sum
      fees = @payments.map{|h| h['fee_total']}.sum
      net = @payments.map{|h| h['net_amount']}.sum
      @payout = force_create(:payout, gross_amount: gross, fee_total: fees, net_amount: net, nonprofit: @nonprofit)
      @payment_payouts = @payments.map {|p| force_create(:payment_payout, payment: p, payout:@payout)}

      @result = QueryPayments.for_payout(@nonprofit['id'], @payout['id'])
    end

    it 'sets the correct headers' do
      expect(@result.first).to eq(["date", "gross_total", "fee_total", "net_total", "bank_name", "status"])
    end

    it 'sets the correct payout data' do
      expect(@result[1].count).to eq(6) # TODO
    end
    
    it 'sets the payment headers', :pending => true do
      expect(@result[3]).to eq(["Date", "Gross Amount", "Fee Total", "Net Amount", "Type", "Payment ID", "Last Name", "First Name", "Full Name", "Organization", "Email", "Phone", "Address", "City", "State", "Postal Code", "Country", "Anonymous?", "Designation", "Honorarium/Memorium", "Comment", "Campaign", "Campaign Gift Level", "Event"])
    end

    it 'sets the correct payment data', :pending => true do
      expect(@result[4].count).to eq 24
    end
  end

  describe '.for_export_enumerable' do
    it 'finishes two payment export' do
      rows = QueryPayments::for_export_enumerable(@nonprofit.id, {}).to_a

      headers = MockHelpers.payment_export_headers

      expect(rows.length).to eq(3)
      expect(rows[0]).to eq(headers)

    end
  end

  describe '.full_search' do

    include_context :shared_rd_donation_value_context
    before(:each) {
      nonprofit.stripe_account_id = Stripe::Account.create()['id']
      nonprofit.save!
      card.stripe_customer_id = 'some other id'
      cust = Stripe::Customer.create()
      card.stripe_customer_id = cust['id']
      card.save!
      expect(Stripe::Charge).to receive(:create).exactly(3).times.and_wrap_original {|m, *args| a = m.call(*args);
      @stripe_charge_id = a['id']
      a
      }

      allow(QueueDonations).to receive(:execute_for_donation)

    }

    let(:charge_amount_small) { 200}
    let(:charge_amount_medium) { 400}
    let(:charge_amount_large) { 600}

    def generate_donation(h)
      token = h[:token]
      date = h[:date]
      amount = h[:amount]

      input = {amount: amount,
               nonprofit_id: nonprofit.id,
               supporter_id: supporter.id,
               token: token,

               date: date,
               dedication: 'dedication',
               designation: 'designation'}
      if h[:event_id]
        input[:event_id] = h[:event_id]
      end

      if h[:campaign_id]
        input[:campaign_id] = h[:campaign_id]
      end

      InsertDonation.with_stripe(input)
    end

    describe 'general donations' do
      let(:donation_result_yesterday) {
          generate_donation(amount: charge_amount_small,

                                     token: source_tokens[0].token,
                                     date: (Time.now - 1.day).to_s)


      }

      let(:donation_result_today) {

          generate_donation(amount:  charge_amount_medium,

                                     token: source_tokens[1].token,

                                     date: (Time.now).to_s
                             )
      }

      let(:donation_result_tomorrow) {

          generate_donation(amount: charge_amount_large,

                                     token: source_tokens[2].token,
                                     date: (Time.now - 1.day).to_s
                            )

      }

      let (:first_refund_of_yesterday) {
        charge =  donation_result_yesterday['charge']

          InsertRefunds.with_stripe(charge.attributes, {amount: 100}.with_indifferent_access)

      }

      let(:second_refund_of_yesterday) {
        charge =  donation_result_yesterday['charge']

        InsertRefunds.with_stripe(charge.attributes, {amount: 50}.with_indifferent_access)

      }


      it 'empty filter returns all' do
        donation_result_yesterday
        donation_result_today
        donation_result_tomorrow
        first_refund_of_yesterday
        second_refund_of_yesterday

        result = QueryPayments::full_search(nonprofit.id, {})

        expect(result[:data].count).to eq 5
      end







    end

    describe 'event donations' do
      let(:donation_result_yesterday) {
        generate_donation(amount: charge_amount_small,
                          event_id: event.id,
                          token: source_tokens[0].token,
                          date: (Time.now - 1.day).to_s)


      }

      let(:donation_result_today) {

        generate_donation(amount:  charge_amount_medium,
                          event_id: event.id,
                          token: source_tokens[1].token,

                          date: (Time.now).to_s
        )
      }

      let(:donation_result_tomorrow) {

        generate_donation(amount: charge_amount_large,

                          token: source_tokens[2].token,
                          date: (Time.now - 1.day).to_s
        )

      }

      let (:first_refund_of_yesterday) {
        charge =  donation_result_yesterday['charge']

        InsertRefunds.with_stripe(charge.attributes, {amount: 100}.with_indifferent_access)

      }

      let(:second_refund_of_yesterday) {
        charge =  donation_result_yesterday['charge']

        InsertRefunds.with_stripe(charge.attributes, {amount: 50}.with_indifferent_access)

      }

      it 'search includes refunds for that event ' do
        donation_result_yesterday
        donation_result_today
        donation_result_tomorrow
        first_refund_of_yesterday
        second_refund_of_yesterday

        result = QueryPayments::full_search(nonprofit.id, {event_id: event.id})

        expect(result[:data].count).to eq 4
        expect(result[:data]).to_not satisfy {|i| i.any?{|j| j['id'] == donation_result_tomorrow['payment']['id']}}
      end
    end

    describe 'campaign donations' do
      let(:donation_result_yesterday) {
        generate_donation(amount: charge_amount_small,
                          campaign_id:campaign.id,
                          token: source_tokens[0].token,
                          date: (Time.now - 1.day).to_s)


      }

      let(:donation_result_today) {

        generate_donation(amount:  charge_amount_medium,
                          campaign_id:campaign.id,
                          token: source_tokens[1].token,

                          date: (Time.now).to_s
        )
      }

      let(:donation_result_tomorrow) {

        generate_donation(amount: charge_amount_large,

                          token: source_tokens[2].token,
                          date: (Time.now - 1.day).to_s
        )

      }

      let (:first_refund_of_yesterday) {
        charge =  donation_result_yesterday['charge']

        InsertRefunds.with_stripe(charge.attributes, {amount: 100}.with_indifferent_access)

      }

      let(:second_refund_of_yesterday) {
        charge =  donation_result_yesterday['charge']

        InsertRefunds.with_stripe(charge.attributes, {amount: 50}.with_indifferent_access)

      }


      it 'search includes refunds for that campaign ' do
        donation_result_yesterday
        donation_result_today
        donation_result_tomorrow
        first_refund_of_yesterday
        second_refund_of_yesterday

        result = QueryPayments::full_search(nonprofit.id, {campaign_id: campaign.id})

        expect(result[:data].count).to eq 4
        expect(result[:data]).to_not satisfy {|i| i.any?{|j| j['id'] == donation_result_tomorrow['payment']['id']}}
      end
    end

  end
end