package com.atlassian.bitbucket.scm;

import com.atlassian.bitbucket.pull.PullRequestMergeStrategy;
import com.atlassian.bitbucket.user.ApplicationUser;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static com.google.common.base.Preconditions.checkNotNull;

public class AbstractMergeCommandParameters extends AbstractCommandParameters {

    private final ApplicationUser author;
    private final boolean dryRun;
    private final String message;
    private final String strategyId;

    /**
     * @param builder a {@link AbstractBuilder} builder with properties to set on the constructed instance
     */
    protected AbstractMergeCommandParameters(AbstractBuilder<?> builder) {
        author = builder.author;
        dryRun = builder.dryRun;
        message = builder.message;
        strategyId = builder.strategyId;
    }

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

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

    /**
     * @return the {@link PullRequestMergeStrategy#getId ID} for the strategy to use
     *         to perform the merge, which may be {@code null} to use the SCM's default strategy
     * @since 4.9
     */
    @Nullable
    public String getStrategyId() {
        return strategyId;
    }

    public boolean isDryRun() {
        return dryRun;
    }

    public abstract static class AbstractBuilder<B extends AbstractBuilder<B>> {

        private ApplicationUser author;
        private boolean dryRun;
        private String message;
        private String strategyId;

        public AbstractBuilder() {
        }

        @Nonnull
        public B author(@Nonnull ApplicationUser value) {
            author = checkNotNull(value, "author");

            return self();
        }

        @Nonnull
        public B dryRun(boolean value) {
            dryRun = value;

            return self();
        }

        @Nonnull
        public B message(@Nullable String value) {
            message = value;

            return self();
        }

        /**
         * @param value the {@link PullRequestMergeStrategy#getId ID} of the merge strategy
         *              to use, or {@code null} to use the SCM's default strategy
         * @return {@code this}
         * @since 4.9
         */
        @Nonnull
        public B strategyId(@Nullable String value) {
            strategyId = value;

            return self();
        }

        @Nonnull
        protected abstract B self();

        protected void validate() {
            if (author == null) {
                throw new IllegalStateException("When performing or testing a merge, an author is required");
            }
            if (StringUtils.isBlank(author.getDisplayName())) {
                throw new IllegalStateException("When performing a merge, the author must have a display name");
            }
            if (StringUtils.isBlank(author.getEmailAddress())) {
                throw new IllegalStateException("When performing a merge, the author must have an e-mail address");
            }
            if (!dryRun && StringUtils.isEmpty(message)) {
                throw new IllegalStateException("When performing a merge, a commit message is required");
            }
        }
    }
}
