package com.atlassian.bitbucket.mesh.git.push;

import com.atlassian.bitbucket.mesh.credentials.Credentials;
import com.atlassian.bitbucket.mesh.git.AbstractParameters;
import com.atlassian.bitbucket.mesh.git.GitConstants;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGitTimeouts;
import com.atlassian.bitbucket.mesh.util.Person;
import com.atlassian.bitbucket.mesh.util.UrlUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

public class PushParameters extends AbstractParameters {

    private final Person author;
    private final Credentials credentials;
    private final Map<String, String> environment;
    private final boolean force;
    private final boolean prune;
    private final List<String> refspecs;
    private final Repository repository;
    private final String targetUrl;

    private PushParameters(Builder builder) {
        super(builder);

        author = requireNonNull(builder.author, "author");
        credentials = builder.credentials;
        environment = builder.environment.build();
        force = builder.force;
        prune = builder.prune;
        refspecs = builder.refspecs.build();
        repository = builder.repository;
        targetUrl = UrlUtils.maybeSetUserInfo(requireNonNull(builder.targetUrl, "targetUrl"), credentials);

        checkArgument(!refspecs.isEmpty(), "no refspecs supplied");
    }

    @Nonnull
    public Person getAuthor() {
        return author;
    }

    @Nullable
    public Credentials getCredentials() {
        return credentials;
    }

    @Nonnull
    public Map<String, String> getEnvironment() {
        return environment;
    }

    @Nonnull
    public List<String> getRefspecs() {
        return refspecs;
    }

    @Nonnull
    public Repository getRepository() {
        return repository;
    }

    @Nonnull
    public String getTargetUrl() {
        return targetUrl;
    }

    public boolean isForce() {
        return force;
    }

    public boolean isPrune() {
        return prune;
    }

    public static class Builder extends AbstractBuilder<Builder, PushParameters> {

        private final ImmutableMap.Builder<String, String> environment;
        private final ImmutableList.Builder<String> refspecs;
        private final Repository repository;

        private Person author;
        private Credentials credentials;
        private boolean force;
        private boolean prune;
        private String targetUrl;

        public Builder(@Nonnull Repository repository, @Nonnull RpcGitTimeouts timeouts) {
            super(timeouts);

            this.repository = requireNonNull(repository, "repository");

            author = GitConstants.SYSTEM_GIT_IDENT;
            environment = ImmutableMap.builder();
            refspecs = ImmutableList.builder();
        }

        @Nonnull
        @Override
        public PushParameters build() {
            return new PushParameters(this);
        }

        @Nonnull
        public Builder author(@Nonnull Person value) {
            author = requireNonNull(value, "author");
            return self();
        }

        @Nonnull
        public Builder credentials(@Nullable Credentials value) {
            credentials = value;
            return self();
        }

        @Nonnull
        public Builder force(boolean value) {
            force = value;
            return self();
        }

        @Nonnull
        public Builder prune(boolean value) {
            prune = value;
            return self();
        }

        @Nonnull
        public Builder refspecs(@Nullable String value, @Nullable String... moreValues) {
            addIf(StringUtils::isNotBlank, refspecs, value, moreValues);
            return self();
        }

        @Nonnull
        public Builder refspecs(@Nonnull Iterable<String> values) {
            addIf(StringUtils::isNotBlank, refspecs, requireNonNull(values, "refspecs"));
            return self();
        }

        @Nonnull
        public Builder targetUrl(@Nonnull String value) {
            targetUrl = requireNonBlank(value, "targetUrl");
            return self();
        }

        @Nonnull
        public Builder withEnvironment(@Nonnull Map<String, String> value) {
            environment.putAll(requireNonNull(value, "environment"));
            return self();
        }

        @Nonnull
        @Override
        protected Builder self() {
            return this;
        }
    }
}
