package com.atlassian.bitbucket.mesh.transaction;

import com.atlassian.bitbucket.mesh.network.MeshManager;
import com.atlassian.bitbucket.mesh.network.MeshNode;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

/**
 * {@link TransactionLeader} implementation where the transaction is not coordinated with other replicas. This is
 * used when the local replica is initialized or repaired.
 */
public class LocalOnlyTransactionLeader implements TransactionLeader {

    private static final Logger log = LoggerFactory.getLogger(LocalOnlyTransactionLeader.class);

    private final MeshManager meshManager;
    private final String transactionId;

    private volatile Repository repository;

    LocalOnlyTransactionLeader(@Nonnull String transactionId, @Nonnull MeshManager meshManager) {
        this.meshManager = requireNonNull(meshManager, "meshManager");
        this.transactionId = requireNonNull(transactionId, "transactionId");
    }

    @Nonnull
    @Override
    public MeshNode getNode() {
        return meshManager.getLocalNode();
    }

    @Override
    public void onRolledBack(Throwable throwable) {
        log.debug("[{}] Transaction {} has been rolled back", repository, transactionId, throwable);
    }

    @Override
    public void onCommitted(Throwable throwable) {
        log.debug("[{}] Transaction {} has been committed", repository, transactionId, throwable);
    }

    @Nonnull
    @Override
    public Future<PrepareResult> onPrepared(@Nonnull Repository repository, @Nonnull RpcTransaction transaction) {
        this.repository = requireNonNull(repository, "repository");
        checkArgument(transactionId.equals(requireNonNull(transaction, "transaction").getId()),
                "onPrepared called for transaction %s on transaction leader for %s", transaction.getId(), transactionId);

        log.debug("[{}] Transaction {} is prepared", repository, transactionId);
        return CompletableFuture.completedFuture(PrepareResult.SUCCESS);
    }
}
