/**
 * This script deploys the receiver contracts on all the chains and creates a governance proposal to update the
 * wormhole addresses to the deployed receiver contracts.
 */

import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import {
  DefaultStore,
  EvmChain,
  loadHotWallet,
  WormholeEvmContract,
} from "contract_manager";
import Web3 from "web3";
import { CHAINS } from "xc_admin_common";
import * as fs from "fs";

const { getDefaultConfig } = require("./contractManagerConfig");

const parser = yargs(hideBin(process.argv))
  .usage(
    "Usage: $0 --contracts <path-to-contract-json-folder> --network <contract_id> --private-key <private-key> --ops-wallet <ops-wallet>"
  )
  .options({
    contract: {
      type: "string",
      demandOption: true,
      desc: "Path to the contract json file containing abi and bytecode",
    },
    network: {
      type: "string",
      demandOption: true,
      choices: ["testnet", "mainnet"],
      desc: "The network to deploy the contract on",
    },
    "private-key": {
      type: "string",
      demandOption: true,
      desc: "Private key to sign the transactions. Hex format, without 0x prefix.",
    },
    "ops-wallet": {
      type: "string",
      demandOption: true,
      desc: "Path to operations wallet json file",
    },
  });

async function memoize(key: string, fn: () => Promise<any>) {
  const path = `./cache/${key}.json`;
  if (fs.existsSync(path)) {
    return JSON.parse(fs.readFileSync(path).toString());
  }
  const result = await fn();
  fs.writeFileSync(path, JSON.stringify(result));
  return result;
}

async function main() {
  const argv = await parser.argv;
  const privateKey = argv["private-key"];
  const network = argv["network"];
  const setupInfo = require(argv["contract"] + "/ReceiverSetup.json");
  const implementationInfo = require(argv["contract"] +
    "/ReceiverImplementation.json");
  const receiverInfo = require(argv["contract"] + "/WormholeReceiver.json");

  const payloads: Buffer[] = [];
  for (const chain of Object.values(DefaultStore.chains)) {
    if (
      chain instanceof EvmChain &&
      chain.isMainnet() === (network === "mainnet")
    ) {
      if (chain.wormholeChainName === "zksync") continue; // deploy zksync receiver separately
      const {
        wormholeGovernanceChainId,
        wormholeGovernanceContract,
        wormholeInitialSigners,
      } = getDefaultConfig(chain.getId());
      console.log(chain.getId());
      const address = await memoize(chain.getId(), async () => {
        const setupAddress = await chain.deploy(
          privateKey,
          setupInfo.abi,
          setupInfo.bytecode,
          []
        );
        console.log("setupAddress", setupAddress);
        const implementationAddress = await chain.deploy(
          privateKey,
          implementationInfo.abi,
          implementationInfo.bytecode,
          []
        );
        console.log("implementationAddress", implementationAddress);
        const web3 = new Web3();
        const setup = new web3.eth.Contract(setupInfo.abi, setupAddress);
        const initData = setup.methods
          .setup(
            implementationAddress,
            wormholeInitialSigners,
            CHAINS[chain.wormholeChainName],
            wormholeGovernanceChainId,
            wormholeGovernanceContract
          )
          .encodeABI();

        // deploy proxy
        const receiverAddress = await chain.deploy(
          privateKey,
          receiverInfo.abi,
          receiverInfo.bytecode,
          [setupAddress, initData]
        );
        const contract = new WormholeEvmContract(chain, receiverAddress);
        console.log("receiverAddress", receiverAddress);
        await contract.syncMainnetGuardianSets(privateKey);
        console.log("synced");
        return contract.address;
      });
      const payload = chain.generateGovernanceSetWormholeAddressPayload(
        address.replace("0x", "")
      );
      payloads.push(payload);
    }
  }
  let vaultName;
  if (network === "mainnet") {
    vaultName = "mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj";
  } else {
    vaultName = "devnet_6baWtW1zTUVMSJHJQVxDUXWzqrQeYBr6mu31j3bTKwY3";
  }
  const vault = DefaultStore.vaults[vaultName];
  vault.connect(await loadHotWallet(argv["ops-wallet"]));
  await vault.proposeWormholeMessage(payloads);
}

main();
