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

import com.atlassian.bitbucket.internal.mirroring.mirror.InternalUpstreamServer;
import com.atlassian.bitbucket.internal.mirroring.mirror.MirrorConstants;
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.permission.Permission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

import static com.atlassian.bitbucket.internal.mirroring.mirror.MirrorConstants.PARAM_UPSTREAM_USERNAME;
import static com.atlassian.bitbucket.internal.mirroring.mirror.MirrorConstants.PARAM_UPSTREAM_USERNAME_TEMPLATE;
import static com.google.common.base.Preconditions.checkNotNull;

@Component("mirrorNavBuilder")
public class DefaultMirroringNavBuilder implements MirroringNavBuilder {

    private final MirroringConfig mirroringConfig;
    private final NavBuilder navBuilder;

    @Autowired
    public DefaultMirroringNavBuilder(MirroringConfig mirroringConfig, NavBuilder navBuilder) {
        this.mirroringConfig = mirroringConfig;
        this.navBuilder = navBuilder;
    }

    @Nonnull
    @Override
    public String buildAbsolute() {
        return navBuilder.buildAbsolute();
    }

    @Nonnull
    @Override
    public CloudUpstreamNavBuilder cloudUpstream(@Nonnull UpstreamServer upstream) {
        return new DefaultCloudUpstreamNavBuilder(mirroringConfig, this, (InternalUpstreamServer) upstream);
    }

    @Nonnull
    @Override
    public ServerUpstreamNavBuilder serverUpstream(@Nonnull UpstreamServer upstream) {
        return new DefaultServerUpstreamNavBuilder(upstream);
    }

    @Nonnull
    @Override
    public MirroringUrl stp(@Nonnull UpstreamServer upstream) {
        PathUrlBuilder pathUrlBuilder = new PathUrlBuilder(navBuilder.buildAbsolute(),
                "plugins", "servlet", "stp", "view");

        if (upstream.getType() == UpstreamServerType.BITBUCKET_CLOUD) {
            pathUrlBuilder = pathUrlBuilder.parameter(MirrorConstants.PARAM_HIGHEST_PERM, Permission.SYS_ADMIN.name());
        }

        return pathUrlBuilder;
    }

    @Nonnull
    @Override
    public UpstreamNavBuilder upstream(@Nonnull UpstreamServer upstream) {
        switch (upstream.getType()) {
            case BITBUCKET_CLOUD:
                return cloudUpstream(upstream);
            case BITBUCKET_SERVER:
                return serverUpstream(upstream);
            default:
                throw new IllegalStateException("Unsupported UpstreamServer type: " + upstream.getType());
        }
    }

    @Nonnull
    @Override
    public MirroringUrl mirrorConfig(@Nonnull UpstreamServer upstream) {
        PathUrlBuilder pathUrlBuilder = new PathUrlBuilder(navBuilder.buildAbsolute(),
                "plugins", "servlet", "admin", "mirror");

        if (upstream.getType() == UpstreamServerType.BITBUCKET_CLOUD) {
            addUsername(pathUrlBuilder);
        }

        return pathUrlBuilder;
    }

    @Nonnull
    @Override
    public MirroringUrl placeholder() {
        return new PathUrlBuilder(navBuilder.buildAbsolute(), "mirror");
    }

    @Nonnull
    @Override
    public Rest rest() {
        return new RestBuilder(navBuilder.buildAbsolute());
    }

    private static PathUrlBuilder addUsername(PathUrlBuilder pathUrlBuilder) {
        return pathUrlBuilder.parameterNotEncoded(PARAM_UPSTREAM_USERNAME, PARAM_UPSTREAM_USERNAME_TEMPLATE);
    }

    private static class RestBuilder extends PathUrlBuilder implements Rest {

        private RestBuilder(String baseUrl) {
            super(baseUrl, "rest", "mirroring", "latest");
        }

        @Nonnull
        @Override
        public UpstreamServerRest upstreamServer(@Nonnull String upstreamId) {
            return new UpstreamServerRestBuilder(this, checkNotNull(upstreamId, "upstreamId"));
        }
    }

    private static class UpstreamServerRestBuilder extends PathUrlBuilder implements UpstreamServerRest {

        private UpstreamServerRestBuilder(PathUrlBuilder builder, String upstreamId) {
            super(builder, "upstreamServers", upstreamId);
        }

        @Nonnull
        @Override
        public AddonRest addon() {
            return new AddonRestBuilder(this);
        }

        @Nonnull
        @Override
        public MirroringUrl templateRepoUrl() {
            PathUrlBuilder repos = new PathUrlBuilder(this, "repos").addPathNotEncoded("{repository.uuid}");
            return addUsername(repos);
        }
    }

    private static class AddonRestBuilder extends PathUrlBuilder implements AddonRest {

        private AddonRestBuilder(PathUrlBuilder builder) {
            super(builder, "addon");
        }

        @Nonnull
        @Override
        public MirroringUrl descriptor() {
            return new PathUrlBuilder(this, "descriptor");
        }

        @Nonnull
        @Override
        public MirroringUrl installedHook() {
            return new PathUrlBuilder(this, "installed");
        }

        @Nonnull
        @Override
        public MirroringUrl events() {
            return new PathUrlBuilder(this, "events");
        }

        @Nonnull
        @Override
        public MirroringUrl uninstalledHook() {
            return new PathUrlBuilder(this, "uninstalled");
        }
    }
}
