package com.atlassian.bitbucket.pull;

import com.atlassian.bitbucket.user.ApplicationUser;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Set;

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

/**
 * Request object used for {@link PullRequestService#update(PullRequestUpdateRequest) updating a pull request}.
 */
public class PullRequestUpdateRequest extends AbstractPullRequestRequest {

    private final String description;
    private final Set<String> reviewers;
    private final String title;
    private final String toBranchId;
    private final int version;

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

        description = builder.description;
        reviewers = builder.reviewers;
        title = builder.title;
        toBranchId = builder.toBranchId;
        version = builder.version;

        if (StringUtils.isBlank(title)) {
            throw new IllegalStateException("A \"title\" is required to update a pull request");
        }
    }

    /**
     * @return the new description of the pull request
     */
    @Nullable
    public String getDescription() {
        return description;
    }

    /**
     * @return the new set of reviewers of the pull request. Any current reviewer not
     *         in this set will be relegated to participant status. Any new reviewer not already
     *         a participant will start participating. Any existing reviewer in the set will be unaltered.
     *         The values are user names (as returned by {@link ApplicationUser#getName()})).
     */
    @Nonnull
    public Set<String> getReviewers() {
        return reviewers;
    }

    /**
     * @return the new title of the pull request
     */
    @Nonnull
    public String getTitle() {
        return title;
    }

    /**
     * @return the new destination branch for the pull request, or {@code null} which will be ignored
     */
    @Nullable
    public String getToBranchId() {
        return toBranchId;
    }

    /**
     * @return the expected version of the pull request
     */
    public int getVersion() {
        return version;
    }

    public static class Builder extends AbstractBuilder<Builder> {

        private final int version;

        private String description;
        private ImmutableSet<String> reviewers;
        private String title;
        private String toBranchId;

        public Builder(@Nonnull PullRequest pullRequest, int version) {
            super(pullRequest);

            this.version = version;

            reviewers = ImmutableSet.of();
        }

        public Builder(int repositoryId, long pullRequestId, int version) {
            super(repositoryId, pullRequestId);

            this.version = version;

            reviewers = ImmutableSet.of();
        }

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

        @Nonnull
        public Builder description(@Nullable String value) {
            description = value;

            return self();
        }

        @Nonnull
        public Builder reviewers(@Nonnull Iterable<String> value) {
            reviewers = ImmutableSet.copyOf(checkNotNull(value, "reviewers"));

            return self();
        }

        @Nonnull
        public Builder title(@Nonnull String value) {
            title = checkNotBlank(value, "title");

            return self();
        }

        @Nonnull
        public Builder toBranchId(@Nullable String value) {
            toBranchId = value == null ? null : checkNotBlank(value, "toBranchId");

            return self();
        }

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