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

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.git.builder.fetch.GitFetchTagMode;
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 FetchParameters extends AbstractParameters {

    private final boolean atomic;
    private final Person author;
    private final Credentials credentials;
    private final Map<String, String> environment;
    private final boolean prune;
    private final List<String> refspecs;
    private final Repository repository;
    private final String sourceUrl;
    private final GitFetchTagMode tagMode;

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

        atomic = builder.atomic;
        author = requireNonNull(builder.author, "author");
        credentials = builder.credentials;
        environment = builder.environment.build();
        prune = builder.prune;
        refspecs = builder.refspecs.build();
        repository = builder.repository;
        sourceUrl = UrlUtils.maybeSetUserInfo(requireNonNull(builder.sourceUrl, "sourceUrl"), credentials);
        tagMode = builder.tagMode;

        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 getSourceUrl() {
        return sourceUrl;
    }

    @Nonnull
    public GitFetchTagMode getTagMode() {
        return tagMode;
    }

    public boolean isAtomic() {
        return atomic;
    }

    public boolean isPrune() {
        return prune;
    }

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

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

        private boolean atomic;
        private Person author;
        private Credentials credentials;
        private boolean prune;
        private String sourceUrl;
        private GitFetchTagMode tagMode;

        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();
            tagMode = GitFetchTagMode.DEFAULT;
        }

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

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

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

        @Nonnull
        public Builder credentials(@Nullable Credentials value) {
            credentials = 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 sourceUrl(@Nonnull String value) {
            sourceUrl = requireNonBlank(value, "sourceUrl");
            return self();
        }

        @Nonnull
        public Builder tagMode(@Nonnull GitFetchTagMode value) {
            tagMode = requireNonNull(value, "tagMode");
            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;
        }
    }
}
