/*****************************************************************************
 * © 2015 Avaya Inc. All rights reserved.
 ****************************************************************************/
package com.avaya.services.Callingpolicies;

import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.avaya.collaboration.call.Call;
import com.avaya.collaboration.call.Participant;
import com.avaya.collaboration.call.ParticipantFactory;
import com.avaya.collaboration.util.logger.Logger;

@RunWith(PowerMockRunner.class)
@PrepareForTest(
{ ParticipantFactory.class })
public class CallingPoliciesCallListenerTest
{
    @Mock
    private PromptAndCollectOperation prmptNClctOper;
    @Mock
    private Call call;
    @Mock
    private Participant callingParty, alertingParty, droppedParticipant;
    @Mock
    private Logger logger;
    @Mock
    private Participant bob;
    private final String bobHandle = "12345";
    private final String bobDomain = "avaya.com";
    private final String bobAddress = bobHandle + "@" + bobDomain;
    private final String bobDisplayName = "Bob" + bobHandle;

    private CallingPoliciesCallListener calngPolcsCallLstnr;
    private List<Participant> activeParties = new ArrayList<Participant>();

    @Before
    public void setUp()
    {
        PowerMockito.mockStatic(ParticipantFactory.class);
        calngPolcsCallLstnr = new CallingPoliciesCallListener(prmptNClctOper, logger);
        when(logger.isFinerEnabled()).thenReturn(true);

        when(bob.getAddress()).thenReturn(bobAddress);
        when(bob.getHandle()).thenReturn(bobHandle);
        when(bob.getDomain()).thenReturn(bobDomain);
        when(bob.getDisplayName()).thenReturn(bobDisplayName);
        activeParties.add(bob);
        when(call.getActiveParties()).thenReturn(activeParties);
    }

    @Test
    public void callIntercepted()
    {
        calngPolcsCallLstnr.callIntercepted(call);

        verify(prmptNClctOper).executePromptAndCollectOperation(call);
    }

