package com.atlassian.bitbucket.scm.git.command;

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.repository.Repository;

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

import static java.util.Objects.requireNonNull;

/**
 * Provides settings which control how {@link CommitReader} populates {@code Commit}s.
 *
 * @since 4.5
 */
public class CommitReaderSettings {

    /**
     * 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 int maxMessageLength;
    private final CommitMessageMode messageMode;
    private final Repository repository;

    private CommitReaderSettings(Builder builder) {
        // All values are normalized to at least -1
        maxMessageLength = Math.max(UNLIMITED_MESSAGE_LENGTH, builder.maxMessageLength);
        // If the message length is zero, we do not want messages regardless of the specified mode. Rather than
        // getting the message and discarding it, we should just not get the message in the first place
        messageMode = builder.maxMessageLength == 0 ? CommitMessageMode.NONE : builder.messageMode;
        repository = builder.repository;
    }

    /**
     * @return the maximum length to read for {@link Commit#getMessage() commit messages}; if set, messages
     *         longer than this will be truncated
     */
    public int getMaxMessageLength() {
        return maxMessageLength;
    }

    /**
     * @return the message mode for the reader
     * @since 4.6
     */
    @Nonnull
    public CommitMessageMode getMessageMode() {
        return messageMode;
    }

    /**
     * @return the repository the commits are being read from, or {@code null} if one was not set
     */
    @Nullable
    public Repository getRepository() {
        return repository;
    }

    /**
     * @return {@code true} if the commit message should be read in full; otherwise, {@code false} to indicate
     *         there is a {@link #getMaxMessageLength() max message length}
     */
    public boolean isMessageLengthUnlimited() {
        return maxMessageLength == UNLIMITED_MESSAGE_LENGTH;
    }

    /**
     * @return {@code true} if {@link Commit#getMessage() commit messages} should be read; otherwise, {@code false}
     *         to omit them
     */
    public boolean isWithMessages() {
        return messageMode != CommitMessageMode.NONE;
    }

    public static class Builder {

        private int maxMessageLength;
        private CommitMessageMode messageMode;
        private Repository repository;

        public Builder() {
            maxMessageLength = UNLIMITED_MESSAGE_LENGTH;
            messageMode = CommitMessageMode.RAW_BODY;
        }

        /**
         * @return {@link CommitReaderSettings} based on the assembled values provided to this builder
         */
        @Nonnull
        public CommitReaderSettings build() {
            return new CommitReaderSettings(this);
        }

        /**
         * @param value the maximum length for commit messages, or {@link #UNLIMITED_MESSAGE_LENGTH} (default) to
         *              read the full message without truncation
         * @return {@code this}
         */
        @Nonnull
        public Builder maxMessageLength(int value) {
            maxMessageLength = value;

            return this;
        }

        /**
         * @param value the message mode for the reader, which may not be {@code null}
         * @return {@code this}
         * @throws NullPointerException if the provided {@code value} is {@code null}
         * @since 4.6
         */
        @Nonnull
        public Builder messageMode(@Nonnull CommitMessageMode value) {
            messageMode = requireNonNull(value, "messageMode");

            return this;
        }

        /**
         * @param value the repository to set on commits
         * @return {@code this}
         */
        @Nonnull
        public Builder repository(@Nullable Repository value) {
            repository = value;

            return this;
        }

        /**
         * @param value {@code true} to read commit messages; otherwise, {@code false} to omit them
         * @return {@code this}
         */
        @Nonnull
        public Builder withMessages(boolean value) {
            if (value) {
                // For withMessages(true), if the messageMode is NONE, set it back to RAW_BODY (the default mode).
                // Otherwise, if messageMode is any other value already, ignore the call.
                if (messageMode == CommitMessageMode.NONE) {
                    messageMode = CommitMessageMode.RAW_BODY;
                }
            } else {
                // For withMessages(false), set messageMode to NONE
                messageMode = CommitMessageMode.NONE;
            }

            return this;
        }
    }
}
