package com.atlassian.stash.internal.audit;

import com.atlassian.bitbucket.audit.AuditEntry;
import com.atlassian.bitbucket.audit.Priority;
import com.atlassian.bitbucket.commit.MinimalCommit;
import com.atlassian.bitbucket.event.audit.AuditEvent;
import com.atlassian.bitbucket.event.pull.*;
import com.atlassian.bitbucket.pull.PullRequest;
import com.atlassian.bitbucket.pull.PullRequestParticipant;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.util.SimpleCancelState;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;

import java.util.Collections;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class PullRequestEventListenerTest extends AbstractAuditEventListenerTest {

    @InjectMocks
    private PullRequestEventListener listener;

    @Test
    public void testOnPullRequestOpened() throws Exception {
        listener.onPullRequestOpened(new PullRequestOpenedEvent(this, mockPullRequest()));

        AuditEvent event = getAuditEvent();
        AuditEntry entry = event.getEntry();
        assertContains(entry.getDetails(), "1", "project-key1", "sluggy1", "project-key3", "sluggy3");
        assertEquals("project-key3/sluggy3", entry.getTarget());
        assertEventCommonDetails(event, Priority.LOW);
    }

    @Test
    public void testOnPullRequestApproved() throws Exception {
        listener.onPullRequestApproved(new PullRequestApprovedEvent(this, mockPullRequestParticipant(USERNAME), mockPullRequest()));

        AuditEvent event = getAuditEvent();
        AuditEntry entry = event.getEntry();
        assertContains(entry.getDetails(), "1", "USERNAME");
        assertEquals("project-key3/sluggy3", entry.getTarget());
        assertEventCommonDetails(event, Priority.LOW);
    }

    @Test
    public void testOnPullPullRequestDeclined() throws Exception {
        listener.onPullRequestDeclined(new PullRequestDeclinedEvent(this, mockPullRequest()));

        AuditEvent event = getAuditEvent();
        AuditEntry entry = event.getEntry();
        assertContains(entry.getDetails(), "1");
        assertEquals("project-key3/sluggy3", entry.getTarget());
        assertEventCommonDetails(event, Priority.LOW);
    }

    @Test
    public void testOnPullRequestMerged() throws Exception {
        MinimalCommit commit = mock(MinimalCommit.class);
        when(commit.getId()).thenReturn("id");

        listener.onPullRequestMerged(new PullRequestMergedEvent(this, mockPullRequest(),
                commit, null, Collections.<String, Object>emptyMap()));

        AuditEvent event = getAuditEvent();
        AuditEntry entry = event.getEntry();
        assertContains(entry.getDetails(), "1", "to", "toLatestCommit");
        assertEquals("project-key3/sluggy3", entry.getTarget());
        assertEventCommonDetails(event, Priority.LOW);
    }

    @Test
    public void testOnPullRequestRolesUpdated() throws Exception {
        listener.onPullRequestReviewersUpdated(new PullRequestReviewersUpdatedEvent(this, mockPullRequest(),
                Collections.singleton(mockUser("fred")), Collections.singleton(mockUser("barney"))));

        AuditEvent event = getAuditEvent();
        AuditEntry entry = event.getEntry();
        assertContains(entry.getDetails(), "1", "fred", "barney");
        assertEquals("project-key3/sluggy3", entry.getTarget());
        assertEventCommonDetails(event, Priority.LOW);
    }

    @Test
    public void testOnPullRequestOpenRequested() throws Exception {
        listener.onPullRequestOpenRequested(new PullRequestOpenRequestedEvent(this, mockPullRequest(),
                Collections.singleton(mockUser("fred")), new SimpleCancelState()));

        AuditEvent event = getAuditEvent();
        AuditEntry entry = event.getEntry();
        assertContains(entry.getDetails(), "1", "project-key1", "sluggy1", "project-key3", "sluggy3", "fred");
        assertEquals("project-key3/sluggy3", entry.getTarget());
        assertEventCommonDetails(event, Priority.LOW);
    }

    @Test
    public void testOnPullRequestParticipantsUpdated() throws Exception {
        listener.onPullRequestParticipantsUpdated(new PullRequestParticipantsUpdatedEvent(this, mockPullRequest(),
                Collections.singleton(mockUser("fred")), Collections.singleton(mockUser("barney"))));

        AuditEvent event = getAuditEvent();
        AuditEntry entry = event.getEntry();
        assertContains(entry.getDetails(), "1", "fred", "barney");
        assertEquals("project-key3/sluggy3", entry.getTarget());
        assertEventCommonDetails(event, Priority.LOW);
    }

    private static PullRequest mockPullRequest() {
        Repository from = mockRepositoryWithOrigin(1);
        Repository to = mockRepositoryWithOrigin(3);

        PullRequest pullRequest = mock(PullRequest.class, Mockito.RETURNS_DEEP_STUBS);
        when(pullRequest.getId()).thenReturn(1L);
        when(pullRequest.getFromRef().getId()).thenReturn("from");
        when(pullRequest.getFromRef().getRepository()).thenReturn(from);
        when(pullRequest.getToRef().getId()).thenReturn("to");
        when(pullRequest.getToRef().getLatestCommit()).thenReturn("toLatestCommit");
        when(pullRequest.getToRef().getRepository()).thenReturn(to);

        return pullRequest;
    }

    private static PullRequestParticipant mockPullRequestParticipant(String name) {
        ApplicationUser user = mockUser(name);

        PullRequestParticipant participant = mock(PullRequestParticipant.class, Mockito.RETURNS_DEEP_STUBS);
        when(participant.getUser()).thenReturn(user);

        return participant;
    }
}
