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

import com.atlassian.bitbucket.mesh.git.AbstractParameters;
import com.atlassian.bitbucket.mesh.git.pull.PullRequest;
import com.atlassian.bitbucket.mesh.git.pull.PullRequestRef;
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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Objects;

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

public class MergeParameters extends AbstractParameters {

    private final boolean allowUnrelated;
    private final Person author;
    private final Person committer;
    private final Ref from;
    private final Repository fromRepository;
    private final int log;
    private final int maxMessageLength;
    private final GitMergeStrategy mergeStrategy;
    private final String message;
    private final Repository repository;
    private final String targetRef;
    private final Ref to;

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

        allowUnrelated = builder.allowUnrelated;
        author = requireNonNull(builder.author, "author");
        committer = requireNonNull(builder.committer, "committer");
        from = requireNonNull(builder.from, "from");
        fromRepository = firstNonNull(builder.fromRepository, requireNonNull(builder.repository, "repository"));
        log = builder.log;
        maxMessageLength = log > 0 ? builder.maxMessageLength : 0;
        mergeStrategy = requireNonNull(builder.mergeStrategy, "mergeStrategy");
        message = requireNonNull(builder.message);
        repository = builder.repository;
        targetRef = firstNonNull(builder.targetRef, requireNonNull(builder.to, "to").getId());
        to = builder.to; //Null-checked on fromRepository above
    }

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

    @Nonnull
    public Person getCommitter() {
        return committer;
    }

    @Nonnull
    public Ref getFrom() {
        return from;
    }

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

    public int getLog() {
        return log;
    }

    public int getMaxMessageLength() {
        return maxMessageLength;
    }

    @Nonnull
    public GitMergeStrategy getMergeStrategy() {
        return mergeStrategy;
    }

    @Nonnull
    public String getMessage() {
        return message;
    }

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

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

    @Nonnull
    public Ref getTo() {
        return to;
    }

    public boolean isAllowUnrelated() {
        return allowUnrelated;
    }

    public boolean isCrossRepository() {
        return !Objects.equals(repository, fromRepository);
    }

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

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

        private final Repository repository;

        private boolean allowUnrelated;
        private Person author;
        private Person committer;
        private Ref from;
        private Repository fromRepository;
        private int log;
        private int maxMessageLength;
        private GitMergeStrategy mergeStrategy;
        private String message;
        private String targetRef;
        private Ref to;

        public Builder(@Nonnull PullRequest pullRequest) {
            PullRequestRef fromRef = requireNonNull(pullRequest, "pullRequest").getFromRef();
            PullRequestRef toRef = pullRequest.getToRef();

            from = fromRef;
            fromRepository = fromRef.getRepository();
            repository = toRef.getRepository();
            targetRef = toRef.getId();
            to = toRef;
        }

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

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

            return this;
        }

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

            return this;
        }

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

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

            return this;
        }

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

            return this;
        }

        @Nonnull
        public Builder fromRepository(@Nullable Repository value) {
            fromRepository = value;
            return this;
        }

        @Nonnull
        public Builder log(int value) {
            log = value;

            return this;
        }

        @Nonnull
        public Builder maxMessageLength(int value) {
            maxMessageLength = value;

            return this;
        }

        @Nonnull
        public Builder mergeStrategy(@Nonnull GitMergeStrategy value) {
            mergeStrategy = requireNonNull(value, "mergeStrategy");

            return this;
        }

        @Nonnull
        public Builder message(@Nonnull String value) {
            message = requireNonNull(value, "message");

            return this;
        }

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

            return this;
        }

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

            return this;
        }

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