import { LCDClient, MnemonicKey } from "@terra-money/terra.js";
import {
  MsgInstantiateContract,
  MsgStoreCode,
} from "@terra-money/terra.js";
import { readFileSync } from "fs";
import { Bech32, toHex } from "@cosmjs/encoding";
import { zeroPad } from "ethers/lib/utils.js";
import axios from "axios";
import yargs from "yargs";
import {hideBin} from "yargs/helpers";

export const TERRA_GAS_PRICES_URL = "https://fcd.terra.dev/v1/txs/gas_prices";

const argv = yargs(hideBin(process.argv))
  .option('network', {
    description: 'Which network to deploy to',
    choices: ['mainnet', 'testnet', 'devnet'],
    required: true
  })
  .option('artifact', {
    description: 'Which WASM file to deploy',
    type: 'string',
    required: true
  })
  .option('mnemonic', {
    description: 'Mnemonic (private key)',
    type: 'string',
    required: true
  })
  .help()
  .alias('help', 'h').argv;

const artifact = argv.artifact;

/* Set up terra client & wallet */

const terra_host =
      argv.network === "mainnet"
    ? {
        URL: "https://lcd.terra.dev",
        chainID: "columbus-5",
        name: "mainnet",
      }
    : argv.network === "testnet"
    ? {
        URL: "https://bombay-lcd.terra.dev",
        chainID: "bombay-12",
        name: "testnet",
      }
    : {
        URL: "http://localhost:1317",
        chainID: "localterra",
      };

const lcd = new LCDClient(terra_host);

const feeDenoms = ["uluna"];

const gasPrices = await axios
  .get(TERRA_GAS_PRICES_URL)
  .then((result) => result.data);

const wallet = lcd.wallet(
  new MnemonicKey({
    mnemonic: argv.mnemonic
  })
);

await wallet.sequence();

/* Deploy artifacts */

let codeId;
const contract_bytes = readFileSync(artifact);
console.log(`Storing WASM: ${artifact} (${contract_bytes.length} bytes)`);

const store_code = new MsgStoreCode(
  wallet.key.accAddress,
  contract_bytes.toString("base64")
);

const feeEstimate = await lcd.tx.estimateFee(
  wallet.key.accAddress,
  [store_code],
  {
    memo: "",
    feeDenoms,
    gasPrices,
  }
);

console.log("Deploy fee: ", feeEstimate.amount.toString());

const tx = await wallet.createAndSignTx({
  msgs: [store_code],
  memo: "",
  feeDenoms,
  gasPrices,
  fee: feeEstimate,
});

const rs = await lcd.tx.broadcast(tx);
const ci = /"code_id","value":"([^"]+)/gm.exec(rs.raw_log)[1];
codeId = parseInt(ci);

console.log("Code ID: ", codeId);

/* Instantiate contracts.
 *
 * We instantiate the core contracts here (i.e. wormhole itself and the bridge contracts).
 * The wrapped asset contracts don't need to be instantiated here, because those
 * will be instantiated by the on-chain bridge contracts on demand.
 * */
async function instantiate(codeId, inst_msg) {
  var address;
  await wallet
    .createAndSignTx({
      msgs: [
        new MsgInstantiateContract(
          wallet.key.accAddress,
          wallet.key.accAddress,
          codeId,
          inst_msg
        ),
      ],
      memo: "",
    })
    .then((tx) => lcd.tx.broadcast(tx))
    .then((rs) => {
      address = /"contract_address","value":"([^"]+)/gm.exec(rs.raw_log)[1];
    });
  console.log(`Instantiated ${contract} at ${address} (${convert_terra_address_to_hex(address)})`);
  return address;
}

// example usage of instantiate:

// const contractAddress = await instantiate("wormhole.wasm", {
//   gov_chain: govChain,
//   gov_address: Buffer.from(govAddress, "hex").toString("base64"),
//   guardian_set_expirity: 86400,
//   initial_guardian_set: {
//     addresses: [
//       {
//         bytes: Buffer.from(
//           "beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe",
//           "hex"
//         ).toString("base64"),
//       },
//     ],
//     expiration_time: 0,
//   },
// });


// Terra addresses are "human-readable", but for cross-chain registrations, we
// want the "canonical" version
function convert_terra_address_to_hex(human_addr) {
  return "0x" + toHex(zeroPad(Bech32.decode(human_addr).data, 32));
}
