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

import com.atlassian.bitbucket.mesh.git.AbstractParameters;
import com.atlassian.bitbucket.mesh.git.GitRefPattern;
import com.atlassian.bitbucket.mesh.git.file.EditFileParameters;
import com.atlassian.bitbucket.mesh.git.merge.MergeParameters;
import com.atlassian.bitbucket.mesh.git.rebase.RebaseParameters;
import com.atlassian.bitbucket.mesh.git.ref.Branch;
import com.atlassian.bitbucket.mesh.git.ref.Ref;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.util.Person;
import com.atlassian.bitbucket.mesh.util.ShaUtils;

import javax.annotation.Nonnull;

import java.util.Objects;

import static com.google.common.base.MoreObjects.firstNonNull;
import static java.util.Objects.requireNonNull;

public class RefFetchParameters extends AbstractParameters {

    private final Person author;
    private final boolean crossRepository;
    private final boolean forced;
    private final Repository fromRepository;
    private final Ref ref;
    private final String targetRef;
    private final Repository toRepository;

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

        author = requireNonNull(builder.author, "author");
        crossRepository = builder.crossRepository;
        forced = builder.forced;
        fromRepository = firstNonNull(builder.fromRepository, requireNonNull(builder.toRepository, "toRepository"));
        ref = requireNonNull(builder.ref, "ref");
        targetRef = firstNonNull(builder.targetRef, ref.getId());
        toRepository = builder.toRepository; //Null-checked against fromRepository above
    }

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

    @Nonnull
    public Repository getFromRepository() {
        return fromRepository;
    }

    @Nonnull
    public Ref getRef() {
        return ref;
    }

    @Nonnull
    public String getTargetRef() {
        return targetRef;
    }

    @Nonnull
    public Repository getToRepository() {
        return toRepository;
    }

    public boolean isCrossRepository() {
        return crossRepository;
    }

    public boolean isForced() {
        return forced;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        if (isCrossRepository()) {
            builder.append(fromRepository)
                    .append(":")
                    .append(ref.getId())
                    .append(" -> ")
                    .append(toRepository)
                    .append(":")
                    .append(targetRef);
        } else {
            builder.append(toRepository)
                    .append(" ")
                    .append(ref.getId())
                    .append(" -> ")
                    .append(targetRef);
        }
        return builder.toString();
    }

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

        private Person author;
        private boolean crossRepository;
        private boolean forced;
        private Repository fromRepository;
        private Ref ref;
        private String targetRef;
        private Repository toRepository;

        public Builder() {
        }

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

            author = parameters.getAuthor();
            fromRepository = toRepository = parameters.getRepository();
            ref = parameters.getBranch();
            targetRef = GitRefPattern.HEADS.qualify(parameters.getTargetBranch());

            if (ref == null) {
                //If a branch wasn't specified, it means we're creating an initial commit in an empty repository.
                //FetchRequests require a branch, so we construct a placeholder using the null SHA-1. This way
                //FetchStrategies should ensure we're creating a new ref
                ref = new Branch.Builder()
                        .id(targetRef)
                        .latestCommit(ShaUtils.NULL_SHA1)
                        .build();
            }
        }

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

            this.fromRepository = parameters.getFromRepository();
            this.toRepository = parameters.getRepository();

            author = parameters.getAuthor();
            crossRepository = parameters.isCrossRepository();
            ref = parameters.getTo();
            targetRef = parameters.getTargetRef();
        }

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

            this.fromRepository = parameters.getUpstreamRepository();
            this.toRepository = parameters.getRepository();

            author = parameters.getCommitter();
            crossRepository = parameters.isCrossRepository();
            ref = parameters.getBranch();
            targetRef = parameters.getBranch().getId();
        }

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

            return self();
        }

        @Nonnull
        @Override
        public RefFetchParameters build() {
            crossRepository = !Objects.equals(fromRepository, toRepository);
            return new RefFetchParameters(this);
        }

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

            return self();
        }

        @Nonnull
        public Builder fromRepository(@Nonnull Repository value) {
            fromRepository = requireNonNull(value, "fromRepository");

            return self();
        }

        @Nonnull
        public Builder ref(@Nonnull Branch value) {
            ref = requireNonNull(value, "ref");

            return self();
        }

        @Nonnull
        public Builder targetRef(@Nonnull String value) {
            targetRef = requireNonBlank(value, "targetRef");

            return self();
        }

        @Nonnull
        public Builder toRepository(@Nonnull Repository value) {
            toRepository = requireNonNull(value, "toRepository");

            return self();
        }

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