require 'spec_helper'

module Telephony
  module Events
    describe Base do
      describe '.log' do
        let(:conversation) { create :conversation }

        before do
          @count = Terminate.count
          @call_state = 'call_state'
          @agent = create :agent
          conversation.calls.create number: "1112223333",
            agent: @agent

          Base.log name: :terminate,
            data: {
              call_state: @call_state,
              conversation_id: conversation.id,
              call_id: conversation.calls.first.id,
              conversation_state: "terminated"
            }
        end

        it 'creates an event of the given type' do
          Terminate.count.should == @count + 1
        end

        it 'sets its attributes from the given data' do
          event = Terminate.last
          event.should be
          event.call_state.should == @call_state
        end

        it 'serializes the agent messages into message data' do
          event = Terminate.last
          event.message_data.should == [agent: @agent]
        end
      end

      describe '.find_last_for_agent' do
        before do
          @agent = create :on_a_call_agent
        end

        context 'agent has no calls' do
          it 'returns the default event' do
            Base.find_last_for_agent(@agent).should be_an(InitializeWidget)
          end
        end

        context 'agent has calls' do
          context 'but with no events' do
            before do
              @conversation = create :conversation
              @call = create :call, conversation: @conversation, agent: @agent
            end

            it 'returns the default event' do
              Base.find_last_for_agent(@agent).should be_an(InitializeWidget)
            end
          end

          context 'but with no publishable events' do
            before do
              @conversation = create :conversation
              @call = create :call, conversation: @conversation, agent: @agent

              non_publishable_event = create :answer_event, conversation: @conversation
            end

            it 'returns the default event' do
              Base.find_last_for_agent(@agent).should be_an(InitializeWidget)
            end
          end

          context 'with publishable events for agent2' do
            before do
              class Base
                attr_accessible :type
              end

              class Dummy < Base
                def publishable?
                  true
                end

                def agent_messages
                  [{ agent: agent2 }]
                end
              end
            end

            it 'returns the default event' do
              agent1 = create :on_a_call_agent
              agent2 = create :on_a_call_agent
              conversation = create :conversation
              agent1_call = create :call, conversation: conversation, agent: agent1
              customer = create :call, conversation: conversation
              agent2_call = create :call, conversation: conversation, agent: agent2
              publishable_event = conversation.events.create type: 'Telephony::Events::Dummy'

              Base.find_last_for_agent(agent1).should be_an(InitializeWidget)
            end
          end

          context 'with publishable events for agent1' do
            before do
              @agent1 = create :on_a_call_agent
              @conversation1 = create :conversation
              create :call, conversation: @conversation1, agent: @agent1
              create :call, conversation: @conversation1
              publishable_event = build :conversation_start_event, conversation: @conversation1

              publishable_event.update_attributes message_data: publishable_event.agent_messages
            end

            it 'returns the last event for agent1' do
              Base.find_last_for_agent(@agent1).should be_an(Start)
            end

            context "even if another agent transfers a call to the first agent" do
              before do
                @conversation2 = create(:in_progress_conversation).tap do |conversation|
                  create :active_agent_leg, conversation: @conversation2
                  create :customer_leg, conversation: @conversation2
                end
                @conversation2.transfer!(@agent1.csr_id, true)
              end

              it 'still returns the last event for agent1' do
                Base.find_last_for_agent(@agent1).should be_an(Start)
              end
            end
          end
        end

        context "given an agent that's not on a call" do
          before do
            @agent = create :available_agent

            @event = Base.find_last_for_agent @agent
          end

          it 'returns a new default event' do
            @event.should be_an(InitializeWidget)
          end
        end
      end

      describe '#new_default_event' do
        before do
          @event = Base.new_default_event
        end

        it 'returns a new initialize widget event' do
          @event.should be_an(InitializeWidget)
        end
      end

      describe '#publish' do
        let(:event_publisher) { mock(:event_publisher) }

        context "given a publishable event" do
          let(:event) { build(:publishable_event) }

          context "with an agent" do
            before do
              event.message_data = event.agent_messages
              PusherEventPublisher
                .should_receive(:publish)
                .with(channel: "csrs-#{event.call.agent.csr_id}",
                      name: event.class.name.demodulize,
                      data: {
                        event_id: event.id,
                        conversation_id: event.conversation.id,
                        conversation_state: event.conversation.state,
                        call_id: event.call.id,
                        number: event.conversation.customer.number,
                        loan_id: event.conversation.loan_id,
                        owner: true
                      })
            end

            it "asks the event publisher to publish an event" do
              event.publish
            end
          end

          context "with no agent" do
            before do
              event.call.stub(:agent).and_return nil
              PusherEventPublisher.should_not_receive(:publish)
            end

            it "does not ask the event publisher to publish an event" do
              event.publish
            end
          end
        end

        context 'given a non-publishable event' do
          let(:event) { build(:non_publishable_event) }

          before do
            PusherEventPublisher.should_not_receive(:publish)
          end

          it "does not ask the event publisher to publish an event" do
            event.publish
          end
        end
      end

      describe '#publishable?' do
        context 'by default' do
          before do
            @event = Base.new
          end

          it 'returns false' do
            @event.should_not be_publishable
          end
        end
      end

      describe '#default_data' do
        let(:event) { create(:publishable_event) }

        it 'returns the event data' do
          event.default_data[:event_id].should == event.id
          event.default_data[:conversation_id].should == event.conversation.id
          event.default_data[:conversation_state].should == event.conversation.state
          event.default_data[:call_id].should == event.call.id
          event.default_data[:number].should == event.conversation.customer.number
          event.default_data[:loan_id].should == event.conversation.loan_id
          event.default_data[:owner].should == true
        end
      end

      describe "#agent2" do
        it "returns the last call's agent" do
          agent1 = create :agent
          agent2 = create :agent
          conversation = create :conversation
          agent1_call = create :call, conversation: conversation, agent: agent1
          customer = create :call, conversation: conversation
          agent2_call = create :call, conversation: conversation, agent: agent2, state: 'terminated'
          event = create(:two_step_transfer_failed_event, conversation: conversation)

          event.send(:agent2).should == agent2
        end
      end

      describe '#republish_only_for' do
        context 'given an event that was published for multiple agents' do
          before do
            @event = create :two_step_transfer_failed_event
          end

          context 'and the current agent received the event' do
            before do
              @agent = @event.agent2
              PusherEventPublisher.should_receive :publish
            end

            it 'republishes the event for the current agent' do
              @event.republish_only_for @agent
            end
          end

          context 'and the current agent did not receive the event' do
            before do
              @agent = create :agent
              PusherEventPublisher.should_not_receive :publish
            end

            it 'does not republish the event for the current agent' do
              @event.republish_only_for @agent
            end
          end
        end
      end
    end

    describe Connect do
      describe '#agent_messages' do
        context "for agent1" do
          let(:event) { build(:agent_call_connect_event) }

          before do
            @agent1 = event.agent_messages.first[:agent]
          end

          it 'returns agent1' do
            @agent1.should == event.conversation.first_active_agent
          end

          it 'sets owner to true' do
            event.agent_messages.first[:data][:owner].should be_true
          end
        end

        context "for agent2" do
          let(:event) { build :agent_call_connect_event }

          before do
            event.should_receive(:agent1).and_return('something not equal')
          end

          it 'sets owner to false' do
            event.agent_messages.first[:data][:owner].should be_false
          end
        end
      end

      describe '#publishable?' do
        context 'given a call connect event' do
          context 'for an agent' do
            let(:event) { build(:agent_call_connect_event) }

            it 'returns true' do
              event.should be_publishable
            end
          end

          context 'for a non agent' do
            let(:event) { build(:borrower_call_connect_event) }

            it 'returns false' do
              event.should_not be_publishable
            end
          end

          context 'for a transferring conversation' do
            let(:event) { build(:agent_call_connect_event,
                                conversation_state: 'one_step_transferring') }

            it 'returns false' do
              event.should_not be_publishable
            end
          end
        end

        context 'given a conversation connect event' do
          let(:event) { build(:conversation_connect_event) }

          it 'returns false' do
            event.should_not be_publishable
          end
        end
      end
    end

    describe Start do
      describe '#agent_messages' do
        let(:event) { build :conversation_start_event }

        before do
          @agent1 = event.agent_messages.first[:agent]
        end

        it 'returns agent1' do
          @agent1.should == event.conversation.first_active_agent
        end
      end

      describe '#publishable?' do
        let(:event) { build(:conversation_start_event) }

        it 'returns true' do
          event.should be_publishable
        end
      end
    end

    describe CallAnswered do
      context "for an agent's call" do
        describe '#agent_messages' do
          context "for agent1" do
            let(:event) { build :conversation_answer_event }

            before do
              @agent1 = event.agent_messages.first[:agent]
            end

            it 'returns agent1' do
              @agent1.should == event.conversation.first_active_agent
            end

            it 'sets owner to true' do
              event.agent_messages.first[:data][:owner].should be_true
            end
          end

          context "for agent2" do
            let(:event) { build :conversation_answer_event }

            before do
              event.should_receive(:agent1).and_return('something not equal')
            end

            it 'sets owner to false' do
              event.agent_messages.first[:data][:owner].should be_false
            end
          end
        end

        describe '#publishable?' do
          let(:event) { build(:conversation_answer_event) }

          it 'returns true' do
            event.should be_publishable
          end
        end
      end

      context "for a customer's call" do
        describe '#publishable?' do
          let(:event) { build(:conversation_answer_event_for_customer) }

          it 'returns false' do
            event.should_not be_publishable
          end
        end
      end
    end

    describe Ended do
      describe '#agent_messages' do
        let(:event) { build(:csr_call_ended_event) }

        before do
          @agent = event.agent_messages.first[:agent]
        end

        it "returns the call's agent" do
          @agent.should == event.call.agent
        end
      end

      describe '#publishable?' do
        context 'given a call ended event' do
          context 'for a CSR' do
            let(:event) { build(:csr_call_ended_event) }

            it 'returns true' do
              event.should be_publishable
            end
          end

          context 'for a non CSR' do
            let(:event) { build(:borrower_call_ended_event) }

            it 'returns false' do
              event.should_not be_publishable
            end
          end
        end

        context 'given a conversation ended event' do
          let(:event) { build(:conversation_ended_event) }

          it 'returns false' do
            event.should_not be_publishable
          end
        end
      end
    end

    describe Transfer do
      describe '#publishable?' do
        let(:event) { build(:two_step_transfer_initiated_event) }

        it 'returns true' do
          event.should be_publishable
        end
      end

      describe '#agent_messages' do
        let(:event) { build(:two_step_transfer_initiated_event) }

        context 'for agent1' do
          before do
            @agent1 = event.agent_messages.first[:agent]
            @agent1_data = event.agent_messages.first[:data]
          end

          it 'returns agent1' do
            @agent1 == event.conversation.first_active_agent
          end

          it 'returns the event data' do
            @agent1_data[:transferrer].should be_true
            @agent1_data[:agent_name].should == event.conversation.second_active_agent.name
            @agent1_data[:agent_ext].should == event.conversation.second_active_agent.phone_ext
            @agent1_data[:agent_type].should == event.conversation.second_active_agent.csr_type
          end
        end

        context 'for agent2' do
          before do
            @agent2 = event.agent_messages.last[:agent]
            @agent2_data = event.agent_messages.last[:data]
          end

          it 'returns agent2' do
            @agent2.should == event.conversation.second_active_agent
          end

          it 'returns the event data' do
            @agent2_data[:transferrer].should be_false
            @agent2_data[:agent_name].should == event.conversation.first_active_agent.name
            @agent2_data[:agent_ext].should == event.conversation.first_active_agent.phone_ext
            @agent2_data[:agent_type].should == event.conversation.first_active_agent.csr_type
            @agent2_data[:owner].should == false
          end
        end
      end
    end

    describe LeaveTwoStepTransfer do
      describe '#publishable?' do
        let(:event) { build(:leave_two_step_transfer_event) }

        it 'returns true' do
          event.should be_publishable
        end
      end

      describe '#agent_messages' do
        let(:event) { build(:leave_two_step_transfer_event) }

        before do
          @agent = event.agent_messages.first[:agent]
        end

        it 'returns agent1' do
          @agent.should == event.conversation.first_active_agent
        end
      end
    end

    describe FailOneStepTransfer do
      let(:event) { build(:one_step_transfer_failed_event) }

      describe '#publishable?' do
        it 'returns true' do
          event.should be_publishable
        end
      end

      describe '#agent_messages' do
        context 'for agent1' do
          before do
            @agent1 = event.agent_messages.first[:agent]
            @agent1_data = event.agent_messages.first[:data]
          end

          it 'returns agent1' do
            @agent1.should == event.conversation.first_inactive_agent
          end

          it 'returns the event data' do
            @agent1_data[:transferrer].should be_true
            @agent1_data[:agent_name].should == event.conversation.transferee.name
            @agent1_data[:agent_ext].should == event.conversation.transferee.phone_ext
            @agent1_data[:agent_type].should == event.conversation.transferee.csr_type
          end
        end

      end

      describe '#agent1' do
        before do
          @agent1 = event.agent1
        end

        it "returns its conversation's first inactive agent" do
          @agent1.should == event.conversation.first_inactive_agent
        end
      end
    end

    describe LeaveVoicemail do
      describe '#publishable?' do
        let(:event) { build :leave_voicemail_event }

        it 'returns true' do
          event.should be_publishable
        end
      end

      describe '#agent_messages' do
        let(:event) { build :leave_voicemail_event }

        before do
          @agent_messages = event.agent_messages.first
        end

        it 'returns agent1' do
          @agent_messages[:agent].should == event.agent1
        end

        it 'returns agent2 data' do
          agent2 = event.agent2
          data = @agent_messages[:data]
          data[:agent_name].should == agent2.name
          data[:agent_ext].should == agent2.phone_ext
          data[:agent_type].should == agent2.csr_type
        end
      end
    end

    describe CompleteHold do
      context "conversation level event" do
        let(:event) { build :complete_hold_event }

        describe "#publishable?" do
          it { event.should be_publishable }
        end

        describe "#agent_messages" do
          context "when there is one agent" do
            it "publishes to first agent's channel" do
              first_agent = event.agent_messages.first[:agent]
              first_agent.should == event.agent1
            end

            it "does not publish for agent2" do
              event.agent_messages.size.should == 1
            end
          end

          context "when there are two agents" do
            before do
              @agent2_leg = build :active_agent_leg
              event.conversation.calls.concat([@agent2_leg])
              @agent2_msgs = event.agent_messages.last
            end

            it "publishes to second agent's channel" do
              second_agent = @agent2_msgs[:agent]
              second_agent.should == @agent2_leg.agent
            end

            it "sets owner to false" do
              @agent2_msgs[:data][:owner].should == false
            end
          end
        end
      end

      context "call level event" do
        let(:event) { build :complete_hold_event, call_id: 1 }

        describe "#publishable?" do
          it { event.should_not be_publishable }
        end
      end
    end

    describe CompleteResume do
      let(:event) { build :complete_resume_event }

      describe "#publishable?" do
        it { event.should be_publishable }
      end

      describe "#agent_messages" do
        context "when there is one agent" do
          it "publishes to first agent's channel" do
            first_agent = event.agent_messages.first[:agent]
            first_agent.should == event.agent1
          end

          it "does not publish for agent2" do
            event.agent_messages.size.should == 1
          end
        end

        context "when there are two agents" do
          before do
            @agent2_leg = build :active_agent_leg
            event.conversation.calls.concat([@agent2_leg])
            @agent2_msgs = event.agent_messages.last
          end

          it "publishes to second agent's channel" do
            second_agent = @agent2_msgs[:agent]
            second_agent.should == @agent2_leg.agent
          end

          it "sets owner to false" do
            @agent2_msgs[:data][:owner].should == false
          end
        end
      end
    end

    describe InitializeWidget do
      describe '#republish_only_for' do
        before do
          @agent = create :agent
          @event = InitializeWidget.new

          PusherEventPublisher.should_receive :publish
        end

        it 'republishes the event for the current agent' do
          @event.republish_only_for @agent
        end
      end
    end
  end
end
