import { GAS_LIMIT_MULTIPLIER } from "@src/constants";
import { ethers } from "ethers";
import { useMemo } from "react";

export const useEvmContractApi = () =>
  useMemo(
    () => ({
      // TODO check if we can avoid nullable contract (think about error handling)
      query: async (
        contract: ethers.Contract | null,
        method: string,
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        args: any[] = [],
      ) => {
        if (contract?.provider) {
          return await contract[method](...args);
        }
        return null;
      },
      execute: async (
        contract: ethers.Contract | null,
        method: string,
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        args: any[] = [],
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        options: Record<string, any> = {},
      ) => {
        try {
          if (contract?.provider) {
            // @ts-expect-error idk, needs to be handled another time in the future
            const gasEstimation = await contract.estimateGas[method](
              ...args,
              options,
            );
            if (!options.gasLimit) {
              options.gasLimit = Math.floor(
                gasEstimation.toNumber() * GAS_LIMIT_MULTIPLIER,
              );
            }
            return await contract[method](...args, options);
          }
          return null;
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
        } catch (err: any) {
          let decodedError;
          try {
            // Try to decode custom error from smart contract
            decodedError = contract?.interface.parseError(
              err?.error?.data?.data,
            );
          } catch (_) {
            // If it fails but there is a message - throw that message
            if (err?.error?.data?.message) {
              throw new Error(err?.error?.data?.message);
            }
            // Else throw whole error to be handled by TransactionProvider
            throw err;
          }
          throw new Error(decodedError?.name);
        }
      },
      executeCalldata: async (signer, transaction) => {
        try {
          const gasEstimation = await signer.estimateGas({
            ...transaction,
          });
          return await signer.sendTransaction({
            ...transaction,
            gasLimit: Math.floor(
              gasEstimation.toNumber() * GAS_LIMIT_MULTIPLIER,
            ),
          });
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
        } catch (err: any) {
          if (err?.error?.data?.message) {
            throw new Error(err?.error?.data?.message);
          }
          throw err;
        }
      },
    }),
    [],
  );

export default useEvmContractApi;
