package it.com.atlassian.stash.rest.webhook;

import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.bitbucket.test.WebHookTestHelper;
import com.google.common.collect.ImmutableList;
import it.com.atlassian.stash.BaseRestTest;
import org.apache.commons.lang.mutable.MutableInt;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.google.common.collect.ImmutableList.of;
import static java.util.Collections.emptyMap;
import static javax.ws.rs.core.Response.Status.FORBIDDEN;

public class WebHookRegistrationTest extends BaseRestTest {
    private static final String NO_PERM_USER = WebHookRegistrationTest.class.getSimpleName() + "-no-permissions-user";
    private static final String WEBHOOK_ADMIN = WebHookRegistrationTest.class.getSimpleName() + "-admin";
    private static final String WEBHOOK_USER = WebHookRegistrationTest.class.getSimpleName() + "-user";
    private static final String WEBHOOK_PROJECT = WebHookRegistrationTest.class.getSimpleName() + "-PROJECT";
    private static final String WEBHOOK_REPOSITORY = WebHookRegistrationTest.class.getSimpleName() + "-REPOSITORY";
    private static final String WEBHOOK_CALLBACK_URL = DefaultFuncTestData.getBaseURL() + "/rest/webhooks/1.0/webhook";

    private static final MutableInt projectId = new MutableInt();
    private static final MutableInt repositoryId = new MutableInt();

    @BeforeClass
    public static void createEntities() {
        getClassTestContext()
            .user(WEBHOOK_ADMIN)
            .user(WEBHOOK_USER)
            .user(NO_PERM_USER)
            .project(WEBHOOK_PROJECT, WebHookRegistrationTest.projectId::setValue)
            .repository(WEBHOOK_PROJECT, WEBHOOK_REPOSITORY, WebHookRegistrationTest.repositoryId::setValue)
            .projectPermission(WEBHOOK_PROJECT, WEBHOOK_ADMIN, Permission.PROJECT_ADMIN)
            .projectPermission(WEBHOOK_PROJECT, WEBHOOK_USER, Permission.PROJECT_READ);
    }

    @Rule
    public TestName testName = new TestName();

    @Test
    public void testCanCreateCatchAllWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("*"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCantCreateProjectSpecificCatchAllWebHookIfNotPermitted() {
        new WebHookTestHelper(NO_PERM_USER, NO_PERM_USER).create(getWebHookName(), WEBHOOK_CALLBACK_URL,
                of("*"), filterOnCurrentProject(), FORBIDDEN);
    }

    @Test
    public void testCanCreateProjectSpecificCatchAllWebHookIfProjectReadPermission() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL,
                of("*"), filterOnCurrentProject(), WEBHOOK_USER, WEBHOOK_USER);
    }

    @Test
    public void testCanCreateProjectSpecificCatchAllWebHookIfProjectAdminPermission() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL,
                of("*"), filterOnCurrentProject(), WEBHOOK_USER, WEBHOOK_USER);
    }

    @Test
    public void testCantCreateRepositorySpecificCatchAllWebHookIfNotPermitted() {
        new WebHookTestHelper(NO_PERM_USER, NO_PERM_USER).create(getWebHookName(), WEBHOOK_CALLBACK_URL,
                of("*"), filterOnCurrentRepository(), FORBIDDEN);
    }

    @Test
    public void testCanCreateRepositorySpecificCatchAllWebHookIfRepositoryReadPermission() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL,
                of("*"), filterOnCurrentRepository(), WEBHOOK_USER, WEBHOOK_USER);
    }

    @Test
    public void testCanCreateRepositorySpecificCatchAllWebHookIfRepositoryAdminPermission() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL,
                of("*"), filterOnCurrentRepository(), WEBHOOK_USER, WEBHOOK_USER);
    }

    @Test
    public void testCanCreateProjectWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("project:*"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateProjectCreatedWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("project:created"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateProjectModifiedWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("project:modified"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateProjectDeletedWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("project:deleted"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateRepositoryWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("repo:*"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateRepositoryCreatedWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("repo:created"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateRepositoryModifiedWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("repo:modified"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateRepositoryDeletedWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("repo:deleted"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCanCreateRepositoryRefsChangedWebHook() {
        testContext.webHook(getWebHookName(), WEBHOOK_CALLBACK_URL, of("repo:refs_changed"), emptyMap(), NO_PERM_USER, NO_PERM_USER);
    }

    @Test
    public void testCantCreateNonExistentWebHook() {
        new WebHookTestHelper(NO_PERM_USER, NO_PERM_USER).create(getWebHookName(), WEBHOOK_CALLBACK_URL, of("foobar"), emptyMap(), FORBIDDEN);
    }

    private Map<String, String> filterOnCurrentProject() {
        return WebHookTestHelper.filterOnProjectWithId(projectId.intValue());
    }

    private Map<String, String> filterOnCurrentRepository() {
        return WebHookTestHelper.filterOnRepositoryWithId(repositoryId.intValue());
    }

    private String getWebHookName() {
        return testName.getMethodName() + "-" + Long.toString(new Date().getTime(), 32);
    }
}
