package com.atlassian.bitbucket.event.pull;

import com.atlassian.bitbucket.event.CancelableEvent;
import com.atlassian.bitbucket.event.annotation.TransactionAware;
import com.atlassian.bitbucket.i18n.KeyedMessage;
import com.atlassian.bitbucket.pull.PullRequest;
import com.atlassian.bitbucket.pull.PullRequestAction;
import com.atlassian.bitbucket.pull.PullRequestService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.util.CancelState;

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

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

/**
 * Raised just before a {@link PullRequest pull request} is {@link PullRequestOpenedEvent opened}. This event is
 * synchronous, allowing listeners to perform operations in the same database transaction where the pull request
 * will be opened.
 * <p>
 * This event is {@link CancelableEvent cancelable}. A listener may prevent the pull request from being opened by
 * {@link #cancel(KeyedMessage) canceling} this event. Throwing an exception <i>will not</i> prevent the pull request
 * from being opened; the exception will be logged and ignored.
 * <p>
 * Note: The pull request attached to the event will <i>not</i> have an {@link PullRequest#getId() ID}, as it has not
 * been opened yet. Any listener which would like to create associations based on that ID must listen for the
 * {@link PullRequestOpenedEvent} instead, which will include it (but will not run in the same transaction).
 *
 * @see PullRequestService#create(String, String, java.util.Set, Repository, String, Repository, String)
 */
@TransactionAware(TransactionAware.When.IMMEDIATE) //Override transactionality from PullRequestEvent
public class PullRequestOpenRequestedEvent extends PullRequestEvent implements CancelableEvent {

    private final CancelState cancelState;
    private final Set<ApplicationUser> reviewers;

    public PullRequestOpenRequestedEvent(@Nonnull Object source, @Nonnull PullRequest pullRequest,
                                         @Nonnull Set<ApplicationUser> reviewers, @Nonnull CancelState cancelState) {
        super(source, pullRequest, PullRequestAction.OPENED);

        this.cancelState = checkNotNull(cancelState, "cancelState");
        this.reviewers = Collections.unmodifiableSet(checkNotNull(reviewers, "reviewers"));
    }

    /**
     * Cancels pull request creation, providing a message explaining why.
     *
     * @param message a descriptive message explaining why the operation has been canceled
     */
    @Override
    public void cancel(@Nonnull KeyedMessage message) {
        cancelState.cancel(message);
    }

    /**
     * Retrieves a set containing the {@link ApplicationUser users} who have been specified as reviewers for the pull request
     * that is about to be created.
     * <p>
     * The returned set is <i>immutable</i>. Listeners <i>cannot</i> add reviewers using this event. The set may be
     * empty, if no reviewers have been requested, but it will never be {@code null}.
     *
     * @return a set containing 0 or more reviewers for the pull request
     */
    @Nonnull
    public Set<ApplicationUser> getReviewers() {
        return reviewers;
    }

    /**
     * Retrieves a flag indicating whether pull request creation has already been canceled by another listener.
     *
     * @return {@code true} if another listener has already canceled pull request creation; otherwise, {@code false}
     */
    @Override
    public boolean isCanceled() {
        return cancelState.isCanceled();
    }
}
