import { EtherspotBundler, RemoteSignerSdk, UserOperation, toRemoteSigner, BigNumber, erc20Abi, sleep, printOp } from '@etherspot/remote-signer';
import * as dotenv from 'dotenv';
import { privateKeyToAccount } from 'viem/accounts';
import { encodeFunctionData, Hex, http, parseAbi, parseUnits } from 'viem';

dotenv.config();

// add/change these values
const recipient = '0xdE79F0eF8A1268DAd0Df02a8e527819A3Cd99d40'; // recipient wallet address
const value = '0.00000001'; // transfer value
const tokenAddress = ''; // token address
const bundlerApiKey = process.env.API_KEY as string;
const sessionKey = '';
const erc20SessionKeyValidator = process.env.ERC20_SESSION_KEY_VALIDATOR as string; 
const apiKey = process.env.API_KEY as string;
const etherspotWalletAddress = process.env.ETHERSPOT_WALLET_ADDRESS as string;
const chainId = Number(process.env.CHAIN_ID);
const privateKey = process.env.WALLET_PRIVATE_KEY as string;

async function main() {
  const externalViemAccount = privateKeyToAccount(privateKey as Hex);
  const bundlerProvider = new EtherspotBundler(chainId, bundlerApiKey);
  const remoteSignerSdk = await RemoteSignerSdk.create(externalViemAccount, {
    etherspotWalletAddress: etherspotWalletAddress,
    chainId: chainId,
    apiKey: apiKey,
    sessionKey: sessionKey,
    bundlerProvider: bundlerProvider
  });

  const transactionData = await getTransferERC20Data(remoteSignerSdk.getPublicClient());

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

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

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

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

  const signedUserOp = await remoteSignerSdk.signUserOp(op);

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

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

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

  // sending to the bundler with isUserOpAlreadySigned true...
  const uoHash = await remoteSignerSdk.send(signedUserOp);
  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 remoteSignerSdk.getUserOpReceipt(uoHash);
  }
  console.log('\x1b[33m%s\x1b[0m', `Transaction Receipt: `, userOpsReceipt);
}

main()
  .catch(console.error)
  .finally(() => process.exit());

async function getTransferERC20Data(publicClient) {
  const decimals = await publicClient.readContract({
    address: tokenAddress as Hex,
    abi: parseAbi(erc20Abi),
    functionName: 'decimals',
    args: []
  });

  // get transferFrom encoded data
  const transactionData = encodeFunctionData({
    functionName: 'transfer',
    abi: parseAbi(erc20Abi),
    args: [recipient, parseUnits(value, decimals as number)]
  });
  return transactionData;
}