package com.atlassian.bitbucket.internal.mirroring.mirror.nav;

import com.atlassian.bitbucket.internal.mirroring.mirror.MirroringConfig;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamServer;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamServerType;
import com.atlassian.bitbucket.nav.NavBuilder;
import com.atlassian.bitbucket.util.PageRequest;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Arrays;
import java.util.function.Function;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class DefaultMirroringNavBuilderTest {

    private static final String BASEURL = "http://bitbucket.company.com/bitbucket";
    private static final String CLOUD_USER_PARAM = "?upstream-username={user.username}";
    private static final String CONFIG_PATH = "/plugins/servlet/admin/mirror";
    private static final String REST_MIRRORING_PATH = "/rest/mirroring/latest";

    @Mock
    private MirroringConfig config;
    @Mock
    private NavBuilder navBuilder;
    @InjectMocks
    private DefaultMirroringNavBuilder builder;

    @Before
    public void setUpNavBuilder() {
        when(navBuilder.buildAbsolute()).thenReturn(BASEURL);
    }

    @Test
    public void testUpstreamRestUrls() {
        MirroringNavBuilder.UpstreamServerRest upstreamServer = builder.rest().upstreamServer("aa-bb-cc");
        assertEquals(BASEURL + REST_MIRRORING_PATH + "/upstreamServers/aa-bb-cc", upstreamServer.getAbsolute());
        assertEquals(REST_MIRRORING_PATH + "/upstreamServers/aa-bb-cc", upstreamServer.getRelative());
    }

    @Test
    public void testUpstreamRepoTemplateUrls() {
        MirroringUrl upstreamServer = builder.rest().upstreamServer("aa-bb-cc").templateRepoUrl();
        String RELATIVE = "/upstreamServers/aa-bb-cc/repos/{repository.uuid}?upstream-username={user.username}";
        assertEquals(BASEURL + REST_MIRRORING_PATH + RELATIVE, upstreamServer.getAbsolute());
        assertEquals(REST_MIRRORING_PATH + RELATIVE, upstreamServer.getRelative());
    }

    @Test
    public void testUpstreamAddonRestUrls() {
        MirroringNavBuilder.AddonRest addon = builder.rest().upstreamServer("aa-bb-cc").addon();
        assertEquals(BASEURL + REST_MIRRORING_PATH + "/upstreamServers/aa-bb-cc/addon/descriptor", addon.descriptor().getAbsolute());
        assertEquals(BASEURL + REST_MIRRORING_PATH + "/upstreamServers/aa-bb-cc/addon/installed", addon.installedHook().getAbsolute());
        assertEquals(BASEURL + REST_MIRRORING_PATH + "/upstreamServers/aa-bb-cc/addon/uninstalled", addon.uninstalledHook().getAbsolute());
        assertEquals(BASEURL + REST_MIRRORING_PATH + "/upstreamServers/aa-bb-cc/addon/events", addon.events().getAbsolute());
    }

    @Test
    public void testMirrorConfigUrlForServer() {
        UpstreamServer upstreamServer = createUpstreamServer(UpstreamServerType.BITBUCKET_SERVER);
        MirroringUrl builder = this.builder.mirrorConfig(upstreamServer);

        assertEquals(BASEURL + CONFIG_PATH, builder.getAbsolute());
        assertEquals(CONFIG_PATH, builder.getRelative());
    }

    @Test
    public void testMirrorConfigUrlForCloud() {
        UpstreamServer upstreamServer = createUpstreamServer(UpstreamServerType.BITBUCKET_CLOUD);
        MirroringUrl builder = this.builder.mirrorConfig(upstreamServer);

        assertEquals(BASEURL + CONFIG_PATH + CLOUD_USER_PARAM, builder.getAbsolute());
        assertEquals(CONFIG_PATH + CLOUD_USER_PARAM, builder.getRelative());
    }

    @Test
    public void testStpForCloud() throws Exception {
        UpstreamServer upstreamServer = createUpstreamServer(UpstreamServerType.BITBUCKET_CLOUD);

        assertEquals(BASEURL + "/plugins/servlet/stp/view?highest-permission=SYS_ADMIN",
                builder.stp(upstreamServer).getAbsolute());
        assertEquals("/plugins/servlet/stp/view?highest-permission=SYS_ADMIN",
                builder.stp(upstreamServer).getRelative());
    }

    @Test
    public void testStpForServer() throws Exception {
        UpstreamServer upstreamServer = createUpstreamServer(UpstreamServerType.BITBUCKET_SERVER);

        assertEquals(BASEURL + "/plugins/servlet/stp/view", builder.stp(upstreamServer).getAbsolute());
        assertEquals("/plugins/servlet/stp/view", builder.stp(upstreamServer).getRelative());
    }

    static void assertPagedUrl(String expected, Function<PageRequest, String> urlProducer, PageRequest pageRequest) {
        String url = urlProducer.apply(pageRequest);
        assertNotNull(url);
        String[] parts = url.split("\\?");
        assertEquals(2, parts.length);
        assertEquals(expected, parts[0]);
        String[] queryParams = parts[1].split("&");
        assertThat(Arrays.asList(queryParams), containsInAnyOrder(
                equalTo("start=" + pageRequest.getStart()),
                equalTo("limit=" + pageRequest.getLimit())));
    }

    static void assertUrlWithParams(String expected, String[] expectedParams, String actual) {
        String[] parts = actual.split("\\?");
        assertEquals(2, parts.length);
        assertEquals(expected, parts[0]);
        String[] queryParams = parts[1].split("&");
        Matcher[] paramMatchers = new Matcher[expectedParams.length];
        for (int i = 0; i < expectedParams.length; i++) {
            paramMatchers[i] = equalTo(expectedParams[i]);
        }
        assertThat(Arrays.asList(queryParams), containsInAnyOrder(paramMatchers));
    }

    private static UpstreamServer createUpstreamServer(UpstreamServerType type) {
        UpstreamServer upstreamServer = Mockito.mock(UpstreamServer.class);
        when(upstreamServer.getType()).thenReturn(type);
        return upstreamServer;
    }

}