package com.atlassian.bitbucket.internal.webhook.repository;

import com.atlassian.bitbucket.event.pull.PullRequestMergedEvent;
import com.atlassian.bitbucket.event.repository.RepositoryDeletedEvent;
import com.atlassian.bitbucket.event.repository.RepositoryModificationRequestedEvent;
import com.atlassian.bitbucket.event.repository.RepositoryRefsChangedEvent;
import com.atlassian.bitbucket.internal.webhook.WebHookFilterParameters;
import com.atlassian.bitbucket.internal.webhook.WebHookTestSupport;
import com.atlassian.bitbucket.project.Project;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.fugue.Option;
import com.atlassian.webhooks.api.register.listener.WebHookListener;
import com.google.common.base.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.io.IOException;

import static com.atlassian.bitbucket.internal.webhook.WebHookTestSupport.serialize;
import static com.atlassian.fugue.Option.none;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class RepositoryEventMatcherTest {

    private static final int PROJECT_ID = 123;
    private static final int REPOSITORY_ID = 456;

    private RepositoryEventMatcher matcher = new RepositoryEventMatcher(RepositoryDeletedEvent.class);
    @Mock
    private Project project;
    @Mock
    private Repository repository;

    @Before
    public void setup() {
        when(repository.getProject()).thenReturn(project);
        when(repository.getId()).thenReturn(REPOSITORY_ID);
        when(project.getId()).thenReturn(PROJECT_ID);
    }

    @Test
    public void testNonRepositoryTargetEventDoesMatch() throws IOException {
        RepositoryEventMatcher matcher = new RepositoryEventMatcher(RepositoryRefsChangedEvent.class);

        PullRequestMergedEvent event = WebHookTestSupport.mockRepositoryRefsChangedEvent(
                repository, PullRequestMergedEvent.class);
        WebHookListener listener = createListenerForRepository(Option.some(PROJECT_ID), none(Integer.class));

        assertTrue(matcher.matches(event, listener));
    }

    @Test
    public void testNonTargetEventDoesNotMatch() throws IOException {
        RepositoryModificationRequestedEvent event = WebHookTestSupport.mockRepositoryEvent(
                repository, RepositoryModificationRequestedEvent.class);
        WebHookListener listener = createListenerForRepository(Option.some(PROJECT_ID), Option.some(REPOSITORY_ID));

        assertFalse(matcher.matches(event, listener));
    }

    @Test
    public void testTargetEventDoesNotMatchProjectScope() throws IOException {
        RepositoryDeletedEvent event = WebHookTestSupport.mockRepositoryEvent(
                repository, RepositoryDeletedEvent.class);
        WebHookListener listener = createListenerForRepository(Option.some(PROJECT_ID + 1), none(Integer.class));

        assertFalse(matcher.matches(event, listener));
    }

    @Test
    public void testTargetEventDoesNotMatchRepositoryScope() throws IOException {
        RepositoryDeletedEvent event = WebHookTestSupport.mockRepositoryEvent(
                repository, RepositoryDeletedEvent.class);
        WebHookListener listener = createListenerForRepository(Option.some(PROJECT_ID), Option.some(REPOSITORY_ID + 1));

        assertFalse(matcher.matches(event, listener));
    }

    @Test
    public void testTargetEventMatchesRepositoryScope() throws IOException {
        RepositoryDeletedEvent event = WebHookTestSupport.mockRepositoryEvent(
                repository, RepositoryDeletedEvent.class);
        WebHookListener listener = createListenerForRepository(Option.some(PROJECT_ID), Option.some(REPOSITORY_ID));

        assertTrue(matcher.matches(event, listener));
    }

    @Test
    public void testTargetEventMatchesWithGlobalScope() throws IOException {
        RepositoryDeletedEvent event = WebHookTestSupport.mockRepositoryEvent(
                repository, RepositoryDeletedEvent.class);
        WebHookListener listener = createListenerForRepository(none(Integer.class), none(Integer.class));

        assertTrue(matcher.matches(event, listener));
    }

    @Test
    public void testTargetEventMatchesWithProjectScope() throws IOException {
        RepositoryDeletedEvent event = WebHookTestSupport.mockRepositoryEvent(
                repository, RepositoryDeletedEvent.class);
        WebHookListener listener = createListenerForRepository(Option.some(PROJECT_ID), none(Integer.class));

        assertTrue(matcher.matches(event, listener));
    }

    private WebHookListener createListenerForRepository(Option<Integer> projectId, Option<Integer> repositoryId) throws IOException {
        WebHookFilterParameters.Builder builder = new WebHookFilterParameters.Builder();
        for (Integer someId : projectId) {
            builder = builder.project(someId);
        }
        for (Integer someId : repositoryId) {
            builder = builder.repository(someId);
        }
        WebHookFilterParameters parameters = builder.build();
        return createWebHookListener(parameters).build();
    }

    private WebHookListener.Builder createWebHookListener(WebHookFilterParameters parameters) throws IOException {
        return WebHookListener.fromPersistentStore(0, "listener", Optional.of("1"))
                .to("http://www.atlassian.com")
                .withFilter(serialize(parameters));
    }
}
