package com.atlassian.bitbucket.concurrent;

import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.util.Operation;
import com.atlassian.bitbucket.util.UncheckedOperation;

import javax.annotation.Nonnull;

/**
 * A reusable component for locking a {@link Repository repository} while an {@link Operation operation} is performed.
 * <p>
 * Separate instances of this interface <i>are not related</i>. Locking a {@link Repository repository} in one instance
 * does not lock it in any other. The intention is to allow grouping {@link Operation operations} that perform similar
 * processing, or rely on the same repository data, so that only one is running on a given repository at a time, using
 * multiple locks when different bands of processing can be performed concurrently.
 * <p>
 * <b>Note</b>: When used by plugin developers, no instance of this lock can ever prevent the <i>host application</i>
 * from performing any of its own processing on <i>any</i> aspect of a repository.
 *
 * @see PullRequestLock
 * @see UncheckedOperation UncheckedOperation
 */
public interface RepositoryLock {

    /**
     * Performs the provided {@link Operation operation} while holding a lock on the specified {@link Repository
     * repository}.
     * <p>
     * <b>Note</b>: Locking a repository by ID or by {@link Repository} instance acquires the same underlying lock.
     *
     * @param repositoryId the {@link Repository#getId() ID} of the repository to lock
     * @param operation    the {@link Operation operation} to perform <i>while holding the lock</i>
     * @return the result of {@link Operation#perform() performing} the operation
     * @throws E if the {@link Operation operation} fails
     */
    <T, E extends Exception> T withLock(int repositoryId, @Nonnull Operation<T, E> operation) throws E;

    /**
     * Performs the provided {@link Operation operation} while holding a lock on the specified {@link Repository
     * repository}.
     * <p>
     * <b>Note</b>: Locking a repository by ID or by {@link Repository} instance acquires the same underlying lock.
     *
     * @param repository the {@link Repository repository} to lock
     * @param operation  the {@link Operation operation} to perform <i>while holding the lock</i>
     * @return the result of {@link Operation#perform() performing} the operation
     * @throws E if the {@link Operation operation} fails
     */
    <T, E extends Exception> T withLock(@Nonnull Repository repository, @Nonnull Operation<T, E> operation) throws E;
}
