require 'spec_helper'

describe RsvpRequest do

  before(:each) do
    SessionInfoComment.delete_all
    Notification.delete_all
    RsvpRequestRsvpSlot.delete_all
    RsvpRequest.delete_all
    RsvpSlot.delete_all
    Invitation.delete_all
    MusicSession.delete_all
    User.delete_all

    @session_invitee = FactoryGirl.create(:user)
    @session_invitee.save

    @non_session_invitee = FactoryGirl.create(:user)
    @non_session_invitee.save

    @session_creator = FactoryGirl.create(:user)
    @session_creator.save

    # session invitations require sender and receiver to be friends
    FactoryGirl.create(:friendship, :user => @session_invitee, :friend => @session_creator)
    FactoryGirl.create(:friendship, :user => @session_creator, :friend => @session_invitee)

    @music_session = FactoryGirl.build(:music_session, :creator => @session_creator)
    @music_session.save

    @slot1 = FactoryGirl.build(:rsvp_slot, :music_session => @music_session, :instrument => JamRuby::Instrument.find('electric guitar'))
    @slot1.save!

    @slot2 = FactoryGirl.build(:rsvp_slot, :music_session => @music_session, :instrument => JamRuby::Instrument.find('drums'))
    @slot2.save!

    @invitation = FactoryGirl.build(:invitation, :sender => @session_creator, :receiver => @session_invitee, :music_session => @music_session)
    @invitation.save!
  end

  describe "create" do
    it "should require a valid music session" do
      expect {RsvpRequest.create({:session_id => "1234", :rsvp_slots => [@slot1.id, @slot2.id]}, @session_invitee)}.to raise_error(JamRuby::StateError)
    end

    it "should require at least 1 slot" do
      expect {RsvpRequest.create({:session_id => @music_session.id}, @session_invitee)}.to raise_error(JamRuby::StateError)
    end

    it "should not allow user to RSVP for slot he has already RSVPed to" do
      # allow open RSVPs
      @music_session.open_rsvps = true
      @music_session.save

      RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)
      expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to raise_error(JamRuby::StateError)
    end

    it "should allow invitee to RSVP to session with closed RSVPs" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "We be jammin!"}, @session_invitee)

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "We be jammin!"

      # verify 2 notifications were created for this music session (1 for RSVP and 1 for comment)
      notifications = Notification.where(:session_id => @music_session.id)
      notifications.count.should == 2
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_COMMENT).count.should == 1
    end

    it "should allow non-invitee to RSVP to session with open RSVPs" do
      # allow open RSVPs
      @music_session.open_rsvps = true
      @music_session.save

      expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to_not raise_error

      # verify notification was created
      n = Notification.find_by_source_user_id(@non_session_invitee.id)
      n.description.should == NotificationTypes::SCHEDULED_SESSION_RSVP
    end

    it "should not allow user to RSVP to slot that has already been accepted" do
      # allow open RSVPs
      @music_session.open_rsvps = true
      @music_session.save

      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)

      # accept 1 of the slots
      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)
      rs1.chosen.should == nil
      rs2.chosen.should == nil

      RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => false}]}, @session_creator)

      # attempt to create a request for the already accepted slot
      expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [rs1.id]}, @non_session_invitee)}.to raise_error(JamRuby::StateError)
    end

    it "should not allow non-invitee to RSVP to session with closed RSVPs" do
      @music_session.open_rsvps = false
      @music_session.save!
      expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to raise_error(JamRuby::JamPermissionError)
    end

    it "should allow RSVP creation with autoapprove option for open RSVP sessions" do
      # allow open RSVPs
      @music_session.open_rsvps = true
      @music_session.save

      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :autoapprove => true}, @session_creator)

      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)

      rs1.chosen.should == true
      rs2.chosen.should == true
    end

    it "should allow RSVP creation with autoapprove option for closed RSVP sessions" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :autoapprove => true}, @session_creator)

      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)

      rs1.chosen.should == true
      rs2.chosen.should == true
    end

    it "should allow RSVP to unstructured music session" do
      @music_session.is_unstructured_rsvp = true
      @music_session.save!

      rsvp_request = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => ['unstructured']}, @non_session_invitee)
      rsvp_request.rsvp_slots.length.should == 1
      rsvp_request.rsvp_slots[0].is_unstructured_rsvp.should be_true
      rsvp_request.rsvp_slots[0].proficiency_level.should be_nil
      rsvp_request.rsvp_slots[0].instrument.should be_nil
    end
  end

  describe "index" do
    it "should allow retrieval of RSVPs by session" do
      # allow open RSVPs
      @music_session.open_rsvps = true
      @music_session.save

      user2 = FactoryGirl.create(:user)

      RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @non_session_invitee)
      RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id], :message => "Let's Jam 2!"}, user2)

      rsvps = RsvpRequest.index(@music_session)
      rsvps.count.should == 2
    end

    it "should allow retrieval of RSVPs by session and user" do
      # allow open RSVPs
      @music_session.open_rsvps = true
      @music_session.save

      user2 = FactoryGirl.create(:user)

      RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @non_session_invitee)
      RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id], :message => "Let's Jam 2!"}, user2)

      rsvps = RsvpRequest.index(@music_session, @non_session_invitee)
      rsvps.count.should == 1
    end

    it "should allow retrieval of RSVps by session and approval status" do
      # allow open RSVPs
      @music_session.open_rsvps = true
      @music_session.save

      approved_rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)

      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_request_id_and_rsvp_slot_id(approved_rsvp.id, @slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_request_id_and_rsvp_slot_id(approved_rsvp.id, @slot2.id)
      RsvpRequest.update({:id => approved_rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => false}]}, @session_creator)

      user2 = FactoryGirl.create(:user)
      declined_rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot2.id]}, user2)

      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_request_id_and_rsvp_slot_id(declined_rsvp, @slot2.id)
      RsvpRequest.update({:id => declined_rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs2.id, :accept => false}]}, @session_creator)

      user3 = FactoryGirl.create(:user)
      pending_rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot2.id]}, user3)

      rsvps = RsvpRequest.index(@music_session, nil, {:status => "pending"})
      rsvps.count.should == 1

      rsvps = RsvpRequest.index(@music_session, user3, {:status => "pending"})
      rsvps.count.should == 1

      rsvps = RsvpRequest.index(@music_session, nil, {:status => "approved"})
      rsvps.count.should == 1

      rsvps = RsvpRequest.index(@music_session, @non_session_invitee, {:status => "approved"})
      rsvps.count.should == 1

      rsvps = RsvpRequest.index(@music_session)
      rsvps.count.should == 3
    end
  end

  describe "update" do
    it "should only allow session organizer to approve request" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee)

      # attempt to approve with non-organizer
      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)
      expect {RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => true}]}, @session_invitee)}.to raise_error(JamPermissionError)

      # approve with organizer
      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)
      expect {RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => true}]}, @session_creator)}.to_not raise_error

      # verify notification was created
      n = Notification.find_by_source_user_id(@session_creator.id)
      n.description.should == NotificationTypes::SCHEDULED_SESSION_RSVP_APPROVED
    end

    it "should allow approval of multiple RSVPs to same slot ID when multiple slots exist for same instrument and proficiency level" do
      @music_session.open_rsvps = true
      @music_session.save!

      @slot2.delete

      slot2 = FactoryGirl.build(:rsvp_slot, :music_session => @music_session, :instrument => JamRuby::Instrument.find('electric guitar'))
      slot2.save!

      rsvp1 = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id], :message => "Let's Jam!"}, @session_invitee)
      rsvp2 = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id], :message => "Let's Jam!"}, @non_session_invitee)

      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_request_id(rsvp1.id)
      rs1.rsvp_slot_id.should == @slot1.id

      # approve RSVP 1
      RsvpRequest.update({:id => rsvp1.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}]}, @session_creator)

      # ensure slot ID didn't change
      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_request_id(rsvp1.id)
      rs1.rsvp_slot_id.should == @slot1.id

      # check before approving
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_request_id(rsvp2.id)
      rs2.rsvp_slot_id.should == @slot1.id

      # approve RSVP 2 (same slot ID, but slot2 is same instrument + proficiency level)
      RsvpRequest.update({:id => rsvp2.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs2.id, :accept => true}]}, @session_creator)

      # should have linked the RsvpRequestRsvpSlot record to the second slot
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_request_id(rsvp2.id)
      rs2.rsvp_slot_id.should == slot2.id

      slots = RsvpSlot.where("music_session_id = ?", @music_session.id)
      slots.count.should == 2

      request_slots = RsvpRequestRsvpSlot.where("chosen=true")
      request_slots.count.should == 2
    end

    it "should create a new slot for an RSVP for a slot that has already been approved" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee)

      # approve
      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)
      expect {RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => true}]}, @session_creator)}.to_not raise_error

      # approve again
      rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id)
      rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id)
      RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => true}]}, @session_creator)

      slots = RsvpSlot.where("music_session_id = ?", @music_session.id)
      slots.count.should == 4
    end
  end


  describe "cancel" do
    it "should allow session organizer to cancel an RSVP to a single session" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee)

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "Let's Jam!"

      # cancel
      expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "yes", :message => "Sorry, you're booted for this session"}, @session_creator)}.to_not raise_error

      rsvp = RsvpRequest.find_by_id(rsvp.id)
      rsvp.canceled.should == true
      rsvp.cancel_all.should == false

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_creator)
      comment.comment.should == "Sorry, you're booted for this session"

      # verify 4 notifications were created for this music session (1 for RSVP, 1 for cancellation, and 2 for comments)
      notifications = Notification.where(:session_id => @music_session.id)
      notifications.count.should == 4
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED_ORG).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_COMMENT).count.should == 2
    end

    it "should allow session organizer to cancel an RSVP for all future sessions" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee)

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "Let's Jam!"

      # cancel
      expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "all", :message => "Sorry, you're booted for this session"}, @session_creator)}.to_not raise_error

      rsvp = RsvpRequest.find_by_id(rsvp.id)
      rsvp.canceled.should == true
      rsvp.cancel_all.should == true

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_creator)
      comment.comment.should == "Sorry, you're booted for this session"

      # verify 4 notifications were created for this music session (1 for RSVP, 1 for cancellation, and 2 for comments)
      notifications = Notification.where(:session_id => @music_session.id)
      notifications.count.should == 4
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED_ORG).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_COMMENT).count.should == 2
    end

    it "should allow RSVP creator to cancel an RSVP to a single session" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee)

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "Let's Jam!"

      # cancel
      expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "yes", :message => "Sorry, I'm bailing for this session"}, @session_invitee)}.to_not raise_error

      rsvp = RsvpRequest.find_by_id(rsvp.id)
      rsvp.canceled.should == true
      rsvp.cancel_all.should == false

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "Sorry, I'm bailing for this session"

      # verify 4 notifications were created for this music session (1 for RSVP, 1 for cancellation, and 2 for comments)
      notifications = Notification.where(:session_id => @music_session.id)
      notifications.count.should == 4
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_COMMENT).count.should == 2
    end

    it "should allow RSVP creator to cancel an RSVP for all future sessions" do
      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee)

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "Let's Jam!"

      calendar_count = Calendar.count

      # cancel & check that calendar has been added:
      expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "all", :message => "Sorry, I'm bailing for all sessions"}, @session_invitee)}.to_not raise_error
      rsvp = RsvpRequest.find_by_id(rsvp.id)
      rsvp.canceled.should == true
      rsvp.cancel_all.should == true
      (Calendar.count - calendar_count).should eq(1)

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "Sorry, I'm bailing for all sessions"

      # verify 4 notifications were created for this music session (1 for RSVP, 1 for cancellation, and 2 for comments)
      notifications = Notification.where(:session_id => @music_session.id)
      notifications.count.should == 4
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_RSVP_CANCELLED).count.should == 1
      notifications.where(:description => NotificationTypes::SCHEDULED_SESSION_COMMENT).count.should == 2
    end

    it "should not allow anyone else to cancel" do
      user = FactoryGirl.create(:user)

      rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee)

      # verify comment
      comment = SessionInfoComment.find_by_creator_id(@session_invitee)
      comment.comment.should == "Let's Jam!"

      # cancel
      expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "all", :message => "I'm gonna cancel all your RSVPs"}, user)}.to raise_error(JamPermissionError)
    end
  end

  describe "instrument_list" do
    it "single instrument" do
      @slot1.valid?
      rsvp_request = FactoryGirl.create(:rsvp_request, rsvp_slots: [@slot1], user: @session_invitee)
      rsvp_request.instrument_list.should == [ JamRuby::Instrument.find('electric guitar')]
      rsvp_request.rsvp_slots.should eq([@slot1])
      rsvp_request = RsvpRequest.find(rsvp_request.id)
      rsvp_request.rsvp_slots.should eq([@slot1])
    end

    it "multiple instruments" do
      rsvp_request = FactoryGirl.create(:rsvp_request, rsvp_slots: [@slot1, @slot2], user: @session_invitee)
      rsvp_request.instrument_list.should == [ @slot1.instrument, @slot2.instrument ]
    end
  end
end