package com.atlassian.bitbucket.mesh.transaction;

import com.atlassian.bitbucket.mesh.network.MeshNode;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcStreamChunk;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcTransaction;
import com.atlassian.bitbucket.mesh.util.SatiableConsumer;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.Future;

import static java.util.Optional.empty;

public interface TransactionLeader {

    /**
     * @return the directory where incoming staged objects are located; only defined for follower transactions
     */
    @Nonnull
    default Optional<Path> getIncomingDir() {
        return empty();
    }

    /**
     * @return the {@link MeshNode} node that is the transaction leader
     */
    @Nonnull
    MeshNode getNode();

    /**
     * Starts a new writer to send a pack of objects to follower transactions. Only defined for local
     * transaction leaders.
     * <p>
     * The caller should use the returned {@link SatiableConsumer} to send the pack contents to the followers. The
     * returned {@link SatiableConsumer} returns {@code true} when all follower transactions have signalled that
     * they're not interested in more data.
     *
     * @param repository the repository for which a pack is to be sent
     * @return a consumer that replicates the stream chunks to all follower transactions
     */
    @Nonnull
    default Optional<SatiableConsumer<RpcStreamChunk>> newPackWriter(@Nonnull Repository repository) {
        return empty();
    }

    /**
     * Signals to the transaction leader that this node has committed the local transaction
     *
     * @param throwable when non-null, indicates that the local transaction was not cleanly committed
     */
    void onCommitted(@Nullable Throwable throwable);

    /**
     * Signals to the transaction leader that this node has prepared the local transaction
     *
     * @param repository the repository the transaction applies to
     * @param transaction the transaction
     * @return the outcome the transaction leader has decided on
     */
    @Nonnull
    Future<PrepareResult> onPrepared(@Nonnull Repository repository, @Nonnull RpcTransaction transaction);

    /**
     * Signals to the transaction leader that this node has rolled back the local transaction
     *
     * @param throwable when non-null, indicates that the local transaction was not cleanly rolled back
     */
    void onRolledBack(@Nullable Throwable throwable);
}
