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.mirroring.mirror.UpstreamServer;
import com.atlassian.bitbucket.mirroring.mirror.UpstreamServerType;
import com.google.common.collect.ImmutableMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

import static com.atlassian.bitbucket.internal.mirroring.mirror.rest.webhook.cloud.CloudWebHookEventId.*;
import static com.google.common.base.Preconditions.checkNotNull;

@Component
public class DefaultConnectAddonSupport implements ConnectAddonSupport {

    private final MirroringConfig config;
    private final MirroringNavBuilder mirroringNavBuilder;

    @Autowired
    public DefaultConnectAddonSupport(MirroringConfig config,
                                      MirroringNavBuilder mirroringNavBuilder) {
        this.config = config;
        this.mirroringNavBuilder = mirroringNavBuilder;
    }

    @Nonnull
    @Override
    public ConnectAddonDescriptor getDescriptor(@Nonnull UpstreamServer upstreamServer) {
        checkNotNull(upstreamServer, "upstreamServer");

        UpstreamServerType type = upstreamServer.getType();
        if (type == UpstreamServerType.BITBUCKET_CLOUD) {
            return getCloudDescriptor(upstreamServer);
        } else if (type == UpstreamServerType.BITBUCKET_SERVER) {
            return getServerDescriptor(upstreamServer);
        } else {
            throw new IllegalStateException("Unknown upstream type: " + type);
        }
    }

    private ConnectAddonDescriptor getCloudDescriptor(UpstreamServer upstreamServer) {
        MirroringNavBuilder.AddonRest builder = mirroringNavBuilder.rest()
                .upstreamServer(upstreamServer.getId()).addon();

        ConnectAddonDescriptor descriptor = getCommonDescriptor(builder)
                .scopes("project", "repository:admin") //project includes repository permission. repo:admin is required for auth delegation.
                .contexts("team");

        descriptor.modules().configurePage()
                .key("mirror-config")
                .name("Configure Mirror")
                .url(mirroringNavBuilder.mirrorConfig(upstreamServer).getRelative());

        descriptor.modules().addAdminPage("mirror-stp")
                .name("Mirror Support Tools")
                .location("org.bitbucket.account.admin")
                .condition("has_account_permission", ImmutableMap.of("permission", "admin"))
                .url(mirroringNavBuilder.stp(upstreamServer).getRelative());

        descriptor.modules()
                .addWebhook(PROJECT_UPDATED.getValue(), builder.events().getRelative())
                .addWebhook(REPOSITORY_CREATED.getValue(), builder.events().getRelative())
                .addWebhook(REPOSITORY_DELETED.getValue(), builder.events().getRelative())
                .addWebhook(REPOSITORY_IMPORTED.getValue(), builder.events().getRelative())
                .addWebhook(REPOSITORY_PUSH.getValue(), builder.events().getRelative())
                .addWebhook(REPOSITORY_UPDATED.getValue(), builder.events().getRelative());

        descriptor.modules().mirrorServer()
                .key("mirror-server")
                .name(config.getDisplayName())
                .repoMirrorUrl(mirroringNavBuilder
                        .rest().upstreamServer(upstreamServer.getId()).templateRepoUrl().getRelative());

        return descriptor;
    }

    private ConnectAddonDescriptor getServerDescriptor(UpstreamServer upstreamServer) {
        MirroringNavBuilder.UpstreamServerRest upstreamRest = mirroringNavBuilder.rest()
                .upstreamServer(upstreamServer.getId());
        MirroringNavBuilder.AddonRest addonRest = upstreamRest.addon();

        ConnectAddonDescriptor descriptor = getCommonDescriptor(addonRest)
                .scopes("read");

        descriptor.links().rest(upstreamRest.getRelative());

        String callback = addonRest.events().getRelative();
        descriptor.modules()
                .addWebhook("project:*", callback)
                .addWebhook("repo:*", callback);

        descriptor.modules()
                .addAdminPage("mirror-stp")
                .name("Support Tools")
                .url(mirroringNavBuilder.stp(upstreamServer).getRelative())
                .condition("user_is_sysadmin");

        descriptor.modules()
                .addWebItem("mirror-stp-link")
                .url("mirror-stp")
                .location("atl.admin/admin-support-section")
                .name(config.getAddonStpModuleName())
                .condition("user_is_sysadmin")
                .context("page")
                .weight(21);

        descriptor.modules()
                .addWebPanel("mirror-config-panel")
                .weight(100)
                .location(MirroringConstants.LOCATION_CONFIG_PANEL)
                .url(mirroringNavBuilder.mirrorConfig(upstreamServer).getRelative())
                .name("Mirror Status");

        return descriptor;
    }

    private ConnectAddonDescriptor getCommonDescriptor(MirroringNavBuilder.AddonRest rest) {
        ConnectAddonDescriptor descriptor = new ConnectAddonDescriptor()
                .key(config.getAddonKey())
                .name(config.getAddonName())
                .description(config.getAddonDescription())
                .baseUrl(mirroringNavBuilder.buildAbsolute())
                .atlassianVendor()
                .jwtAuth();

        descriptor.links()
                .self(rest.getAbsolute());

        descriptor.lifecycle()
                .installed(rest.installedHook().getRelative())
                .uninstalled(rest.uninstalledHook().getRelative());

        return descriptor;
    }
}
