package com.atlassian.bitbucket.commit;

import com.atlassian.bitbucket.repository.Branch;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.Tag;
import com.atlassian.bitbucket.util.BuilderSupport;

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

import static java.util.Objects.requireNonNull;

/**
 * A base class for requests which take a single {@link #getCommitId commit ID} and {@link #getPath path}.
 *
 * @since 4.6
 */
public abstract class AbstractCommitRequest {

    /**
     * A symbolic constant for {@link #getMaxMessageLength() max message length} which means the full commit
     * message should be read regardless of length.
     */
    public static final int UNLIMITED_MESSAGE_LENGTH = -1;

    private final String commitId;
    private final int maxMessageLength;
    private final String path;
    private final Repository repository;

    protected AbstractCommitRequest(AbstractCommitRequestBuilder<?> builder) {
        commitId = builder.commitId;
        path = builder.path;
        repository = builder.repository;
        maxMessageLength = builder.maxMessageLength;
    }

    /**
     * @return the {@link Commit#getId commit ID}, which may also be specified using a {@link Branch#getId branch ID}
     *         or {@link Tag#getId() tag ID}
     */
    @Nonnull
    public String getCommitId() {
        return commitId;
    }

    /**
     * @return the max message length to be processed for any of the commits, which may be
     *         {@link #UNLIMITED_MESSAGE_LENGTH} to include the full commit message regardless of length
     */
    public int getMaxMessageLength() {
        return maxMessageLength;
    }

    /**
     * Retrieves an optional path which is used to control which commit is returned. When a path is provided, the
     * {@link #getCommitId() commit ID} is used as a <i>starting point</i> and may not actually be the commit that
     * ultimately gets returned. Instead, the first commit which modifies the specified path will be returned. If
     * none of the commits in the ancestry of the starting commit modify the specified path, no commit is returned.
     *
     * @return an optional path
     */
    @Nullable
    public String getPath() {
        return path;
    }

    /**
     * @return the repository the commit should be loaded from
     */
    @Nonnull
    public Repository getRepository() {
        return repository;
    }

    public abstract static class AbstractCommitRequestBuilder<B extends AbstractCommitRequestBuilder<B>>
            extends BuilderSupport {

        private final String commitId;
        private final Repository repository;
        private int maxMessageLength;
        private String path;

        protected AbstractCommitRequestBuilder(@Nonnull Repository repository, @Nonnull String commitId) {
            this.commitId = requireNonBlank(commitId, "commitId");
            this.repository = requireNonNull(repository, "repository");

            maxMessageLength = UNLIMITED_MESSAGE_LENGTH;
        }

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

            return self();
        }

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

            return self();
        }

        protected abstract B self();
    }
}
