// deploy.js is a simple deploy script to initialize a program. This is run
// immediately after a deploy.

const serumCmn = require("@project-serum/common");
const anchor = require("@project-serum/anchor");
const PublicKey = anchor.web3.PublicKey;

module.exports = async function (provider) {
  // Configure client to use the provider.
  anchor.setProvider(provider);

  // Setup genesis state.
  const registrarConfigs = await genesis(provider);

  // Program clients.
  const lockup = anchor.workspace.Lockup;
  const registry = anchor.workspace.Registry;

  // Registry state constructor.
  await registry.state.rpc.new({
    accounts: {
      lockupProgram: lockup.programId,
    },
  });

  // Lockup state constructor.
  await lockup.state.rpc.new({
    accounts: {
      authority: provider.wallet.publicKey,
    },
  });

  // Delete the default whitelist entries.
  const defaultEntry = { programId: new anchor.web3.PublicKey.default() };
  await lockup.state.rpc.whitelistDelete(defaultEntry, {
    accounts: {
      authority: provider.wallet.publicKey,
    },
  });

  // Whitelist the registry.
  await lockup.state.rpc.whitelistAdd(
    { programId: registry.programId },
    {
      accounts: {
        authority: provider.wallet.publicKey,
      },
    }
  );

  // Initialize all registrars.
  const cfgKeys = Object.keys(registrarConfigs);
  for (let k = 0; k < cfgKeys.length; k += 1) {
    let r = registrarConfigs[cfgKeys[k]];
    const registrar = await registrarInit(
      registry,
      r.withdrawalTimelock,
      r.stakeRate,
      r.rewardQLen,
      new anchor.web3.PublicKey(r.mint)
    );
    r["registrar"] = registrar.toString();
  }

  // Generate code for whitelisting on UIs.
  const code = generateCode(registry, lockup, registrarConfigs);
  console.log("Generated whitelisted UI addresses:", code);
};

function generateCode(registry, lockup, registrarConfigs) {
  const registrars = Object.keys(registrarConfigs)
    .map((cfg) => `${cfg}: new PublicKey('${registrarConfigs[cfg].registrar}')`)
    .join(",");

  const mints = Object.keys(registrarConfigs)
    .map((cfg) => `${cfg}: new PublicKey('${registrarConfigs[cfg].mint}')`)
    .join(",");

  return `{
registryProgramId: new PublicKey('${registry.programId}'),
lockupProgramId: new PublicKey('${lockup.programId}'),
registrars: { ${registrars} },
mints: { ${mints} },
 }`;
}

async function genesis(provider) {
  if (
    provider.connection._rpcEndpoint === "https://api.mainnet-beta.solana.com"
  ) {
    return {
      srm: {
        withdrawalTimelock: 60 * 60 * 24 * 7, // 1 week.
        stakeRate: 500 * 10 ** 6, // 500 SRM.
        rewardQLen: 150,
        mint: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt",
      },
      msrm: {
        withdrawalTimelock: 60 * 60 * 24 * 7, // 1 week.
        stakeRate: 1,
        rewardQLen: 150,
        mint: "MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L",
      },
    };
  } else {
    const [token1Mint, _god1] = await serumCmn.createMintAndVault(
      provider,
      new anchor.BN(10000000000000),
      undefined,
      6
    );
    const [token2Mint, _god2] = await serumCmn.createMintAndVault(
      provider,
      new anchor.BN(10000000000),
      undefined,
      0
    );
    return {
      token1: {
        withdrawalTimelock: 60 * 60 * 24 * 7,
        stakeRate: 1000 * 10 ** 6,
        rewardQLen: 150,
        mint: token1Mint.toString(),
      },
      token2: {
        withdrawalTimelock: 60 * 60 * 24 * 7,
        stakeRate: 1,
        rewardQLen: 150,
        mint: token2Mint.toString(),
      },
    };
  }
}

async function registrarInit(
  registry,
  _withdrawalTimelock,
  _stakeRate,
  rewardQLen,
  mint
) {
  const registrar = anchor.web3.Keypair.generate();
  const rewardQ = anchor.web3.Keypair.generate();
  const withdrawalTimelock = new anchor.BN(_withdrawalTimelock);
  const stakeRate = new anchor.BN(_stakeRate);
  const [registrarSigner, nonce] =
    await anchor.web3.PublicKey.findProgramAddress(
      [registrar.publicKey.toBuffer()],
      registry.programId
    );
  const poolMint = await serumCmn.createMint(
    registry.provider,
    registrarSigner
  );
  await registry.rpc.initialize(
    mint,
    registry.provider.wallet.publicKey,
    nonce,
    withdrawalTimelock,
    stakeRate,
    rewardQLen,
    {
      accounts: {
        registrar: registrar.publicKey,
        poolMint,
        rewardEventQ: rewardQ.publicKey,
        rent: anchor.web3.SYSVAR_RENT_PUBKEY,
      },
      signers: [registrar, rewardQ],
      instructions: [
        await registry.account.registrar.createInstruction(registrar),
        await registry.account.rewardQueue.createInstruction(rewardQ, 8250),
      ],
    }
  );
  return registrar.publicKey;
}
