import { CloseIcon } from "@src/assets/icons";
import {
  Button,
  SnackMessage,
  Spinner,
  TxModal,
  TxModalType,
} from "@src/components";
import { ConfirmationView } from "@src/components/Swap/SwapView/ConfirmationView";
import { CCIP_EXPLORER } from "@src/constants";
import {
  Ecosystem,
  EnqueueTxProps,
  TxStatus,
  TxUIWrapperState,
} from "@src/models";
import { parseWeb3Error, trimTxHash } from "@src/utils";
import { ContractReceipt } from "ethers";
import { useSnackbar } from "notistack";
import {
  Context,
  createContext,
  ReactNode,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { useSwapContext } from "./SwapProvider";
import {
  EvmHandlers,
  SolanaHandlers,
  useTransaction,
} from "./TransactionProvider";
import { useWallet } from "./WalletProvider";

const TxUIWrapperContext: Context<TxUIWrapperState> =
  createContext<TxUIWrapperState>({} as TxUIWrapperState);

type Props = {
  children: ReactNode;
};
export const TxUIWrapper = ({ children }: Props) => {
  const { evm, solana } = useWallet();
  const { chainId } = evm;
  const { chainId: solanaChainId } = solana;
  const { enqueueTransaction: nativeEnqueueTransaction } = useTransaction();
  const { enqueueSnackbar, closeSnackbar } = useSnackbar();
  const [currentNetwork, setCurrentNetwork] = useState(Ecosystem.EVM);
  const isTxModalOpenRef = useRef(false);
  const [isTxModalOpen, setTxModalOpen] = useState(false);
  const [txHash, setTxHash] = useState("");
  const txKey = useRef<number>();
  const [txError, setTxError] = useState("");
  const [txMsg, setTxMsg] = useState("");
  const [txReceipt, setTxReceipt] = useState<ContractReceipt | undefined>(
    undefined,
  );

  const [loading, setLoading] = useState(false);
  const [txNeedsApproval, setTxNeedsApproval] = useState(false);
  const [txStatus, setTxStatus] = useState(TxStatus.SWAP_INIT);

  const { supportedChains, setSrcValue } = useSwapContext();

  const closeTransactionModal = (transactionKey: number) => {
    if (txKey.current === transactionKey) {
      isTxModalOpenRef.current = false;
      setTxModalOpen(false);
    }
  };

  useEffect(() => {
    if (!isTxModalOpen) {
      if (txError) {
        enqueueSnackbar(
          <SnackMessage
            message={{
              text: txError,
              variant: "error",
            }}
          />,
        );
        setTxError("");
      }
      if (loading) {
        if (txHash) {
          const snackbarKey = txHash;
          enqueueSnackbar(
            <SnackMessage
              message={{
                text: `Transaction ${trimTxHash(txHash)} is about to be mined!`,
                variant: "warning",
                action: () => (
                  <div className="flex items-center flex-nowrap">
                    <Button
                      type="button"
                      onClick={() => {
                        window.open(
                          `${
                            supportedChains.find(
                              (chain) =>
                                chain.chainId === chainId?.toString() &&
                                chain.ecosystem === currentNetwork,
                            )?.transactionExplorer
                          }/${txHash}` || "",
                        );
                      }}
                    >
                      Check
                    </Button>

                    <div
                      className="fill-t_text_primary w-[14px] h-[14px] cursor-pointer p-[14px] leading-[0px]"
                      onClick={() => closeSnackbar(snackbarKey)}
                    >
                      <CloseIcon />
                    </div>
                  </div>
                ),
                animation: () => <Spinner className="w-4 h-4" />,
              }}
            />,
            { persist: true, key: snackbarKey },
          );
          setTxHash("");
        }
      }
    }
  }, [
    loading,
    currentNetwork,
    txHash,
    txError,
    enqueueSnackbar,
    closeSnackbar,
    chainId,
    isTxModalOpen,
    supportedChains,
  ]);

  const getTxExplorerUrl = useCallback(
    (txHash: string, txExplorer: string, txReceipt?: ContractReceipt) => {
      const defaultUrl = `${txExplorer}/${txHash}`;
      if (!txReceipt) {
        return defaultUrl;
      } else {
        const msgSendEvent = txReceipt.events?.find(
          (event) => event.event === "MessageSent",
        );
        const messageId = msgSendEvent?.args?.messageId;
        if (messageId) {
          return `${CCIP_EXPLORER}${messageId}`;
        } else {
          return defaultUrl;
        }
      }
    },
    [],
  );

  const addUILogicToHandlersEVM = useCallback(
    (
      currentKey: number,
      handlers?: EvmHandlers,
      showDefaultSuccessMessage = true,
    ): EvmHandlers => {
      const newHandlers: EvmHandlers = { ...handlers };

      newHandlers.onConfirm = (tx) => {
        if (txKey.current === currentKey) {
          if (handlers?.onConfirm) {
            handlers?.onConfirm(tx);
          }
          setTxHash(tx?.hash || "");
          setTxStatus((prevStatus) => prevStatus + 1);
        }
      };

      newHandlers.onSuccess = (result) => {
        closeSnackbar(result?.transactionHash);

        if (txKey.current !== currentKey) {
          return;
        }

        setTxReceipt(result);
        setTxError("");
        setLoading(false);

        if (handlers?.onSuccess) {
          handlers.onSuccess(result);
        }

        if (isTxModalOpenRef.current || !showDefaultSuccessMessage) {
          return;
        }

        const snackbarKey = `${txHash}-success`;
        enqueueSnackbar(
          <SnackMessage
            message={{
              text: `Transaction mined successfully!`,
              variant: "success",
              action: () => (
                <div className="flex items-center flex-nowrap">
                  <Button
                    type="button"
                    onClick={() => {
                      window.open(
                        getTxExplorerUrl(
                          txHash,
                          supportedChains.find(
                            (chain) =>
                              chain.chainId === chainId?.toString() &&
                              chain.ecosystem === currentNetwork,
                          )?.transactionExplorer || "",
                          result,
                        ),
                      );
                    }}
                  >
                    View
                  </Button>
                  <div
                    className="fill-t_text_primary w-[14px] h-[14px] cursor-pointer p-[14px] leading-[0px]"
                    onClick={() => closeSnackbar(snackbarKey)}
                  >
                    <CloseIcon />
                  </div>
                </div>
              ),
            }}
          />,
          { persist: false, key: snackbarKey },
        );
      };

      newHandlers.onError = (err) => {
        if (txKey.current === currentKey) {
          setTxError(parseWeb3Error(err));
          handlers?.onError?.(err);
          setLoading(false);
        }
      };

      return newHandlers;
    },
    [
      closeSnackbar,
      txHash,
      enqueueSnackbar,
      getTxExplorerUrl,
      supportedChains,
      chainId,
      currentNetwork,
    ],
  );

  const addUILogicToHandlersSolana = useCallback(
    (
      currentKey: number,
      handlers?: SolanaHandlers,
      showDefaultSuccessMessage = true,
    ): SolanaHandlers => {
      const newHandlers: SolanaHandlers = { ...handlers };

      newHandlers.onConfirm = (signedTransaction) => {
        if (txKey.current === currentKey) {
          if (handlers?.onConfirm) {
            handlers?.onConfirm(signedTransaction);
          }
          setTxStatus((prevStatus) => prevStatus + 1);
        }
      };

      newHandlers.onSuccess = (transactionHash) => {
        closeSnackbar(transactionHash);

        if (txKey.current !== currentKey) {
          return;
        }

        if (handlers?.onSuccess) {
          handlers?.onSuccess(transactionHash);
        }
        setTxHash(transactionHash || "");
        setTxError("");
        setLoading(false);

        if (isTxModalOpenRef.current || !showDefaultSuccessMessage) {
          return;
        }

        const snackbarKey = `${txHash}-success`;
        enqueueSnackbar(
          <SnackMessage
            message={{
              text: `Transaction mined successfully!`,
              variant: "success",
              action: () => (
                <div className="flex items-center flex-nowrap">
                  <Button
                    type="button"
                    onClick={() => {
                      window.open(
                        getTxExplorerUrl(
                          txHash,
                          supportedChains.find(
                            (chain) =>
                              chain.chainId === solanaChainId?.toString() &&
                              chain.ecosystem === currentNetwork,
                          )?.transactionExplorer || "",
                        ),
                      );
                    }}
                  >
                    View
                  </Button>

                  <div
                    className="fill-t_text_primary w-[14px] h-[14px] cursor-pointer p-[14px] leading-[0px]"
                    onClick={() => closeSnackbar(snackbarKey)}
                  >
                    <CloseIcon />
                  </div>
                </div>
              ),
            }}
          />,
          { persist: false, key: snackbarKey },
        );
      };

      newHandlers.onError = (err) => {
        if (txKey.current === currentKey) {
          setTxError(parseWeb3Error(err));
          handlers?.onError?.(err);
          setLoading(false);
        }
      };

      return newHandlers;
    },
    [
      closeSnackbar,
      txHash,
      enqueueSnackbar,
      getTxExplorerUrl,
      supportedChains,
      solanaChainId,
      currentNetwork,
    ],
  );

  const enqueueTransaction = useCallback(
    ({
      executeTransaction,
      txStatus,
      txMsg = "",
      handlers,
      network = Ecosystem.EVM,
      showDefaultSuccessMessage = true,
    }: EnqueueTxProps) => {
      const currentKey = Date.now();

      let newHandlers = handlers;
      if (network === Ecosystem.EVM) {
        newHandlers = addUILogicToHandlersEVM(
          currentKey,
          handlers as EvmHandlers,
          showDefaultSuccessMessage,
        );
      } else if (network === Ecosystem.SOLANA) {
        newHandlers = addUILogicToHandlersSolana(
          currentKey,
          handlers as SolanaHandlers,
          showDefaultSuccessMessage,
        );
      }

      // extracted tx logic
      nativeEnqueueTransaction({
        executeTransaction,
        txStatus,
        handlers: newHandlers,
        network,
      });

      txKey.current = currentKey;
      isTxModalOpenRef.current = true;
      setTxHash("");
      setTxError("");
      setTxMsg(txMsg);
      setTxStatus(txStatus);
      setTxReceipt(undefined);
      setLoading(true);
      setCurrentNetwork(network);
      setTxModalOpen(true);

      return currentKey;
    },
    [
      addUILogicToHandlersEVM,
      addUILogicToHandlersSolana,
      nativeEnqueueTransaction,
    ],
  );

  const txExplorerUrl = useMemo(() => {
    return getTxExplorerUrl(
      txHash,
      supportedChains.find(
        (chain) =>
          (chain.chainId === chainId?.toString() ||
            chain.chainId === solanaChainId?.toString()) &&
          chain.ecosystem === currentNetwork,
      )?.transactionExplorer || "",
      txReceipt,
    );
  }, [
    chainId,
    solanaChainId,
    currentNetwork,
    getTxExplorerUrl,
    supportedChains,
    txHash,
    txReceipt,
  ]);

  const setModalOpen = useCallback((flag: boolean) => {
    setTxModalOpen(flag);
    isTxModalOpenRef.current = flag;
  }, []);

  const modalType: TxModalType = useMemo(
    () =>
      txError
        ? "error"
        : txStatus === TxStatus.COMPLETED
        ? "success"
        : "neutral",
    [txError, txStatus],
  );

  const state = useMemo<TxUIWrapperState>(
    () => ({
      enqueueTransaction,
      closeTransactionModal,
      isTxModalOpen,
      setTxModalOpen,
      loading,
      txStatus,
      setTxStatus,
      txExplorerUrl,
      txHash,
      txError,
      txMsg,
      setTxMsg,
      setTxError,
      txNeedsApproval,
      setTxNeedsApproval,
    }),
    [
      enqueueTransaction,
      isTxModalOpen,
      loading,
      txStatus,
      txExplorerUrl,
      txHash,
      txError,
      txMsg,
      txNeedsApproval,
    ],
  );

  const closeTxModal = useCallback(() => {
    setTxError("");
    setSrcValue("");
    setModalOpen(false);
  }, [setTxError, setSrcValue, setModalOpen]);

  return (
    <TxUIWrapperContext.Provider value={state}>
      {isTxModalOpen && (
        <TxModal
          id="tx_modal"
          className="max-w-x_modal"
          type={modalType}
          onCloseClick={closeTxModal}
        >
          <ConfirmationView onCloseClick={closeTxModal} />
        </TxModal>
      )}
      {children}
    </TxUIWrapperContext.Provider>
  );
};

export function useTxUIWrapper(): TxUIWrapperState {
  return useContext(TxUIWrapperContext);
}
