import { Transaction } from "@mysten/sui/transactions";
import { fromB64, MIST_PER_SUI, normalizeSuiObjectId } from "@mysten/sui/utils";
import { SuiClient } from "@mysten/sui/client";
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";

import { execSync } from "child_process";
import { SuiPriceFeedContract } from "@pythnetwork/contract-manager";

export function buildForBytecodeAndDigest(packagePath: string) {
  const buildOutput: {
    modules: string[];
    dependencies: string[];
    digest: number[];
  } = JSON.parse(
    execSync(
      `sui move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`,
      { encoding: "utf-8" }
    )
  );
  return {
    modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))),
    dependencies: buildOutput.dependencies.map((d: string) =>
      normalizeSuiObjectId(d)
    ),
    digest: Buffer.from(buildOutput.digest),
  };
}

export async function upgradePyth(
  keypair: Ed25519Keypair,
  provider: SuiClient,
  modules: number[][],
  dependencies: string[],
  signedVaa: Buffer,
  contract: SuiPriceFeedContract
) {
  const pythPackage = await contract.getPackageId(contract.stateId);

  const tx = new Transaction();

  const verificationReceipt = await contract.getVaaVerificationReceipt(
    tx as any,
    pythPackage,
    signedVaa
  );

  // Authorize upgrade.
  const [upgradeTicket] = tx.moveCall({
    target: `${pythPackage}::contract_upgrade::authorize_upgrade`,
    arguments: [tx.object(contract.stateId), verificationReceipt as any],
  });

  // Build and generate modules and dependencies for upgrade.
  const [upgradeReceipt] = tx.upgrade({
    modules,
    dependencies,
    package: pythPackage,
    ticket: upgradeTicket,
  });

  // Commit upgrade.
  tx.moveCall({
    target: `${pythPackage}::contract_upgrade::commit_upgrade`,
    arguments: [tx.object(contract.stateId), upgradeReceipt],
  });

  tx.setGasBudget(MIST_PER_SUI / 4n); // 0.25 SUI

  return provider.signAndExecuteTransaction({
    signer: keypair,
    transaction: tx,
    options: {
      showEffects: true,
      showEvents: true,
    },
  });
}

export async function migratePyth(
  keypair: Ed25519Keypair,
  provider: SuiClient,
  signedUpgradeVaa: Buffer,
  contract: SuiPriceFeedContract,
  pythPackageOld: string
) {
  const pythPackage = await contract.getPackageId(contract.stateId);
  const tx = new Transaction();
  // The pyth package version is not updated yet, therefore we can not get the verification receipts from the new
  // package yet. We need to use the old package id to get the verification receipt in this transaction and then submit
  // it to the migrate function in the new package!
  const verificationReceipt = await contract.getVaaVerificationReceipt(
    tx,
    pythPackageOld,
    signedUpgradeVaa
  );
  tx.moveCall({
    target: `${pythPackage}::migrate::migrate`,
    arguments: [tx.object(contract.stateId), verificationReceipt as any],
  });

  tx.setGasBudget(MIST_PER_SUI / 10n); //0.1 SUI

  return provider.signAndExecuteTransaction({
    signer: keypair,
    transaction: tx,
    options: {
      showEffects: true,
      showEvents: true,
    },
  });
}
