package com.avaya.zephyr.services.multichanbroadcast.announcementcall;

import static org.easymock.EasyMock.expect;
import static org.mockito.Mockito.mock;
import static org.powermock.api.easymock.PowerMock.createMock;
import static org.powermock.api.easymock.PowerMock.expectLastCall;
import static org.powermock.api.easymock.PowerMock.mockStatic;
import static org.powermock.api.easymock.PowerMock.replayAll;
import static org.powermock.api.easymock.PowerMock.verifyAll;

import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;

import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.avaya.collaboration.call.Call;
import com.avaya.collaboration.call.CallFactory;
import com.avaya.collaboration.call.Participant;
import com.avaya.collaboration.call.media.DigitCollectorOperationCause;
import com.avaya.collaboration.call.media.DigitOptions;
import com.avaya.collaboration.call.media.MediaFactory;
import com.avaya.collaboration.call.media.MediaService;
import com.avaya.collaboration.call.media.PlayItem;
import com.avaya.collaboration.call.media.PlayOperationCause;
import com.avaya.collaboration.util.logger.Logger;

@RunWith(PowerMockRunner.class)
@PrepareForTest(
{ MultiChanBroadcastMediaListenerTest.class, CallFactory.class, MediaFactory.class, Map.class, UUID.class,
        DigitCollectorOperationCause.class, PlayOperationCause.class })
public class MultiChanBroadcastMediaListenerTest
{
    private static final String CALL_ATTRIBUTE_DIGITSCOLLECTED = "digitsCollected";
    private static final String ANNOUNCEMENT_CALL_ATTRIBUTE_PROMPTANDCOLLECT_TIMEOUT =
            "announcementPromptAndCollectTimeout";
    private static final String ANNOUNCEMENT_CALL_ATTRIBUTE_FILE_URIS = "announcementFileUris";
    private MultiChanBroadcastMediaListener multiChanBroadcastMediaListenerTest;
    private Logger mockLogger;
    private Call mockCall;
    private ConcurrentMap<String, String> mockUuidCallMap;
    private ConcurrentMap<String, String> mockCallUuidMap;
    private MediaService mockMediaService;
    private PlayItem mockPlayItem;
    private Participant mockParticipant;
    private DigitOptions mockDigitOptions;
    private UUID mockUuid;
    private PlayOperationCause mockPlayOperationCause;
    private DigitCollectorOperationCause mockDigitCollectorOperationCause;

    @Before
    @SuppressWarnings("unchecked")
    public void setUp() throws Exception
    {
        mockLogger = createMock(Logger.class);
        mockCall = createMock(Call.class);
        mockUuidCallMap = createMock(ConcurrentMap.class);
        mockCallUuidMap = createMock(ConcurrentMap.class);
        mockMediaService = createMock(MediaService.class);
        mockPlayItem = createMock(PlayItem.class);
        mockParticipant = createMock(Participant.class);
        mockDigitOptions = mock(DigitOptions.class); // Don't need to verify
                                                     // DigitOptions
        mockDigitCollectorOperationCause = createMock(DigitCollectorOperationCause.class);
        mockUuid = createMock(UUID.class);
        mockPlayOperationCause = createMock(PlayOperationCause.class);

        multiChanBroadcastMediaListenerTest =
                new MultiChanBroadcastMediaListener(mockLogger, mockUuidCallMap, mockCallUuidMap);
    }

    private void setupMockCall(String callId)
    {
        mockStatic(CallFactory.class);
        expect(CallFactory.getCall(callId)).andReturn(mockCall);
    }

    private void setupMockMediaService()
    {
        mockStatic(MediaFactory.class);
        expect(MediaFactory.createMediaService()).andReturn(mockMediaService);
    }

    private void setupMockPlayItem(String[] fileUri) throws URISyntaxException
    {
        expect(MediaFactory.createPlayItem()).andReturn(mockPlayItem);
        if (fileUri == null) // No file supplied
        {
            expect(
                    mockCall.getAttribute(ANNOUNCEMENT_CALL_ATTRIBUTE_FILE_URIS))
                    .andReturn(fileUri);

            mockLogger.error((String) EasyMock.anyObject());
        }
        else if (fileUri[0].equals("")) // Invalid filename scenario
        {
            URISyntaxException uriSyntaxException = new URISyntaxException("", "");
            expect(mockPlayItem.setSource(fileUri)).andThrow(
                    uriSyntaxException);
            mockLogger.error((String) EasyMock.anyObject(), (URISyntaxException)
                    EasyMock.anyObject());
        }
        else
        {
            expect(
                    mockCall.getAttribute(ANNOUNCEMENT_CALL_ATTRIBUTE_FILE_URIS))
                    .andReturn(Arrays.asList(fileUri));

            expect(mockPlayItem.setSource(fileUri)).andReturn(mockPlayItem);
            expect(mockPlayItem.setInterruptible(true)).andReturn(mockPlayItem);
            expect(mockPlayItem.setIterateCount(1)).andReturn(mockPlayItem);
        }
    }

    private void setupMockDigitOptions(Integer timeout)
    {
        expect(
                mockCall.getAttribute(ANNOUNCEMENT_CALL_ATTRIBUTE_PROMPTANDCOLLECT_TIMEOUT))
                .andReturn(timeout);
        expect(mockCall.getAnsweringParty()).andReturn(mockParticipant);
        expect(MediaFactory.createDigitOptions()).andReturn(mockDigitOptions);
    }

    private void setupMockPromptAndCollect(String address)
    {
        expect(mockMediaService.promptAndCollect(mockParticipant, mockPlayItem, mockDigitOptions,
                multiChanBroadcastMediaListenerTest)).andReturn(mockUuid);
        expect(mockParticipant.getAddress()).andReturn(address);
        mockLogger.fine((String) EasyMock.anyObject());
    }