    @Test
    public void callAlerting_SeqFork_timerAlreadyStarted()
    {
        when(alertingParty.getCall()).thenReturn(call);
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.SEQUENTIALFORK_DIGIT5);
        when(call.getAttribute(CallingPoliciesMediaListener.NOANSWERTIMER_ENABLED)).thenReturn(CallingPoliciesMediaListener.NO);
        calngPolcsCallLstnr.callAlerting(alertingParty);
        verify(call, never()).setAttribute(CallingPoliciesMediaListener.NOANSWERTIMER_ENABLED, CallingPoliciesMediaListener.NO);
        verify(logger).finer("callAlerting EXIT - noAnswerTimer had already been started for this call.");
    }

    @Test
    public void callAlerting_SeqFork_timerNotStarted()
    {
        when(alertingParty.getCall()).thenReturn(call);
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.SEQUENTIALFORK_DIGIT5);
        when(call.getAttribute(CallingPoliciesMediaListener.NOANSWERTIMER_ENABLED)).thenReturn(CallingPoliciesMediaListener.YES);
        calngPolcsCallLstnr.callAlerting(alertingParty);

        verify(prmptNClctOper).startNoAnswerTimer(call);
        verify(call).setAttribute(CallingPoliciesMediaListener.NOANSWERTIMER_ENABLED, CallingPoliciesMediaListener.NO);
        verify(logger).finer("callAlerting EXIT");
    }

    @Test
    public void callAlerting_notSeqFork()
    {
        when(alertingParty.getCall()).thenReturn(call);
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(),CallingPoliciesMediaListener.CALLBACK_DIGIT7);
        // when(call.getAttribute(CallingPoliciesMediaListener.OPTION_SELECTED)).thenReturn(CallingPoliciesMediaListener.CALLBACK_DIGIT7);
        calngPolcsCallLstnr.callAlerting(alertingParty);
        verify(call, never()).setAttribute(CallingPoliciesMediaListener.NOANSWERTIMER_ENABLED, CallingPoliciesMediaListener.NO);
        verify(logger).finer("callAlerting EXIT");
    }

    @Test
    public void callAnswered()
    {
        calngPolcsCallLstnr.callAnswered(call);

        verify(prmptNClctOper).cancelSequentialForkingTimer(call);
    }

    @Test
    public void participantDropped_noActiveParties()
    {
        activeParties.remove(0);
        calngPolcsCallLstnr.participantDropped(call, droppedParticipant, null);
        verify(logger).finer("participantDropped ENTER");
        verify(prmptNClctOper, never()).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT - Last participant dropped.");
    }

    @Test
    public void participantDropped_noOptionSelected()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.NO_SELECTION);
        calngPolcsCallLstnr.participantDropped(call, droppedParticipant, null);
        verify(logger).finer("participantDropped ENTER");
        verify(prmptNClctOper, never()).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT - optionChosen is empty");
    }

    @Test
    public void participantDropped_callback_callingPartyDropped_success()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.CALLBACK_DIGIT7);
        when(call.getCallingParty()).thenReturn(bob);
        when(ParticipantFactory.create(bobHandle, bobDomain, bobDisplayName)).thenReturn(bob);
        calngPolcsCallLstnr.participantDropped(call, bob, null);
        verify(call).addParticipant(bob);
        verify(prmptNClctOper, never()).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void participantDropped_callback_callingPartyDropped_exception()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.CALLBACK_DIGIT7);
        when(call.getCallingParty()).thenReturn(bob);
        final IllegalArgumentException e = new IllegalArgumentException();
        doThrow(e).when(call).addParticipant(Matchers.any(Participant.class));
        calngPolcsCallLstnr.participantDropped(call, bob, null);
        verify(call).drop();
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void participantDropped_callback_NonCallingPartyDropped()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.CALLBACK_DIGIT7);
        when(call.getCallingParty()).thenReturn(bob);
        calngPolcsCallLstnr.participantDropped(call, droppedParticipant, null);
        verify(call, never()).addParticipant(bob);
        verify(prmptNClctOper, never()).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void participantDropped_callback_calledPartyDropped_makeAnotherCall()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.MAKEANOTHERCALL_DIGIT6);
        when(call.getCallingParty()).thenReturn(callingParty);
        calngPolcsCallLstnr.participantDropped(call, bob, null);
        verify(prmptNClctOper).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void participantDropped_callback_calledPartyDropped_seqFork()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.SEQUENTIALFORK_DIGIT5);
        when(call.getCallingParty()).thenReturn(callingParty);
        calngPolcsCallLstnr.participantDropped(call, bob, null);
        verify(prmptNClctOper).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void participantDropped_callback_NoncalledPartyDropped_makeAnotherCall()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.MAKEANOTHERCALL_DIGIT6);
        when(call.getCallingParty()).thenReturn(callingParty);
        calngPolcsCallLstnr.participantDropped(call, callingParty, null);
        verify(prmptNClctOper, never()).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void participantDropped_callback_NoncalledPartyDropped_seqFork()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.SEQUENTIALFORK_DIGIT5);
        when(call.getCallingParty()).thenReturn(callingParty);
        calngPolcsCallLstnr.participantDropped(call, callingParty, null);
        verify(prmptNClctOper, never()).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void participantDropped_notCallback_NotMakeAnotherCall_NotseqFork()
    {
        when(call.getId()).thenReturn("test");
        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.ALLOW_DIGIT1);
        when(call.getCallingParty()).thenReturn(callingParty);
        calngPolcsCallLstnr.participantDropped(call, bob, null);
        verify(prmptNClctOper, never()).addPartyToCall(call);
        verify(logger).finer("participantDropped EXIT");
    }

    @Test
    public void callTerminated()
    {
        calngPolcsCallLstnr.callTerminated(call, null);
        verify(prmptNClctOper).cancelSequentialForkingTimer(call);
    }

}
