package com.atlassian.bitbucket.repository.sync;

import com.atlassian.bitbucket.repository.Repository;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;

import javax.annotation.Nonnull;
import java.util.Map;

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

/**
 * Encapsulates a request to manually synchronize a {@link RejectedRef ref} which can no longer be automatically
 * synchronized with its upstream.
 * <p>
 * Some {@link RefSyncAction actions} support additional {@link #getContext() context} values, allowing greater
 * control over the work they perform. For example, the {@link RefSyncAction#MERGE MERGE} action supports a
 * "commitMessage" context value, which can be used to specify an explicit commit message for the merge. Such
 * context values are described in the documentation for the action.
 * <p>
 * <b>Note</b>: The {@link #getRefId() ref ID} is required to be <i>fully-qualified</i>. Display IDs will <i>not</i>
 * be accepted, even if they are not ambiguous within the repository.
 */
public class RefSyncRequest {

    private final RefSyncAction action;
    private final Map<String, Object> context;
    private final String refId;
    private final Repository repository;

    private RefSyncRequest(Repository repository, String refId, RefSyncAction action, Map<String, Object> context) {
        this.action = action;
        this.context = context;
        this.repository = repository;
        this.refId = refId;
    }

    @Nonnull
    public RefSyncAction getAction() {
        return action;
    }

    @Nonnull
    public Map<String, Object> getContext() {
        return context;
    }

    @Nonnull
    public String getRefId() {
        return refId;
    }

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

    public static class Builder {

        private final ImmutableMap.Builder<String, Object> context;

        private RefSyncAction action;
        private String refId;
        private Repository repository;

        public Builder() {
            context = ImmutableMap.builder();
        }

        @Nonnull
        public RefSyncRequest build() {
            checkState(action != null, "The synchronization action to perform is required");
            checkState(StringUtils.isNotBlank(refId), "The ref to synchronize is required");
            checkState(repository != null, "The repository containing the ref to be synchronized is required");

            return new RefSyncRequest(repository, refId, action, context.build());
        }

        @Nonnull
        public Builder action(@Nonnull RefSyncAction value) {
            action = checkNotNull(value, "action");

            return this;
        }

        @Nonnull
        public Builder context(@Nonnull String key, @Nonnull Object value) {
            context.put(key, value);

            return this;
        }

        @Nonnull
        public Builder context(@Nonnull Map<String, Object> value) {
            context.putAll(value);

            return this;
        }

        @Nonnull
        public Builder refId(@Nonnull String value) {
            checkNotNull(value, "refId");
            checkArgument(StringUtils.isNotBlank(value), "The provided refId is blank");

            refId = value;

            return this;
        }

        @Nonnull
        public Builder repository(@Nonnull Repository value) {
            repository = checkNotNull(value, "repository");

            return this;
        }
    }
}
