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.RpcTransactionRollbackFailed;
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 roll back a {@link Transaction} when instructed to do so by
 * the transaction leader.
 */
public class TransactionRollbackFailedException extends AbstractStatusException {

    private final RpcTransactionRollbackFailed.Reason reason;

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

    public TransactionRollbackFailedException(@Nullable String message,
                                              @Nonnull RpcTransactionRollbackFailed.Reason reason,
                                              @Nullable Throwable cause) {
        super(message, cause, Status.INTERNAL);

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

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

    @Nonnull
    public RpcTransactionRollbackFailed toRollbackFailed() {
        return RpcTransactionRollbackFailed.newBuilder()
                .setReason(reason)
                .build();
    }

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