/*
 * Copyright 2019 Web3 Labs Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package org.web3j.utils;

import java.io.IOException;

import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;

/** Revert reason extraction and retrieval functions. */
public class RevertReasonExtractor {

    public static final String MISSING_REASON = "N/A";

    /**
     * Extracts the error reason of a reverted transaction (if one exists and enabled).
     *
     * @param transactionReceipt the reverted transaction receipt
     * @param data the reverted transaction data
     * @param web3j Web3j instance
     * @param revertReasonCallEnabled flag of reason retrieval via additional call
     * @return the reverted transaction error reason if exists or null otherwise
     * @throws IOException if the call to the node fails
     */
    public static String extractRevertReason(
            TransactionReceipt transactionReceipt,
            String data,
            Web3j web3j,
            Boolean revertReasonCallEnabled)
            throws IOException {

        if (transactionReceipt.getRevertReason() != null) {
            return transactionReceipt.getRevertReason();
        } else if (revertReasonCallEnabled) {
            String revertReason = retrieveRevertReason(transactionReceipt, data, web3j);
            if (revertReason != null) {
                transactionReceipt.setRevertReason(revertReason);
                return revertReason;
            }
        }
        return MISSING_REASON;
    }

    /**
     * Retrieves the error reason of a reverted transaction (if one exists).
     *
     * @param transactionReceipt the reverted transaction receipt
     * @param data the reverted transaction data
     * @param web3j Web3j instance
     * @return the reverted transaction error reason if exists or null otherwise
     * @throws IOException if the call to the node fails
     */
    public static String retrieveRevertReason(
            TransactionReceipt transactionReceipt, String data, Web3j web3j) throws IOException {

        if (transactionReceipt.getBlockNumber() == null) {
            return null;
        }
        return web3j.ethCall(
                        Transaction.createEthCallTransaction(
                                transactionReceipt.getFrom(), transactionReceipt.getTo(), data),
                        DefaultBlockParameter.valueOf(transactionReceipt.getBlockNumber()))
                .send()
                .getRevertReason();
    }
}