    private void setupMockMapInserts()
    {
        expect(mockCall.getId()).andReturn("callId").anyTimes();
        expect(mockUuidCallMap.put((String) EasyMock.anyObject(), (String) EasyMock.anyObject())).andReturn(
                null);
        expect(mockCallUuidMap.put((String) EasyMock.anyObject(), (String) EasyMock.anyObject())).andReturn(
                null);
    }

    @Test
    public void testPlayAnnouncement() throws Exception
    {
        // Test Info

        // Test Data
        final String[] fileUri =
        { "file://somefilelocation/" };
        final String address = "123@domain.com";
        final Integer timeout = 90000;

        // Setup mocks
        setupMockMediaService();
        setupMockPlayItem(fileUri);
        setupMockDigitOptions(timeout);
        setupMockPromptAndCollect(address);
        setupMockMapInserts();

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.playAnnouncementAndCollectDigits(mockCall);
        verifyAll();
    }

    @Test
    public void testPlayAnnouncements() throws Exception
    {
        // Test Info

        // Test Data
        final String[] fileUri =
        { "file://somefilelocation/", "file://somefilelocation2/" };
        final String address = "123@domain.com";
        final Integer timeout = 90000;

        // Setup mocks
        setupMockMediaService();
        setupMockPlayItem(fileUri);
        setupMockDigitOptions(timeout);
        setupMockPromptAndCollect(address);
        setupMockMapInserts();

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.playAnnouncementAndCollectDigits(mockCall);
        verifyAll();
    }

    @Test
    public void testPlayAnnouncementSetSourceException() throws Exception
    {
        // Test Info

        // Test Data
        final String[] fileUri = null;

        // Setup mocks
        setupMockMediaService();
        setupMockPlayItem(fileUri);

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.playAnnouncementAndCollectDigits(mockCall);
        verifyAll();
    }

    @Test
    public void testPlayCompleted() throws Exception
    {
        // Test Info

        // Test Data
        final String callId = "callId";
        final String address = "123@domain.com";

        // Setup mocks
        expect(mockUuidCallMap.get(mockUuid.toString())).andReturn(callId);
        setupMockCall(callId);
        expect(mockCall.getAnsweringParty()).andReturn(mockParticipant);
        expect(mockParticipant.getAddress()).andReturn(address);
        mockLogger.fine((String) EasyMock.anyObject());

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.playCompleted(mockUuid, mockPlayOperationCause);
        verifyAll();
    }

    @Test
    public void testPlayCompletedNullCallFromMap() throws Exception
    {
        // Test Info

        // Test Data

        // Setup mocks
        expect(mockUuidCallMap.get(mockUuid.toString())).andReturn(null);
        mockLogger.error((String) EasyMock.anyObject());

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.playCompleted(mockUuid, mockPlayOperationCause);
        verifyAll();
    }

    @Test
    public void testDigitsCollected() throws Exception
    {
        // Test Info

        // Test Data
        final String callId = "callId";
        final String digits = "123456";
        final String address = "123@domain.com";

        // Setup mocks
        expect(mockUuidCallMap.get(mockUuid.toString())).andReturn(callId);
        setupMockCall(callId);
        expect(mockCall.getAnsweringParty()).andReturn(mockParticipant);
        expect(
                mockCall.getAttribute(CALL_ATTRIBUTE_DIGITSCOLLECTED))
                .andReturn(false).anyTimes();
        expect(mockParticipant.getAddress()).andReturn(address);
        mockLogger.fine((String) EasyMock.anyObject());
        expectLastCall().anyTimes();
        expect(mockCall.setAttribute((String) EasyMock.anyObject(), EasyMock.anyBoolean())).andReturn(
                mockCall);
        mockLogger.info((String) EasyMock.anyObject());
        // mockCall.drop();
        // expectLastCall();

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.digitsCollected(mockUuid, digits,
                mockDigitCollectorOperationCause);
        verifyAll();
    }

    @Test
    public void testDigitsCollectedNullCallFromMap() throws Exception
    {
        // Test Info

        // Test Data
        final String digits = "123456";

        // Setup mocks
        mockLogger.fine((String) EasyMock.anyObject());
        expect(mockUuidCallMap.get(mockUuid.toString())).andReturn(null);
        mockLogger.error((String) EasyMock.anyObject());

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.digitsCollected(mockUuid, digits,
                mockDigitCollectorOperationCause);
        verifyAll();
    }

    @Test
    public void testDigitsCollectedNoDigits() throws Exception
    {
        // Test Info

        // Test Data
        final String callId = "callId";
        final String address = "123@domain.com";
        final String digits = "";

        // Setup mocks
        expect(mockUuidCallMap.get(mockUuid.toString())).andReturn(callId);
        setupMockCall(callId);
        expect(mockCall.getAnsweringParty()).andReturn(mockParticipant);
        expect(mockParticipant.getAddress()).andReturn(address);
        mockLogger.fine((String) EasyMock.anyObject());
        expectLastCall().anyTimes();
        expect(
                mockCall.getAttribute(CALL_ATTRIBUTE_DIGITSCOLLECTED))
                .andReturn(false).anyTimes();
        expect(mockCall.setAttribute((String) EasyMock.anyObject(), EasyMock.anyBoolean())).andReturn(
                mockCall);
        mockLogger.info((String) EasyMock.anyObject());
        // mockCall.drop();
        // expectLastCall();

        // Replay, Execute, and Verify
        replayAll();
        multiChanBroadcastMediaListenerTest.digitsCollected(mockUuid, digits,
                mockDigitCollectorOperationCause);
        verifyAll();
    }
}
