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

import com.atlassian.bitbucket.internal.mirroring.MirroringConstants;
import com.atlassian.bitbucket.internal.mirroring.mirror.nav.MirroringNavBuilder;
import com.atlassian.bitbucket.internal.mirroring.mirror.nav.StubMirroringNavBuilder;
import com.atlassian.bitbucket.mirroring.mirror.IntegrationState;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamServer;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamServerType;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.type.TypeReference;
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 java.util.*;

import static com.atlassian.bitbucket.mirroring.mirror.UpstreamServerType.BITBUCKET_CLOUD;
import static com.atlassian.bitbucket.mirroring.mirror.UpstreamServerType.BITBUCKET_SERVER;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class DefaultConnectAddonSupportTest {

    private static final TypeReference<TreeMap<String, Object>> MAP_TYPE = new TypeReference<TreeMap<String, Object>>(){};
    private static final String DISPLAY_NAME = "EU Mirror";

    private DefaultConnectAddonSupport addonSupport;
    @Mock
    private MirroringConfig config;
    private MirroringNavBuilder mirroringNavBuilder = new StubMirroringNavBuilder();
    @Mock
    private ApplicationPropertiesService propertiesService;

    @Before
    public void setUp() {
        when(config.getAddonKey()).thenReturn("bitbucket.mirror.server-id");
        when(config.getAddonName()).thenReturn("Atlassian Bitbucket Mirror - " + DISPLAY_NAME);
        when(config.getAddonDescription()).thenReturn("Smart Mirroring service - provides fast access to repositories.");
        when(config.getAddonStpModuleName()).thenReturn("Support Tools - EU Mirror");
        when(config.getDisplayName()).thenReturn(DISPLAY_NAME);
        addonSupport = new DefaultConnectAddonSupport(config, mirroringNavBuilder);
    }

    @Test
    public void testGetServerDescriptor() throws IOException {
        UpstreamServer upstreamServer = createUpstream(BITBUCKET_SERVER);

        Map<String, Object> jsonMap = toMap(addonSupport.getDescriptor(upstreamServer));
        assertThat(jsonMap, equalTo(expectedServerDescriptor(upstreamServer)));
    }

    @Test
    public void testGetCloudDescriptor() throws IOException {
        UpstreamServer upstreamServer = createUpstream(BITBUCKET_CLOUD);

        Map<String, Object> jsonMap = toMap(addonSupport.getDescriptor(upstreamServer));
        assertThat(jsonMap, equalTo(expectedCloudDescriptor(upstreamServer)));
    }

    private Map<String, Object> toMap(ConnectAddonDescriptor descriptor) {
        return new ObjectMapper()
                .setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL)
                .convertValue(descriptor, MAP_TYPE);
    }

    private Map<String, Object> expectedServerDescriptor(UpstreamServer upstreamServer) {
        MirroringNavBuilder.AddonRest builder = mirroringNavBuilder.rest()
                .upstreamServer(upstreamServer.getId()).addon();

        Map<String, Object> descriptor = new LinkedHashMap<>();
        descriptor.put("authentication", ImmutableMap.of("type", "jwt"));
        descriptor.put("baseUrl", mirroringNavBuilder.buildAbsolute());
        descriptor.put("description", "Smart Mirroring service - provides fast access to repositories.");
        descriptor.put("key", "bitbucket.mirror.server-id");
        descriptor.put("lifecycle", ImmutableMap.builder()
                .put("installed", builder.installedHook().getRelative())
                .put("uninstalled", builder.uninstalledHook().getRelative())
                .build());
        descriptor.put("links", ImmutableMap.of(
                "self", builder.getAbsolute(),
                "rest", mirroringNavBuilder.rest().upstreamServer(upstreamServer.getId()).getRelative()));
        descriptor.put("modules", ImmutableMap.of(
                "adminPages", ImmutableList.builder()
                        .add(ImmutableMap.builder()
                                .put("conditions", ImmutableList.of(ImmutableMap.of("condition", "user_is_sysadmin")))
                                .put("key", "mirror-stp")
                                .put("name", ImmutableMap.of("value", "Support Tools"))
                                .put("url", "/plugins/servlet/stp/view")
                                .build())
                        .build(),
                "webhooks", ImmutableList.builder()
                        .add(ImmutableMap.of(
                                "event", "project:*",
                                "url", builder.events().getRelative()))
                        .add(ImmutableMap.of(
                                "event", "repo:*",
                                "url", builder.events().getRelative()))
                        .build(),
                "webItems", ImmutableList.builder()
                        .add(ImmutableMap.builder()
                                .put("conditions", ImmutableList.of(ImmutableMap.of("condition", "user_is_sysadmin")))
                                .put("context", "page")
                                .put("key", "mirror-stp-link")
                                .put("location", "atl.admin/admin-support-section")
                                .put("name", ImmutableMap.of("value", "Support Tools - " + DISPLAY_NAME))
                                .put("url", "mirror-stp")
                                .put("weight", 21)
                                .build())
                        .build(),
                "webPanels", ImmutableList.builder()
                        .add(ImmutableMap.builder()
                                .put("key", "mirror-config-panel")
                                .put("weight", 100)
                                .put("location", MirroringConstants.LOCATION_CONFIG_PANEL)
                                .put("url", mirroringNavBuilder.mirrorConfig(upstreamServer).getRelative())
                                .put("name", ImmutableMap.of("value", "Mirror Status"))
                                .build())
                        .build()));
        descriptor.put("name", "Atlassian Bitbucket Mirror - " + DISPLAY_NAME);
        descriptor.put("scopes", ImmutableList.of("read"));
        descriptor.put("vendor", ImmutableMap.of(
                "name", "Atlassian",
                "url", "http://www.atlassian.com"));
        return descriptor;
    }

    private Map<String, Object> expectedCloudDescriptor(UpstreamServer upstreamServer) {
        MirroringNavBuilder.AddonRest builder = mirroringNavBuilder.rest()
                .upstreamServer(upstreamServer.getId()).addon();

        Map<String, Object> descriptor = new LinkedHashMap<>();
        descriptor.put("authentication", ImmutableMap.of("type", "jwt"));
        descriptor.put("baseUrl", mirroringNavBuilder.buildAbsolute());
        descriptor.put("contexts", ImmutableList.of("team"));
        descriptor.put("description", "Smart Mirroring service - provides fast access to repositories.");
        descriptor.put("key", "bitbucket.mirror.server-id");
        descriptor.put("lifecycle", ImmutableMap.builder()
                .put("installed", builder.installedHook().getRelative())
                .put("uninstalled", builder.uninstalledHook().getRelative())
                .build());
        descriptor.put("links", ImmutableMap.of(
                "self", builder.getAbsolute()));
        descriptor.put("modules", ImmutableMap.builder()
                .put("adminPages", ImmutableList.builder()
                        .add(ImmutableMap.of(
                                "conditions", ImmutableList.of(
                                        ImmutableMap.builder()
                                                .put("condition", "has_account_permission")
                                                .put("params", ImmutableMap.of("permission", "admin")).build()),
                                "key", "mirror-stp",
                                "location", "org.bitbucket.account.admin",
                                "name", ImmutableMap.of("value", "Mirror Support Tools"),
                                "url", "/plugins/servlet/stp/view"))
                        .build())
                .put("configurePage", ImmutableList.builder()
                    .add(ImmutableMap.of(
                        "key", "mirror-config",
                        "name", ImmutableMap.of("value", "Configure Mirror"),
                        "url", mirroringNavBuilder.mirrorConfig(upstreamServer).getRelative()))
                    .build())
                .put("webhooks", ImmutableList.builder()
                        .add(ImmutableMap.of(
                                "event", "project:updated",
                                "url", builder.events().getRelative()))
                        .add(ImmutableMap.of(
                                "event", "repo:created",
                                "url", builder.events().getRelative()))
                        .add(ImmutableMap.of(
                                "event", "repo:deleted",
                                "url", builder.events().getRelative()))
                        .add(ImmutableMap.of(
                                "event", "repo:imported",
                                "url", builder.events().getRelative()))
                        .add(ImmutableMap.of(
                                "event", "repo:push",
                                "url", builder.events().getRelative()))
                        .add(ImmutableMap.of(
                                "event", "repo:updated",
                                "url", builder.events().getRelative()))
                        .build())
                .put("mirrorServers", ImmutableList.of(ImmutableMap.of(
                        "key", "mirror-server",
                        "repoMirrorUrl", mirroringNavBuilder
                                .rest().upstreamServer(upstreamServer.getId()).templateRepoUrl().getRelative(),
                        "name", ImmutableMap.of("value", DISPLAY_NAME))))
                .build());

        descriptor.put("name", "Atlassian Bitbucket Mirror - " + DISPLAY_NAME);
        descriptor.put("scopes", ImmutableList.of("project", "repository:admin"));
        descriptor.put("vendor", ImmutableMap.of(
                "name", "Atlassian",
                "url", "http://www.atlassian.com"));
        return descriptor;
    }

    private UpstreamServer createUpstream(UpstreamServerType type) {
        return new SimpleInternalUpstreamServer.Builder()
                .apiBaseUrl("https://api.example.com")
                .baseUrl("https://www.example.com")
                .id(UUID.randomUUID().toString())
                .issuerId("issuerId")
                .lastFullSyncDate(new Date())
                .lastSettingsChangeDate(new Date())
                .namespace("namespace")
                .state(IntegrationState.INSTALLED)
                .type(type)
                .build();
    }
}