package leigh.chain;

import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import org.bouncycastle.util.encoders.Hex;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;

/**
 * This class implements convenience methods for interacting with the LeighChain.
 *
 * @author Alex Leigh
 */
public class LeighChain {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(LeighChain.class);

    /**
     * Submit a zero-value eth 1.0 transaction to the LeighChain via geth. The gas limit and
     * price are set to 21,000.
     * <p>
     * If successful, the transaction hash is returned. This hash can be used to monitor
     * the process of the transaction to determine if it is eventually mined into a block.
     */
    public static String submit(String gethUrl, String wallet, String walletPw,
                                String destAddr, String data)
            throws CipherException, IOException {
        Web3j web3 = Web3j.build(new HttpService(gethUrl));

        Credentials credentials = WalletUtils.loadCredentials(walletPw, wallet);

        BigInteger gasLimit = BigInteger.valueOf(100000);
        BigInteger gasPrice = Convert.toWei("100", Convert.Unit.GWEI).toBigInteger();
        EthGetTransactionCount ethGetTransactionCount = web3.ethGetTransactionCount(credentials.getAddress(),
                DefaultBlockParameterName.LATEST).send();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();

        // There is basically a race here, since if something else publishes, we'll duplicate
        // the nonce. There is basically nothing to be done about this.

        String encodedData = Hex.toHexString(data.getBytes(StandardCharsets.UTF_8));

        logger.info("Created hex string: {}", encodedData);

        RawTransaction rawTransaction = RawTransaction.createTransaction(
                nonce,
                gasPrice,
                gasLimit,
                destAddr,
                BigInteger.valueOf(0), encodedData);

        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);

        // Convert it to Hexadecimal String to be sent to the node
        String hexValue = Numeric.toHexString(signedMessage);
        EthSendTransaction ethSendTransaction = web3.ethSendRawTransaction(hexValue).send();

        String txHash = ethSendTransaction.getTransactionHash();

        logger.info("Transaction posted. [hash: {}]", txHash);

        return txHash;
    }
}
