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

import com.atlassian.bitbucket.mesh.git.AbstractParameters;
import com.atlassian.bitbucket.mesh.git.GitAgent;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.util.Person;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.util.List;

import static java.util.Objects.requireNonNull;

public class FetchRefParameters extends AbstractParameters {

    private final Person author;
    private final boolean forced;
    private final List<String> negotiationTips;
    private final Path remote;
    private final Repository repository;
    private final String source;
    private final String target;

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

        author = requireNonNull(builder.author, "author");
        forced = builder.forced;
        negotiationTips = builder.negotiationTips.build();
        remote = requireNonNull(builder.remote, "remote");
        repository = requireNonNull(builder.repository, "repository");
        source = MoreObjects.firstNonNull(builder.source, GitAgent.HEAD); //Fetch the remote HEAD by default
        target = Strings.nullToEmpty(builder.target); //If no explicit target is provided, just update FETCH_HEAD
    }

    @Nonnull
    public String buildRefspec() {
        StringBuilder refspec = new StringBuilder();
        if (isForced()) {
            refspec.append("+"); //Leading + enables non-fast-forward updates
        }
        refspec.append(getSource())
                .append(":")
                .append(getTarget());

        return refspec.toString();
    }

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

    @Nonnull
    public List<String> getNegotiationTips() {
        return negotiationTips;
    }

    @Nonnull
    public Path getRemote() {
        return remote;
    }

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

    @Nonnull
    public String getSource() {
        return source;
    }

    @Nonnull
    public String getTarget() {
        return target;
    }

    public boolean isForced() {
        return forced;
    }

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

        private final Person author;
        private final ImmutableList.Builder<String> negotiationTips = ImmutableList.builder();
        private final Path remote;
        private final Repository repository;

        private boolean forced;
        private String source;
        private String target;

        public Builder(@Nonnull Repository repository, @Nonnull Path remote, @Nonnull Person author) {
            this.author = requireNonNull(author, "author");
            this.remote = requireNonNull(remote, "remote");
            this.repository = requireNonNull(repository, "repository");
        }

        public Builder(@Nonnull FetchRefParameters parameters) {
            super(parameters);

            author = parameters.getAuthor();
            forced = parameters.isForced();
            negotiationTips.addAll(parameters.getNegotiationTips());
            remote = parameters.getRemote();
            repository = parameters.getRepository();
            source = parameters.getSource();
            target = parameters.getTarget();
        }

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

        @Nonnull
        public Builder forced(boolean value) {
            forced = value;

            return self();
        }

        @Nonnull
        public Builder negotiationTips(@Nullable String value, @Nullable String... values) {
            addIf(NOT_BLANK, negotiationTips, value, values);

            return self();
        }

        @Nonnull
        public Builder source(@Nullable String value) {
            source = Strings.emptyToNull(value);

            return self();
        }

        @Nonnull
        public Builder target(@Nullable String value) {
            target = Strings.emptyToNull(value);

            return self();
        }

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