import {
  createSmartAccountClient,
  grantPermission,
  ownableActions,
  toNexusAccount,
  toOwnableModule,
  usePermission
} from "@biconomy/abstractjs-canary"
import {
  getSmartSessionsValidator,
  getSudoPolicy
} from "@rhinestone/module-sdk"
import { http, type LocalAccount, parseEther } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { COUNTER_ADDRESS } from "../src/addresses"
import type { Infra } from "../src/toEcosystem"
import { toClients } from "../src/utils"

export const validateModules = async (infra: Infra) => {
  await validateOwnables(infra)
  await validateSmartSessions(infra)
}

export const validateOwnables = async (infra: Infra) => {
  const {
    bundler,
    network: { rpcUrl, chain, privateKey }
  } = infra

  const {
    walletClients: [_, __, walletClientTwo]
  } = await toClients({ rpcUrl, chain, privateKey })

  const eoaAccount = privateKeyToAccount(privateKey)
  const redeemerAccount = walletClientTwo.account as LocalAccount

  const ownablesModule = toOwnableModule({
    signer: eoaAccount,
    threshold: 1,
    owners: [redeemerAccount.address]
  })

  const nexusAccount = await toNexusAccount({
    signer: eoaAccount,
    chain,
    transport: http(rpcUrl),
    validators: [ownablesModule]
  })

  const { testClient } = await toClients({
    rpcUrl,
    chain,
    privateKey
  })

  await testClient.setBalance({
    address: nexusAccount.address,
    value: parseEther("1")
  })

  const nexusClient = createSmartAccountClient({
    account: nexusAccount,
    transport: http(bundler.url),
    mock: true
  })

  const ownablesClient = nexusClient.extend(ownableActions())

  const { userOpHash, userOp } = await ownablesClient.prepareForMultiSign({
    calls: [
      {
        to: COUNTER_ADDRESS,
        data: "0x273ea3e3"
      }
    ]
  })
  const sig = await redeemerAccount.signMessage({
    message: { raw: userOpHash }
  })
  const multiSigHash = await ownablesClient.multiSign({
    ...userOp,
    signatures: [sig]
  })

  const receipt = await nexusClient.waitForUserOperationReceipt({
    hash: multiSigHash
  })
  if (!receipt.success) {
    throw new Error("Multi sign failed")
  }
  console.log("ownables module check passed ✅")
}

export const validateSmartSessions = async ({
  bundler,
  network: { rpcUrl, chain, privateKey }
}: Infra) => {
  const eoaAccount = privateKeyToAccount(privateKey)

  const {
    walletClients: [_, walletClientTwo],
    publicClient
  } = await toClients({
    rpcUrl,
    chain,
    privateKey
  })

  const dappAccount = walletClientTwo.account as LocalAccount
  const dappAccountAddress = dappAccount.address

  const nexusAccount = await toNexusAccount({
    signer: eoaAccount,
    chain,
    transport: http(rpcUrl)
  })

  const nexusClient = createSmartAccountClient({
    account: nexusAccount,
    transport: http(bundler.url),
    mock: true
  })

  // Install the smart sessions module on the Nexus client's smart contract account
  const hash = await nexusClient.installModule({
    module: getSmartSessionsValidator({ useRegistry: false })
  })

  // Wait for the module installation transaction to be mined and check its success
  const { success: installSuccess } =
    await nexusClient.waitForUserOperationReceipt({ hash })

  if (!installSuccess) {
    throw new Error("Smart sessions module installation failed")
  }

  const sessionDetails = await grantPermission(nexusClient, {
    redeemer: dappAccountAddress,
    actions: [
      {
        actionTarget: COUNTER_ADDRESS,
        actionTargetSelector: "0x273ea3e3",
        actionPolicies: [getSudoPolicy()]
      }
    ]
  })

  const emulatedAccount = await toNexusAccount({
    accountAddress: nexusAccount.address,
    signer: dappAccount,
    chain,
    transport: http(rpcUrl)
  })
  const emulatedClient = createSmartAccountClient({
    account: emulatedAccount,
    transport: http(bundler.url),
    mock: true
  })

  const userOpHashOne = await usePermission(emulatedClient, {
    sessionDetails,
    calls: [{ to: COUNTER_ADDRESS, data: "0x273ea3e3" }],
    mode: "ENABLE_AND_USE"
  })
  const receiptOne = await nexusClient.waitForUserOperationReceipt({
    hash: userOpHashOne
  })
  if (!receiptOne.success) {
    throw new Error("Smart sessions module validation failed")
  }
  const userOpHashTwo = await usePermission(emulatedClient, {
    sessionDetails,
    calls: [{ to: COUNTER_ADDRESS, data: "0x273ea3e3" }],
    mode: "USE"
  })
  const receiptTwo = await nexusClient.waitForUserOperationReceipt({
    hash: userOpHashTwo
  })
  if (!receiptTwo.success) {
    throw new Error("Smart sessions module validation failed")
  }
  console.log("Smart sessions module check passed ✅")
}
