import { getPublicClient } from "@wagmi/core"
import { Hash, SendTransactionParameters, WalletClient } from "viem"
import { useWalletClient } from "wagmi"
import { wagmiConfig } from "~~/services/web3/wagmiConfig"
import {
  getBlockExplorerTxLink,
  getParsedError,
  notification,
} from "~~/utils/xchange"
import { TransactorFuncOptions } from "~~/utils/xchange/contract"

type TransactionFunc = (
  tx: (() => Promise<Hash>) | SendTransactionParameters,
  options?: TransactorFuncOptions
) => Promise<Hash | undefined>

/**
 * Custom notification content for TXs.
 */
const TxnNotification = ({
  message,
  blockExplorerLink,
}: {
  message: string
  blockExplorerLink?: string
}) => {
  return (
    <div className={`ml-1 flex cursor-default flex-col`}>
      <p className="my-0">{message}</p>
      {blockExplorerLink && blockExplorerLink.length > 0 ? (
        <a
          href={blockExplorerLink}
          target="_blank"
          rel="noreferrer"
          className="link text-md block"
        >
          check out transaction
        </a>
      ) : null}
    </div>
  )
}

/**
 * Runs Transaction passed in to returned function showing UI feedback.
 * @param _walletClient - Optional wallet client to use. If not provided, will use the one from useWalletClient.
 * @returns function that takes in transaction function as callback, shows UI feedback for transaction and returns a promise of the transaction hash
 */
export const useTransactor = (
  _walletClient?: WalletClient
): TransactionFunc => {
  let walletClient = _walletClient
  const { data } = useWalletClient()
  if (walletClient === undefined && data) {
    walletClient = data
  }

  const result: TransactionFunc = async (tx, options) => {
    if (!walletClient) {
      notification.error("Cannot access account")
      console.error("⚡️ ~ file: useTransactor.tsx ~ error")
      return
    }

    let notificationId = null
    let transactionHash: Hash | undefined = undefined
    try {
      const network = await walletClient.getChainId()
      // Get full transaction from public client
      const publicClient: any = getPublicClient(wagmiConfig)

      notificationId = notification.loading(
        <TxnNotification message="Awaiting for user confirmation" />
      )
      if (typeof tx === "function") {
        // Tx is already prepared by the caller
        const result = await tx()
        transactionHash = result
      } else if (tx != null) {
        transactionHash = await walletClient.sendTransaction(tx)
      } else {
        throw new Error("Incorrect transaction passed to transactor")
      }
      notification.remove(notificationId)

      const blockExplorerTxURL = network
        ? getBlockExplorerTxLink(network, transactionHash)
        : ""

      notificationId = notification.loading(
        <TxnNotification
          message="Waiting for transaction to complete."
          blockExplorerLink={blockExplorerTxURL}
        />
      )

      const transactionReceipt = await publicClient.waitForTransactionReceipt({
        hash: transactionHash,
        confirmations: options?.blockConfirmations,
      })
      notification.remove(notificationId)

      notification.success(
        <TxnNotification
          message="Transaction completed successfully!"
          blockExplorerLink={blockExplorerTxURL}
        />,
        {
          icon: "🎉",
        }
      )

      if (options?.onBlockConfirmation)
        options.onBlockConfirmation(transactionReceipt)
    } catch (error: any) {
      if (notificationId) {
        notification.remove(notificationId)
      }
      console.error("⚡️ ~ file: useTransactor.ts ~ error", error)
      const message = getParsedError(error)
      notification.error(message)
      throw error
    }

    return transactionHash
  }

  return result
}
