import { Connection } from "@solana/web3.js";
import { SOLANA_MAINNET_RPC_URL } from "@src/constants";
import { Ecosystem, EnqueueTxProps } from "@src/models";
import { ContractReceipt, ContractTransaction } from "ethers";
import {
  Context,
  ReactNode,
  createContext,
  useCallback,
  useContext,
  useMemo,
} from "react";

export interface TransactionState {
  enqueueTransaction: (props: EnqueueTxProps) => number;
}

const TransactionContext: Context<TransactionState> =
  createContext<TransactionState>({} as TransactionState);

interface TransactionProviderProps {
  children: ReactNode;
}

export interface EvmHandlers {
  onSuccess?: (data: ContractReceipt) => void;
  onError?: <T>(data: T) => void;
  onConfirm?: (data?: ContractTransaction) => void;
}

export interface SolanaHandlers {
  onSuccess?: (data: string) => void;
  onError?: <T>(data: T) => void;
  onConfirm?: (txHash?: string) => void;
}

export const TransactionProvider = ({ children }: TransactionProviderProps) => {
  const evmTransactionExecutor = useCallback(
    async ({ executeTransaction, handlers }) => {
      try {
        const tx = await executeTransaction();
        if (!tx) {
          throw new Error("Transaction is undefined");
        }

        handlers?.onConfirm?.(tx);

        const result = await tx.wait();

        handlers?.onSuccess?.(result);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
      } catch (err: any) {
        console.error(err);
        handlers?.onError?.(err);
      }
    },
    [],
  );
  const customConnection = useMemo(
    () => new Connection(SOLANA_MAINNET_RPC_URL, "confirmed"),
    [],
  );
  const solanaTransactionExecutor = useCallback(
    async ({ executeTransaction, handlers }) => {
      try {
        const signedTransaction = await executeTransaction();

        if (!signedTransaction) {
          throw new Error("Transaction is undefined");
        }
        handlers?.onConfirm?.(signedTransaction);

        const txHash = await customConnection.sendTransaction(
          signedTransaction,
          {
            skipPreflight: false, // Set to `true` to bypass simulation checks
            preflightCommitment: "confirmed",
          },
        );

        const latestBlockhash = await customConnection.getLatestBlockhash();

        const confirmation = await customConnection.confirmTransaction(
          {
            signature: txHash,
            blockhash: latestBlockhash.blockhash,
            lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
          },
          "confirmed", // or "finalized" for stricter confirmation
        );
        if (confirmation.value.err) {
          throw new Error(
            `Transaction failed: ${JSON.stringify(confirmation.value.err)}`,
          );
        }
        handlers?.onSuccess?.(txHash);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
      } catch (err: any) {
        console.error(err);
        handlers?.onError?.(err);
      }
    },
    [customConnection],
  );

  const enqueueTransaction = useCallback(
    ({
      executeTransaction,
      handlers,
      network = Ecosystem.EVM,
    }: EnqueueTxProps) => {
      const currentKey = Date.now();
      if (network === Ecosystem.EVM) {
        evmTransactionExecutor({
          executeTransaction,
          handlers,
        });
      } else if (network === Ecosystem.SOLANA) {
        solanaTransactionExecutor({
          executeTransaction,
          handlers,
        });
      }
      return currentKey;
    },
    [evmTransactionExecutor, solanaTransactionExecutor],
  );

  const state = useMemo<TransactionState>(
    () => ({
      enqueueTransaction,
    }),
    [enqueueTransaction],
  );

  return (
    <TransactionContext.Provider value={state}>
      {children}
    </TransactionContext.Provider>
  );
};

export function useTransaction(): TransactionState {
  return useContext(TransactionContext);
}
