package com.atlassian.stash.internal.notification.pull.handlers;

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitRequest;
import com.atlassian.bitbucket.commit.CommitService;
import com.atlassian.bitbucket.commit.MinimalCommit;
import com.atlassian.bitbucket.NoSuchEntityException;
import com.atlassian.bitbucket.throttle.ResourceBusyException;
import com.atlassian.bitbucket.i18n.KeyedMessage;
import com.atlassian.bitbucket.notification.pull.PullRequestMergedNotification;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Map;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class PullRequestMergedNotificationHandlerTest {

    @Mock
    private CommitService commitService;

    @InjectMocks
    private PullRequestMergedNotificationHandler notificationHandler;

    @Test
    public void testContextValuesNoCommit() {
        PullRequestMergedNotification notification = mock(PullRequestMergedNotification.class);
        PullRequestNotificationTestUtils.setupNotification(notification);

        Map<String, Object> context = notificationHandler.getContext(notification);

        PullRequestNotificationTestUtils.assertNotification(notification, context);
    }

    @Test
    public void testContextValuesCommit() {
        PullRequestMergedNotification notification = mock(PullRequestMergedNotification.class);
        PullRequestNotificationTestUtils.setupNotification(notification);
        PullRequestNotificationTestUtils.setupNotificationPullRequest(notification);

        MinimalCommit minimalCommit = mock(MinimalCommit.class);
        when(notification.getCommit()).thenReturn(minimalCommit);

        String commitId = RandomStringUtils.random(16);
        when(minimalCommit.getId()).thenReturn(commitId);

        Commit commit = mock(Commit.class);
        when(commitService.getCommit(any(CommitRequest.class))).thenReturn(commit);

        Map<String, Object> context = notificationHandler.getContext(notification);

        PullRequestNotificationTestUtils.assertNotification(notification, context);
        assertEquals("Same commit", commit, context.get("commit"));

        ArgumentCaptor<CommitRequest> requestCaptor = ArgumentCaptor.forClass(CommitRequest.class);
        verify(commitService).getCommit(requestCaptor.capture());

        assertNotNull(notification.getCommit());
        assertEquals(notification.getCommit().getId(), requestCaptor.getValue().getCommitId());
        assertEquals(notification.getPullRequest().getToRef().getRepository(), requestCaptor.getValue().getRepository());
    }

    @Test
    public void testContextValuesCommitMissing() {
        PullRequestMergedNotification notification = mock(PullRequestMergedNotification.class);
        PullRequestNotificationTestUtils.setupNotification(notification);
        PullRequestNotificationTestUtils.setupNotificationPullRequest(notification);

        MinimalCommit minimalCommit = mock(MinimalCommit.class);
        when(notification.getCommit()).thenReturn(minimalCommit);

        String commitId = RandomStringUtils.random(16);
        when(minimalCommit.getId()).thenReturn(commitId);

        NoSuchEntityException ex = new NoSuchEntityException(new KeyedMessage("a", "b", "c"));
        when(commitService.getCommit(isA(CommitRequest.class))).thenThrow(ex);

        Map<String, Object> context = notificationHandler.getContext(notification);

        PullRequestNotificationTestUtils.assertNotification(notification, context);
        assertNull("Same commit", context.get("commit"));
    }

    @Test
    public void testContextValuesCommitThrottled() {
        PullRequestMergedNotification notification = mock(PullRequestMergedNotification.class);
        PullRequestNotificationTestUtils.setupNotification(notification);
        PullRequestNotificationTestUtils.setupNotificationPullRequest(notification);

        MinimalCommit minimalCommit = mock(MinimalCommit.class);
        when(notification.getCommit()).thenReturn(minimalCommit);

        String commitId = RandomStringUtils.random(16);
        when(minimalCommit.getId()).thenReturn(commitId);

        ResourceBusyException ex = new ResourceBusyException(new KeyedMessage("a", "b", "c"), "my-resource");
        when(commitService.getCommit(isA(CommitRequest.class))).thenThrow(ex);

        Map<String, Object> context = notificationHandler.getContext(notification);

        PullRequestNotificationTestUtils.assertNotification(notification, context);
        assertNull("Same commit", context.get("commit"));
    }
}
