import { Modal } from "@src/components/Modal";
import { useSwapContext, useTxUIWrapper } from "@src/context";
import { useWallet } from "@src/context/WalletProvider";
import { ADDRESSES } from "@src/contracts";
import { specialApprovalTokens } from "@src/contracts/specialApprovalTokens";
import useNetworks from "@src/hooks/networkManagement/useNetworks";
import { useERC20Token } from "@src/hooks/useERC20Token";
import { Ecosystem, TxStatus } from "@src/models";
import { safeBigNumberFrom } from "@src/utils";
import { BigNumber } from "ethers";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useAccount, useSwitchChain } from "wagmi";
import { WalletPicker } from "../WalletPicker";

type Button = {
  text: string;
  fn: () => void;
  disabled: boolean;
};

export const SwapButton = () => {
  const { address, chainId } = useAccount();
  const { switchChainAsync } = useSwitchChain();
  const { integrationConfig } = useSwapContext();

  const {
    bridgeUI,
    error,
    srcChain,
    dstChain,
    srcToken,
    dstToken,
    srcValueWei,
    srcTokenBalanceWei,
    isFetchingRoute,
    isAsyncDataLoaded,
    hasOnConnectWalletCallback,
    onConnectWallet,
  } = useSwapContext();

  const { setTxStatus, setTxMsg, setTxModalOpen, enqueueTransaction } =
    useTxUIWrapper();

  const srcChainId = useMemo(() => srcChain?.chainId, [srcChain?.chainId]);
  const dstChainId = useMemo(() => dstChain?.chainId, [dstChain?.chainId]);
  const srcTokenAddress = useMemo(() => srcToken?.address, [srcToken?.address]);
  const dstTokenAddress = useMemo(() => dstToken?.address, [dstToken?.address]);

  const [currentTokenAllowance, setCurrentTokenAllowance] = useState("0");

  const {
    networks: { evm },
  } = useNetworks();

  const {
    openEVMWalletModal,
    setOpenEVMWalletModal,
    openSolanaWalletModal,
    setOpenSolanaWalletModal,
    solana,
  } = useWallet();

  const { allowance, approve } = useERC20Token({
    rpcChainId: srcChainId,
    currentChainId: evm.chainId,
    signer: evm.signer,
    tokenAddress: srcTokenAddress,
  });

  const routerAddress = useMemo(() => {
    if (!srcChainId) return "";
    return srcChainId === dstChainId
      ? ADDRESSES[srcChainId]?.XSwapRouterSingleChain || ""
      : ADDRESSES[srcChainId]?.XSwapRouter || "";
  }, [srcChainId, dstChainId]);

  const getAllowance = useCallback(async () => {
    if (routerAddress && evm.address) {
      try {
        const currentAllowance = await allowance(evm.address, routerAddress);
        setCurrentTokenAllowance(currentAllowance.toString());
      } catch (err) {
        setCurrentTokenAllowance("0");
      }
    } else {
      setCurrentTokenAllowance("0");
    }
  }, [allowance, evm.address, routerAddress]);

  useEffect(() => {
    if (
      srcChainId &&
      specialApprovalTokens[srcChainId]?.includes(srcTokenAddress)
    ) {
      getAllowance();
    }
  }, [getAllowance, srcChainId, srcTokenAddress]);

  const showSwapConfirmationView = useCallback(() => {
    setTxStatus(TxStatus.SWAP_INIT);
    setTxMsg("");
    setTxModalOpen(true);
  }, [setTxModalOpen, setTxMsg, setTxStatus]);

  const button: Button = useMemo(() => {
    if (!isAsyncDataLoaded) {
      return {
        text: "Loading XPay...",
        fn: () => {},
        disabled: true,
      };
    }

    if (error) {
      return {
        text: error,
        fn: () => {},
        disabled: true,
      };
    }

    if (srcChain?.ecosystem !== Ecosystem.SOLANA && !address) {
      return {
        text:
          integrationConfig.defaultWalletPicker || hasOnConnectWalletCallback
            ? "Connect Wallet"
            : "Connect Wallet First",
        fn: () => {
          if (integrationConfig.defaultWalletPicker) {
            setOpenEVMWalletModal(true);
          } else if (hasOnConnectWalletCallback) {
            onConnectWallet();
          }
        },
        disabled:
          !integrationConfig.defaultWalletPicker && !hasOnConnectWalletCallback,
      };
    }

    if (!srcChainId || !dstChainId || !srcTokenAddress || !dstTokenAddress) {
      return {
        text: "Select Tokens",
        fn: () => {},
        disabled: true,
      };
    }

    if (
      srcChainId === dstChainId &&
      srcTokenAddress.toLowerCase() === dstTokenAddress.toLowerCase()
    ) {
      return {
        text: "Change Token",
        fn: () => {},
        disabled: true,
      };
    }
    if (srcChain?.ecosystem === Ecosystem.SOLANA && !solana.address) {
      return {
        text: "Connect Solana Wallet",
        fn: () => {
          setOpenSolanaWalletModal(true);
        },
        disabled: false,
      };
    }
    if (
      srcChain?.ecosystem === Ecosystem.SOLANA &&
      dstChain?.ecosystem === Ecosystem.EVM &&
      !address
    ) {
      return {
        text: "Connect EVM Wallet",
        fn: () => {
          setOpenEVMWalletModal(true);
        },
        disabled: false,
      };
    }
    if (
      srcChain?.ecosystem === Ecosystem.EVM &&
      dstChain?.ecosystem === Ecosystem.SOLANA &&
      !solana.address
    ) {
      return {
        text: "Connect Solana Wallet",
        fn: () => {
          setOpenSolanaWalletModal(true);
        },
        disabled: false,
      };
    }
    if (
      srcChain?.ecosystem !== Ecosystem.SOLANA &&
      chainId?.toString() !== srcChainId
    ) {
      return {
        text: "Change Chain",
        fn: async () => await switchChainAsync({ chainId: Number(srcChainId) }),
        disabled: false,
      };
    }

    if (
      srcValueWei &&
      srcTokenBalanceWei &&
      safeBigNumberFrom(srcValueWei).gt(safeBigNumberFrom(srcTokenBalanceWei))
    ) {
      return {
        text: "Insufficient Balance",
        fn: () => {},
        disabled: true,
      };
    }

    if (safeBigNumberFrom(srcValueWei || "0").lte(safeBigNumberFrom("0"))) {
      return {
        text: `Enter Token Amount`,
        fn: () => {},
        disabled: true,
      };
    }

    if (isFetchingRoute) {
      return {
        text: `Fetching Route`,
        fn: () => {},
        disabled: true,
      };
    }

    if (
      specialApprovalTokens[srcChainId]?.includes(srcTokenAddress) &&
      currentTokenAllowance !== "0" &&
      BigNumber.from(currentTokenAllowance).lt(BigNumber.from(srcValueWei))
    ) {
      return {
        text: "Reset Approval",
        fn: async () => {
          enqueueTransaction({
            executeTransaction: () => approve("0", routerAddress),
            txStatus: TxStatus.SIGNING_APPROVAL,
            txMsg: "You are going to reset your approval",
            handlers: {
              onSuccess: () => {
                getAllowance();
                setTxStatus(TxStatus.COMPLETED);
              },
            },
          });
        },
        disabled: false,
      };
    }

    return {
      text: bridgeUI ? "Bridge" : "Swap",
      fn: async () => {
        showSwapConfirmationView();
      },
      disabled: false,
    };
  }, [
    isAsyncDataLoaded,
    error,
    srcChain?.ecosystem,
    dstChain?.ecosystem,
    address,
    srcChainId,
    dstChainId,
    srcTokenAddress,
    dstTokenAddress,
    solana.address,
    chainId,
    srcValueWei,
    isFetchingRoute,
    currentTokenAllowance,
    bridgeUI,
    integrationConfig.defaultWalletPicker,
    hasOnConnectWalletCallback,
    setOpenEVMWalletModal,
    onConnectWallet,
    setOpenSolanaWalletModal,
    switchChainAsync,
    enqueueTransaction,
    approve,
    srcTokenBalanceWei,
    routerAddress,
    getAllowance,
    setTxStatus,
    showSwapConfirmationView,
  ]);

  return (
    <>
      {openEVMWalletModal && (
        <Modal onClose={() => setOpenEVMWalletModal(false)}>
          <WalletPicker
            ecosystem={Ecosystem.EVM}
            onClose={() => setOpenEVMWalletModal(false)}
          />
        </Modal>
      )}
      {openSolanaWalletModal && (
        <Modal onClose={() => setOpenSolanaWalletModal(false)}>
          <WalletPicker
            ecosystem={Ecosystem.SOLANA}
            onClose={() => setOpenSolanaWalletModal(false)}
          />
        </Modal>
      )}
      <button
        type="button"
        disabled={button.disabled}
        onClick={button.fn}
        className={`text-xl font-sans-bold font-bold w-full py-5 cursor-pointer disabled:cursor-not-allowed rounded-2xl border-none ${
          button.disabled
            ? "bg-t_button_pr_off_bg text-t_button_pr_off_text"
            : "bg-gradient-to-r from-t_main_accent_light to-t_main_accent_dark text-t_button_pr_text"
        }`}
      >
        {button.text}
      </button>
    </>
  );
};
