package com.atlassian.bitbucket.mesh.transaction;

import com.atlassian.bitbucket.mesh.AbstractStatusException;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGitError;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGitErrorUtils;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcTransactionCommitFailed;
import io.grpc.Metadata;
import io.grpc.Status;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import static java.util.Objects.requireNonNull;

/**
 * Exception that is thrown when a replica fails to commit a {@link Transaction} when instructed to do so by
 * the transaction leader.
 */
public class TransactionCommitFailedException extends AbstractStatusException {

    private final RpcTransactionCommitFailed.Reason reason;

    public TransactionCommitFailedException(@Nullable String message,
                                            @Nonnull RpcTransactionCommitFailed.Reason reason) {
        this(message, reason, null);
    }

    public TransactionCommitFailedException(@Nullable String message,
                                            @Nonnull RpcTransactionCommitFailed.Reason reason,
                                            @Nullable Throwable cause) {
        super(message, cause, Status.ABORTED);

        this.reason = requireNonNull(reason, "reason");
    }

    @Nonnull
    public RpcTransactionCommitFailed.Reason getReason() {
        return reason;
    }

    @Nonnull
    public RpcTransactionCommitFailed toCommitFailed() {
        return RpcTransactionCommitFailed.newBuilder()
                .setReason(reason)
                .build();
    }

    @Override
    protected Metadata getTrailers() {
        return RpcGitErrorUtils.toMetadata(RpcGitError.newBuilder()
                .setTransactionCommitFailed(toCommitFailed())
                .build());
    }
}
