require 'spec_helper'

describe EmailBatchScheduledSessions do

  after(:each) do
    Timecop.return
  end

  before(:each) do
    UserMailer.deliveries.clear
  end

  def find_user_sessions(obj, user)
    found = obj.find{|item| item[0] == user}
    found.should_not be_nil
    found[1]
  end

  describe 'daily scheduled' do
    # before { pending }

    let (:scheduled_batch) { FactoryGirl.create(:email_batch_scheduled_session) }

    let (:drums) { Instrument.find('drums') }
    let (:guitar) { Instrument.find('acoustic guitar') }
    let (:bass) { Instrument.find('bass guitar') }
    let (:vocals) { Instrument.find('voice') }
    let (:electric_guitar) { Instrument.find('electric guitar')}

    let (:drummer) { FactoryGirl.create(:user, first_name: 'drummer', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [drums, guitar]) }
    let (:guitarist) { FactoryGirl.create(:user, first_name: 'guitarist', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [guitar, drums]) }
    let (:bassist) { FactoryGirl.create(:user, first_name: 'bassist', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [bass]) }
    let (:vocalist) { FactoryGirl.create(:user, first_name: 'vocalist', :last_jam_locidispid => 1, :last_jam_addr => 1, specific_instruments: [vocals, electric_guitar]) }
    let (:loser) { FactoryGirl.create(:user, first_name: 'loser', :last_jam_locidispid => 2, :last_jam_addr => 2, specific_instruments: [vocals, drums]) }

    let (:session3_creator) {FactoryGirl.create(:user, first_name: 'session3_creator', :last_jam_locidispid => 3, :last_jam_addr => 3, specific_instruments: [vocals]) }
    let (:session4_creator) {FactoryGirl.create(:user, first_name: 'session4_creator', :last_jam_locidispid => 4, :last_jam_addr => 4, specific_instruments: [vocals]) }

    let (:session1) do 
      FactoryGirl.create(:music_session, 
                         :creator => drummer, 
                         :scheduled_start => Time.now() + 2.days,
                         :open_rsvps=> true,
                         :is_unstructured_rsvp => false,
                         :created_at => Time.now - 1.hour)
    end
    let (:session2) do 
      FactoryGirl.create(:music_session, 
                         :creator => drummer,
                         :open_rsvps => false,
                         :is_unstructured_rsvp => false,
                         :scheduled_start => Time.now() + 2.days,
                         :created_at => Time.now - 1.hour)
    end
    let (:session3) do
      FactoryGirl.create(:music_session,
                         :creator => session3_creator,
                         :open_rsvps => true,
                         :is_unstructured_rsvp => true,
                         :scheduled_start => Time.now() + 2.days,
                         :created_at => Time.now - 1.hour)
    end
    let (:session4) do
      FactoryGirl.create(:music_session,
                         :creator => session4_creator,
                         :open_rsvps => true,
                         :is_unstructured_rsvp => true,
                         :scheduled_start => Time.now() + 2.days,
                         :created_at => Time.now - 1.hour)
    end

    before(:each) do
      MusicianInstrument.delete_all
      RsvpSlot.delete_all
      JamRuby::Score.delete_all
      JamRuby::Score.connection.execute('delete from current_network_scores').check
      scheduled_batch.reset!

      @drums_rsvp_slot = FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session1)
      @guitar_rsvp_slot = FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session1)
      @bass_rsvp_slot = FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session1)
      FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session2)
      FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session2)
      FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session2)
      # oo = FactoryGirl.create(:rsvp_slot, :instrument => vocals, :music_session => session1)
      # oo.rsvp_request_slot.update_attributes(chosen: true)
      # oo = FactoryGirl.create(:rsvp_request, :user => vocalist, :rsvp_slot => oo)
      # oo.rsvp_request_slot.update_attributes(chosen: true)

      expect(drummer.instruments.include?(drums)).to eq(true)
      expect(drummer.instruments.include?(guitar)).to eq(true)

      loser.touch
    end

    describe "everyone but loser has good enough scores" do

      before(:each) do
        JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10)
        JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, (APP_CONFIG.max_yellow_full_score * 2) + 1)
        @matching_instruments_users = [guitarist, bassist]
        @good_score_users = [guitarist, bassist, vocalist] # not adding the drummer because he's the creator (he does have a good score to these others though)
      end

      describe "session1 is_unstructured" do
        before(:each) do
          session1.is_unstructured_rsvp = true
          session1.save!
        end

        it "no duplicate user matches" do

          def ensure_no_dups(obj)
            found_sessions = obj.map{ |user_and_sessions| user_and_sessions[1] }
            found_sessions.uniq.length.should == found_sessions.length
          end

          Score.connection.execute('delete from scores').check
          1.times  { JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, 10) }
          2.times  { JamRuby::Score.createx(2, 'a', 2, 1, 'a', 1, 20) }
          3.times  { JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, 15) }
          4.times  { JamRuby::Score.createx(2, 'a', 2, 1, 'a', 1, 35) }

          obj = scheduled_batch.fetch_recipients

          ensure_no_dups(obj)
          obj.length.should == 1
          obj[0][1].length.should == 1

          found_users = obj.map{ |user_and_sessions| user_and_sessions[0] }
          found_users.should == [loser]
        end

        it 'finds anyone with good enough scores' do
          obj = scheduled_batch.fetch_recipients
          found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
          @good_score_users.should =~ found_users

          scheduled_batch.deliver_batch
          expect(UserMailer.deliveries.length).to eq(@good_score_users.length)
        end
      end

      it 'finds anyone with good enough scores and matching instruments' do
        obj = scheduled_batch.fetch_recipients

        #Database.dump("SELECT * FROM tmp_candidate_recipients") do |field, value|
        #  if field == 'receiver_id'
        #    User.find(value).first_name
        #  else
        #    value.to_s
        #  end
        #end

        found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
        @matching_instruments_users.should =~ found_users

        scheduled_batch.deliver_batch
        expect(UserMailer.deliveries.length).to eq(@matching_instruments_users.length)
      end

      describe "closes a RSVP slot in session1" do
        before(:each) do
          request = FactoryGirl.create(:rsvp_request, user: loser)
          FactoryGirl.create(:rsvp_request_rsvp_slot, chosen: true, rsvp_request: request, rsvp_slot: @bass_rsvp_slot)
        end

        it "finds one less" do
          # the bass slot is closed; so we should not longer find the bassist in the results
          @matching_instruments_users.delete(bassist).should_not be_nil

          obj = scheduled_batch.fetch_recipients
          found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
          @matching_instruments_users.should =~ found_users

          scheduled_batch.deliver_batch
          expect(UserMailer.deliveries.length).to eq(@matching_instruments_users.length)
        end
      end

      describe "2 more open sessions" do
        before(:each) do
          JamRuby::Score.createx(1, 'a', 1, session3_creator.last_jam_locidispid, 'a', 1, 5)
          JamRuby::Score.createx(1, 'a', 1, session4_creator.last_jam_locidispid, 'a', 2, 20)
          session3.touch
          session4.touch
        end

        it "sessions are sorted by latency" do
            obj = scheduled_batch.fetch_recipients
            obj.length.should == @matching_instruments_users.length + [drummer, vocalist].length

            # verify that all 4 users have the right sessions, in the right order
            @matching_instruments_users.each do |user|
              user_sessions = find_user_sessions(obj, user)
              user_sessions.should == [session3, session1, session4]
            end

            user_sessions = find_user_sessions(obj, drummer)
            user_sessions.should == [session3, session4]

            user_sessions = find_user_sessions(obj, vocalist)
            user_sessions.should == [session3, session4]
        end
      end

      describe "21 more sessions" do
        before(:each) do
          20.times do |i|
            creator = FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1)
            FactoryGirl.create(:music_session,
                                          :creator => creator,
                                          :open_rsvps => true,
                                          :is_unstructured_rsvp => true,
                                          :scheduled_start => Time.now() + 2.days,
                                          :created_at => Time.now - 1.hour)
          end
        end

        it "prevents more than 20 sessions per user" do
          obj = scheduled_batch.fetch_recipients
          obj.length.should == 20 + 4 # 4 is vocalist, drummer, guitarist, and bassist.  20 is the users made in the before block


          # the bassist should *potentially* the drummer's session, and these new 20 sessions. But we limit to 20.
          user_sessions = find_user_sessions(obj, bassist)
          user_sessions.length.should == 20
        end
      end

      it 'finds anyone with good enough scores and matching instruments' do
        obj = scheduled_batch.fetch_recipients

        #Database.dump("SELECT * FROM tmp_candidate_recipients") do |field, value|
        #  if field == 'receiver_id'
        #    User.find(value).first_name
        #  else
        #    value.to_s
        #  end
        #end

        found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
        @matching_instruments_users.should =~ found_users

        scheduled_batch.deliver_batch
        expect(UserMailer.deliveries.length).to eq(@matching_instruments_users.length)
      end

      describe "open_rsvp=false" do
        before(:each) do
          session1.open_rsvps = false
          session1.save!
          session2.open_rsvps = false
          session2.save!
        end

        it "won't find an open_rsvps=false session without an invitation" do
          obj = scheduled_batch.fetch_recipients
          expect(obj.count).to eq(0)
        end

        describe "with invitations" do
          let(:invitation) {friend(drummer, guitarist); FactoryGirl.create(:invitation, sender: drummer, receiver:guitarist, music_session: session1)}

          before(:each) do
            invitation.touch
          end

          it "finds user with the invite" do
            obj = scheduled_batch.fetch_recipients
            found_users = obj.map{ |user_and_sessions| user_and_sessions[0]}
            [guitarist].should =~ found_users

            scheduled_batch.deliver_batch
            expect(UserMailer.deliveries.length).to eq(1)
          end
        end
      end

    end



    it 'handles large batches' do
      creators = []
      8.downto(1) do |nn|
        creators << uu = FactoryGirl.create(:user, :last_jam_locidispid => 5, :last_jam_addr => 5)
        msess = FactoryGirl.create(:music_session, 
                                   :creator => uu, 
                                   :scheduled_start => Time.now() + 2.days,
                                   :musician_access => true,
                                   :approval_required => false,
                                   :created_at => Time.now - 1.hour)
        FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => msess)
        FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => msess)
        FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => msess)
      end
      instruments = [drums, guitar, bass]
      4.downto(1) do |nn|
        uu = FactoryGirl.create(:user, :last_jam_locidispid => 6, :last_jam_addr => 6) 
        uu.musician_instruments << FactoryGirl.build(:musician_instrument, 
                                                     player: uu, 
                                                     instrument: instruments.sample, 
                                                     proficiency_level: 2)      
      end
      JamRuby::Score.createx(5, 'a', 5, 6, 'a', 6, 10)
      JamRuby::Score.createx(5, 'a', 5, 7, 'a', 7, Score::MAX_YELLOW_LATENCY + 1)
      FactoryGirl.create(:user, :last_jam_locidispid => 8, :last_jam_addr => 8) 
      FactoryGirl.create(:user, :last_jam_locidispid => 7, :last_jam_addr => 7) 

      receivers = []
      scheduled_batch.fetch_recipients(3) do |receiver, sessions|
        receivers << receiver
        expect(sessions.count).to eq(8)
      end
      expect(receivers.count).to eq(4)
    end

  end
end
