import { BigNumber, ethers } from 'ethers';
import { EtherspotBundler, ModularSdk, SessionKeyValidator, sleep, ERC20_ABI, printOp } from '@etherspot/modular-sdk';
import * as dotenv from 'dotenv';
dotenv.config();

async function main() {
    // add/change these values
    const recipient = '0xdE79F0eF8A1268DAd0Df02a8e527819A3Cd99d40'; // recipient wallet address
    const value = '1'; // transfer value
    const tokenAddress = ''; // token address
    const decimals = 18;
    const erc20SessionKeyValidator = '0x22A55192a663591586241D42E603221eac49ed09'; 
    const bundlerApiKey = process.env.API_KEY as string;
    const walletPrivateKey = process.env.WALLET_PRIVATE_KEY as string;
    const chainId = Number(process.env.CHAIN_ID);


  // initializating sdk...
  const modularSdk = new ModularSdk({ privateKey: privateKey },
    {
      chainId: chainId,
      bundlerProvider: new EtherspotBundler(chainId, bundlerApiKey)
    })
    const sessionKeyModule = await SessionKeyValidator.create(
        modularSdk,
        new EtherspotBundler(chainId, bundlerApiKey)
    );

  console.log(`sessionKey SDK initialized`);

  // get address of EtherspotWallet...
  const address: string = await modularSdk.getCounterFactualAddress();
  console.log('\x1b[33m%s\x1b[0m', `EtherspotWallet address: ${address}`);

  const provider = new ethers.providers.JsonRpcProvider(process.env.BUNDLER_URL)
  // get erc20 Contract Interface
  const erc20Instance = new ethers.Contract(tokenAddress, ERC20_ABI, provider);

  // get transferFrom encoded data
  const transactionData = erc20Instance.interface.encodeFunctionData('transfer', 
    [recipient, ethers.utils.parseUnits(value, decimals)])

  // clear the transaction batch
  await modularSdk.clearUserOpsFromBatch();

  // add transactions to the batch
  const userOpsBatch = await modularSdk.addUserOpsToBatch({
     to: tokenAddress, data: transactionData });
  console.log('transactions: ', userOpsBatch);

  console.log(`erc20SessionKeyValidator ${erc20SessionKeyValidator} as BigNumber is: ${BigNumber.from(erc20SessionKeyValidator)}`);

  // estimate transactions added to the batch and get the fee data for the UserOp
  const op = await modularSdk.estimate({
    key: BigNumber.from(erc20SessionKeyValidator)
  });

 const nonceBig = BigNumber.from(op.nonce);
 console.log(`Nonce: ${nonceBig}`);

  console.log(`Estimate UserOp: ${await printOp(op)}`);

  // sign the UserOp using sessionKey
  const sessionKey = '';

  const signedUserOp = await sessionKeyModule.signUserOpWithSessionKey(sessionKey, op);
  console.log(`etherspot-modular-sdk -> Signed UserOp: ${signedUserOp.signature}`);

  console.log(`Signed UserOp: ${await printOp(signedUserOp)}`);

  console.log(`UserOpNonce is: ${BigNumber.from(signedUserOp.nonce)}`);

  const userOpHashFromSignedUserOp = await modularSdk.getUserOpHash(signedUserOp);
  console.log(`UserOpHash from Signed UserOp: ${userOpHashFromSignedUserOp}`);

  // sending to the bundler with isUserOpAlreadySigned true...
  const uoHash = await modularSdk.send(signedUserOp, true);
  console.log(`UserOpHash: ${uoHash}`);

  // get transaction hash...
  console.log('Waiting for transaction...');
  let userOpsReceipt = null;
  const timeout = Date.now() + 60000; // 1 minute timeout
  while ((userOpsReceipt == null) && (Date.now() < timeout)) {
    await sleep(2);
    userOpsReceipt = await modularSdk.getUserOpReceipt(uoHash);
  }
  console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt);
}