package it.com.atlassian.stash.admin.repo;

import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.webdriver.bitbucket.page.admin.repo.RepositoryHookDialog;
import com.atlassian.webdriver.bitbucket.page.admin.repo.RepositoryHooksPage;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import it.com.atlassian.stash.BaseBrowserFuncTest;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

/**
 * This test currently assumes that only the hooks configured as part of the stash-func-test-browser project are installed.
 * <p>
 * When that changes, some of the conditions will have to be relaxed (like the check for the number of hooks).  The hook
 * configuration (names, descriptions, etc) is in atlassian-plugin.xml in the stash-func-test-browser project.
 */
public class RepositoryHooksTest extends BaseBrowserFuncTest {

    private RepositoryHooksPage page;

    @Before
    public void setUp() throws Exception {
        page = loginAsAdmin(RepositoryHooksPage.class, DefaultFuncTestData.getProject1(), DefaultFuncTestData.getProject1Repository1());
    }

    @Test
    public void testPaging() throws Exception {
        List<RepositoryHooksPage.HookRow> preReceiveHookRows = page.getPreReceiveHookRows();

        assertThat(preReceiveHookRows.size(), not(is(0)));

        page.loadNextPreReceivePage();

        List<RepositoryHooksPage.HookRow> twoPages = page.getPreReceiveHookRows();

        assertThat(twoPages.size(), greaterThan(preReceiveHookRows.size()));
    }

    @Test
    public void testListPreReceiveHooks() throws Exception {
        List<RepositoryHooksPage.HookRow> preReceiveHookRows = page.getPreReceiveHookRows();

        assertThat(preReceiveHookRows.size(), not(is(0)));

        RepositoryHooksPage.HookRow hookRow = findRow(preReceiveHookRows, is("preReceiveHookName"));
        assertThat(hookRow.isEnabled(), is(false));
        assertThat(hookRow.hasEmptyDescription(), is(false));
        assertThat(hookRow.getDescription(), is("preReceiveHook description"));
    }

    @Test
    public void testListPostReceiveHooks() throws Exception {
        List<RepositoryHooksPage.HookRow> postReceiveHookRows = page.getPostReceiveHookRows();

        assertThat(postReceiveHookRows.size(), not(is(0)));

        findRow(postReceiveHookRows, is("postReceiveHookName"));

        RepositoryHooksPage.HookRow hookRow = findRow(postReceiveHookRows, is("postReceiveHookWithNoDescriptionName"));
        assertThat(hookRow.getName(), is("postReceiveHookWithNoDescriptionName"));
        assertThat(hookRow.hasEmptyDescription(), is(true));
    }

    private RepositoryHooksPage.HookRow findRow(List<RepositoryHooksPage.HookRow> hooks, Matcher<String> nameMatcher) {
        for (RepositoryHooksPage.HookRow row : hooks) {
            if (nameMatcher.matches(row.getName())) {
                return row;
            }
        }
        throw new AssertionError("Hook not found");
    }

    @Test
    public void testEnableAndDisableHook() throws Exception {
        Supplier<RepositoryHooksPage.HookRow> getHook = new Supplier<RepositoryHooksPage.HookRow>() {
            @Override
            public RepositoryHooksPage.HookRow get() {
                return Iterables.find(page.getPostReceiveHookRows(), withName("postReceiveHookName"));
            }
        };
        RepositoryHooksPage.HookRow hook = getHook.get();
        assertThat(hook.getName() + " should be disabled", hook.isEnabled(), is(false));

        page = hook.configure().enable();

        hook = getHook.get();
        assertThat(hook.getName() + " should be enabled", hook.isEnabled(), is(true));

        page = BITBUCKET.visit(RepositoryHooksPage.class, DefaultFuncTestData.getProject1(), DefaultFuncTestData.getProject1Repository1());

        hook = getHook.get();
        assertThat(hook.getName() + " should be enabled", hook.isEnabled(), is(true));

        page = hook.configure().disable();

        hook = getHook.get();
        assertThat(hook.getName() + " should be disabled", hook.isEnabled(), is(false));

        page = BITBUCKET.visit(RepositoryHooksPage.class, DefaultFuncTestData.getProject1(), DefaultFuncTestData.getProject1Repository1());

        hook = getHook.get();
        assertThat(hook.getName() + " should be disabled", hook.isEnabled(), is(false));
    }

    @Test
    public void testEnableAndDisableHookViaToggle() throws Exception {
        Supplier<RepositoryHooksPage.HookRow> getHook = new Supplier<RepositoryHooksPage.HookRow>() {
            @Override
            public RepositoryHooksPage.HookRow get() {
                return Iterables.find(page.getPostReceiveHookRows(), withName("postReceiveHookWithNoDescriptionName"));
            }
        };
        RepositoryHooksPage.HookRow hook = getHook.get();
        assertThat(hook.getName() + " should be disabled", hook.isEnabled(), is(false));

        hook.enable();

        page = BITBUCKET.visit(RepositoryHooksPage.class, DefaultFuncTestData.getProject1(), DefaultFuncTestData.getProject1Repository1());

        hook = getHook.get();
        assertThat(hook.getName() + " should be enabled", hook.isEnabled(), is(true));

        hook.disable();

        page = BITBUCKET.visit(RepositoryHooksPage.class, DefaultFuncTestData.getProject1(), DefaultFuncTestData.getProject1Repository1());

        hook = getHook.get();
        assertThat(hook.getName() + " should be disabled", hook.isEnabled(), is(false));
    }

    @Test
    public void testConfigureHook() throws Exception {
        RepositoryHookDialog dialog = Iterables.find(page.getPostReceiveHookRows(), withName("postReceiveHookName")).configure();

        dialog.getForm().setFieldValue("message", "Hello world");
        dialog.enable(); // TODO click save instead

        page = BITBUCKET.visit(RepositoryHooksPage.class, DefaultFuncTestData.getProject1(), DefaultFuncTestData.getProject1Repository1());

        dialog = Iterables.find(page.getPostReceiveHookRows(), withName("postReceiveHookName")).configure();

        assertThat(dialog.getForm().getFieldValue("message"), is("Hello world"));

        dialog.disable();
    }

    @Test
    public void testConfigureHookValidation() throws Exception {
        RepositoryHookDialog dialog = Iterables.find(page.getPostReceiveHookRows(), withName("postReceiveHookName")).configure();

        dialog.getForm().setFieldValue("message", "bad");
        dialog.enableWithErrors();

        assertEquals("[Bad value]", dialog.getForm().getFieldErrors("message").toString());
        assertEquals("[Some error message]", dialog.getForm().getErrors().toString());
    }

    @Test
    public void testConfigureHookCheckbox() throws Exception {
        RepositoryHooksPage.HookRow row = Iterables.find(page.getPostReceiveHookRows(), withName("postReceiveHookName"));
        RepositoryHookDialog dialog = row.configure();

        dialog.getForm().checkFieldValue("myCheckbox");
        dialog.getForm().setFieldValue("message", "checkbox");
        dialog.enableWithErrors();

        // This ensures we've tested the value is actually true
        // TODO getFieldErrors needs to handle checkbox fields
        // assertEquals("[Bad checkbox value]", dialog.getForm().getFieldErrors("message").toString());
    }

    private Predicate<RepositoryHooksPage.HookRow> withName(final String name) {
        return new Predicate<RepositoryHooksPage.HookRow>() {
            @Override
            public boolean apply(RepositoryHooksPage.HookRow row) {
                return name.equals(row.getName());
            }
        };
    }
}
