import React, { createContext, useContext, useEffect, useState } from "react";
import { toast, ToastProvider } from "./ui";
import WalletSDK from "../index";
import {
  getAllTokens,
  TokenInfo,
  getMultiRelaySwapQuote,
  getMultiFromBTCRelaySwapQuote,
  getMultiToBTCRelaySwapQuote,
  OutputDetails,
  executeSwapFlow,
  executeBungeeSwapFlow,
  executeFromBTCSwapFlow,
  executeToBTCSwapFlow,
  searchToken,
  getTransactionInfo,
  executeLiFiSwapFlow,
} from "../services/services";
import { swapToken } from "../types/swap";
import { ProtocolProvider } from "./utils";

// Add Bitcoin mainnet chain ID constant
const BITCOIN_MAINNET_CHAIN_ID = 8253038;

interface Token {
  chainIds: {
    chainId: number;
    address: string;
  }[];
  tokenAddress: string;
  amount?: string;
  icon?: string;
  symbol: string;
  name: string;
  decimals: number;
}

interface SwapParams {
  fromToken: swapToken;
  toToken: Omit<swapToken, "amount">;
  showMessagePopup?: boolean;
  messageType?: "success" | "error";
  transactionId?: string;
}

// Add new interface for quote calculation
interface QuoteParams {
  fromToken: {
    amount: string;
    chainId: number;
    tokenAddress: string;
    metadata?: {
      tokenName: string;
      tokenSymbol: string;
      decimals: number;
      logoURI: string;
      chainIds: {
        chainId: number;
        address: string;
      }[];
    };
  };
  toToken: {
    chainId: number;
    tokenAddress: string;
    metadata?: {
      tokenName: string;
      tokenSymbol: string;
      decimals: number;
      logoURI: string;
      chainIds: {
        chainId: number;
        address: string;
      }[];
    };
  };
  provider?: ProtocolProvider;
}

interface WalletContextType {
  walletSDK: WalletSDK | null;
  isLoggedIn: boolean;
  username: string | null;
  walletAddress: string | null;
  solanaAddress: string | null;
  bitcoinWalletAddress: string | null;
  balance: any | null;
  connect: () => void;
  disconnect: () => void;
  swap: (params: SwapParams) => void;
  tokenOptions: TokenInfo[];
  tokensLoading: boolean;
  refreshTokenOptions: () => Promise<void>;
  cryptoBalance: any;
  loading: boolean;
  setLoading: (loading: boolean) => void;
  refreshBalance: (showLoading?: boolean) => Promise<void>;
  showActivity: boolean;
  setShowActivity: (showActivity: boolean) => void;
  activity: any;
  setActivity: (activity: any) => void;
  calculateQuote: (params: QuoteParams) => Promise<OutputDetails | string>;
  executeSwap: (params: QuoteParams) => Promise<any>;
}

const WalletContext = createContext<WalletContextType | null>(null);

export const useWallet = () => {
  const context = useContext(WalletContext);
  if (!context) {
    throw new Error("useWallet must be used within a WalletProvider");
  }
  return context;
};

interface WalletProviderProps {
  children: React.ReactNode;
  sdkKey: string;
}

export const WalletProvider: React.FC<WalletProviderProps> = ({
  children,
  sdkKey,
}) => {
  const [walletSDK, setWalletSDK] = useState<WalletSDK | null>(null);
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [username, setUsername] = useState<string | null>(null);
  const [walletAddress, setWalletAddress] = useState<string | null>(null);
  const [solanaAddress, setSolanaAddress] = useState<string | null>(null);
  const [bitcoinWalletAddress, setBitcoinWalletAddress] = useState<
    string | null
  >(null);
  const [balance, setBalance] = useState<any | null>(null);
  const [showSwapModal, setShowSwapModal] = useState(false);
  const [swapParams, setSwapParams] = useState<SwapParams | null>(null);
  const [tokenOptions, setTokenOptions] = useState<TokenInfo[]>([]);
  const [tokensLoading, setTokensLoading] = useState(false);

  // Add new states for crypto balance functionality
  const [cryptoBalance, setCryptoBalance] = useState<any>(null);
  const [loading, setLoading] = useState(false);
  const [showActivity, setShowActivity] = useState(false);
  const [activity, setActivity] = useState<any>(null);
  const [showMessagePopup, setShowMessagePopup] = useState(true);
  const [messageType, setMessageType] = useState<"success" | "error">(
    "success"
  );
  const [transactionId, setTransactionId] = useState<string | null>(null);

  const refreshTokenOptions = async () => {
    try {
      setTokensLoading(true);
      const response = await getAllTokens(sdkKey);
      if (response?.success) {
        setTokenOptions(response.tokens);
      }
    } catch (error) {
      console.error("Error fetching token options:", error);
    } finally {
      setTokensLoading(false);
    }
  };

  const refreshBalance = async (showLoading: boolean = false) => {
    try {
      if (showLoading) {
        setLoading(true);
      }
      const sdkInstance = (window as any).__walletSDKInstance;
      if (sdkInstance) {
        const balance = await sdkInstance.getUserCryptoBalance();
        setCryptoBalance(balance);
        // Store balance in localStorage for persistence
        if (typeof window !== "undefined") {
          localStorage.setItem(
            "enclave_crypto_balance",
            JSON.stringify(balance)
          );
        }
      }
    } catch (error) {
      console.error("Error fetching balance:", error);
    } finally {
      setLoading(false);
    }
  };

  // Initialize crypto balance from localStorage on mount
  useEffect(() => {
    if (typeof window !== "undefined") {
      const savedBalance = localStorage.getItem("enclave_crypto_balance");
      if (savedBalance) {
        try {
          setCryptoBalance(JSON.parse(savedBalance));
        } catch (error) {
          console.error("Error parsing saved balance:", error);
        }
      }
    }
  }, []);

  // Listen for storage changes to trigger balance refresh
  useEffect(() => {
    const handleStorageChange = (e: StorageEvent) => {
      if (e.key === "enclave_wallet_login") {
        if (e.newValue) {
          refreshBalance(true);
        } else {
          setCryptoBalance(null);
          // Clear balance from localStorage when user logs out
          if (typeof window !== "undefined") {
            localStorage.removeItem("enclave_crypto_balance");
          }
        }
      }
    };

    if (typeof window !== "undefined") {
      window.addEventListener("storage", handleStorageChange);
      return () => window.removeEventListener("storage", handleStorageChange);
    }
  }, []);

  // Initial balance load
  useEffect(() => {
    if (typeof window !== "undefined") {
      const userSession = localStorage.getItem("enclave_wallet_login");
      if (userSession) {
        refreshBalance(true); // Show loading on initial load
      }
    }
  }, [sdkKey]);

  // Balance polling effect
  useEffect(() => {
    if (typeof window !== "undefined") {
      const userSession = localStorage.getItem("enclave_wallet_login");
      if (!userSession) return;

      const pollInterval = setInterval(() => {
        refreshBalance(false); // Don't show loading during polling
      }, 10000);

      return () => clearInterval(pollInterval);
    }
  }, [sdkKey]);

  useEffect(() => {
    const sdk = new WalletSDK(sdkKey);
    setWalletSDK(sdk);

    // Check initial state
    if (typeof window !== "undefined") {
      const saved = localStorage.getItem("enclave_wallet_login");
      if (saved) {
        const parsed = JSON.parse(saved);
        setIsLoggedIn(true);
        setUsername(
          parsed.result?.displayName || parsed.result?.username || null
        );
        setWalletAddress(parsed.result?.wallet?.scw_address || null);
        setSolanaAddress(parsed.result?.wallet?.solana_program_wallet || null);
        setBitcoinWalletAddress(
          parsed.result?.wallet?.bitcoin_wallet?.native_segwit_address || null
        );
        // Fetch initial balance
      }
    }
  }, [sdkKey]);

  const connect = () => {
    if (walletSDK) {
      walletSDK.openWalletModal();
    }
  };

  const disconnect = () => {
    if (walletSDK) {
      walletSDK.logout();
      setIsLoggedIn(false);
      setUsername(null);
      setWalletAddress(null);
      setBalance(null);
      setCryptoBalance(null);
      // Clear balance from localStorage when user disconnects
      if (typeof window !== "undefined") {
        localStorage.removeItem("enclave_crypto_balance");
      }
    }
  };

  // Listen for session changes and update balance
  useEffect(() => {
    if (!walletSDK) return;

    const checkSession = async () => {
      if (typeof window !== "undefined") {
        const saved = localStorage.getItem("enclave_wallet_login");
        if (saved) {
          const parsed = JSON.parse(saved);
          setIsLoggedIn(true);
          setUsername(
            parsed.result?.displayName || parsed.result?.username || null
          );
          setWalletAddress(parsed.result?.wallet?.scw_address || null);
          setSolanaAddress(
            parsed.result?.wallet?.solana_program_wallet || null
          );
          setBitcoinWalletAddress(
            parsed.result?.wallet?.bitcoin_wallet?.native_segwit_address || null
          );
          // Fetch updated balance
        } else {
          setIsLoggedIn(false);
          setUsername(null);
          setWalletAddress(null);
          setBalance(null);
          setCryptoBalance(null);
          // Clear balance from localStorage when session is not found
          localStorage.removeItem("enclave_crypto_balance");
        }
      }
    };

    // Check session every 1 seconds
    const interval = setInterval(checkSession, 1000);
    return () => clearInterval(interval);
  }, [walletSDK]);

  const swap = (params: SwapParams) => {
    if (!isLoggedIn) {
      connect();
      return;
    }

    setSwapParams(params);
    setShowSwapModal(true);
    walletSDK?.openWalletModal(params);
  };

  // Add initial token fetch
  useEffect(() => {
    refreshTokenOptions();
  }, [sdkKey]);

  // Add token refresh on login/logout
  useEffect(() => {
    if (isLoggedIn) {
      refreshBalance(true);
    } else {
      setTokenOptions([]);
    }
  }, [isLoggedIn]);

  // Add quote calculation function
  const calculateQuote = async (
    params: QuoteParams
  ): Promise<OutputDetails | string> => {
    const sdkInstance = (window as any).__walletSDKInstance;
    if (!sdkInstance) {
      throw new Error("SDK instance not found");
    }

    const userName = sdkInstance.getUsername();
    if (!userName) {
      throw new Error("User not logged in");
    }

    console.log("params >>>>>>>>", params);

    try {
      // Check if input token is Bitcoin
      if (params.fromToken.chainId === BITCOIN_MAINNET_CHAIN_ID) {
        return await getMultiFromBTCRelaySwapQuote(
          {
            username: userName,
            amount: params.fromToken.amount,
            outputToken: params.toToken.tokenAddress,
            outputChainId: params.toToken.chainId,
          },
          sdkKey
        );
      }
      // Check if output token is Bitcoin
      else if (params.toToken.chainId === BITCOIN_MAINNET_CHAIN_ID) {
        // Get user balance for the input token
        const userBalance = cryptoBalance?.data?.find((token: any) =>
          token.chainIds?.some(
            (chain: any) =>
              chain.address.toLowerCase() ===
                params.fromToken.tokenAddress.toLowerCase() &&
              chain.chainId === params.fromToken.chainId
          )
        );

        // Default balance if user doesn't have the token
        const defaultBalance = [
          {
            chainId: params.fromToken.chainId,
            address: params.fromToken.tokenAddress,
            balance: "0",
          },
        ];

        return await getMultiToBTCRelaySwapQuote(
          {
            username: userName,
            userBalance: userBalance?.chainIds || defaultBalance,
            inputAmount: params.fromToken.amount,
            proMode: false,
          },
          sdkKey
        );
      }
      // Regular swap quote
      else {
        // Get user balance for the input token
        const userBalance = cryptoBalance?.data?.find((token: any) =>
          token.chainIds?.some(
            (chain: any) =>
              chain.address.toLowerCase() ===
                params.fromToken.tokenAddress.toLowerCase() &&
              chain.chainId === params.fromToken.chainId
          )
        );

        // Default balance if user doesn't have the token
        const defaultBalance = [
          {
            chainId: params.fromToken.chainId,
            address: params.fromToken.tokenAddress,
            balance: "0",
          },
        ];

        const metadata = {
          inputToken: {
            tokenName: params.fromToken.metadata?.tokenName || "",
            tokenSymbol: params.fromToken.metadata?.tokenSymbol || "",
            tokenAddress: params.fromToken.tokenAddress,
            decimals: params.fromToken.metadata?.decimals || 0,
            logoURI: params.fromToken.metadata?.logoURI || "",
          },
          outputToken: {
            tokenName: params.toToken.metadata?.tokenName || "",
            tokenSymbol: params.toToken.metadata?.tokenSymbol || "",
            tokenAddress: params.toToken.tokenAddress,
            decimals: params.toToken.metadata?.decimals || 0,
            logoURI: params.toToken.metadata?.logoURI || "",
          },
        };

        return await getMultiRelaySwapQuote(
          {
            username: userName,
            userBalance: userBalance?.chainIds || defaultBalance,
            outputToken: params.toToken.tokenAddress,
            outputChainId: params.toToken.chainId,
            inputAmount: params.fromToken.amount,
            proMode: false,
            metadata,
          },
          sdkKey
        );
      }
    } catch (error) {
      console.error("Error calculating quote:", error);
      return "Error calculating quote";
    }
  };

  // Add execute swap function
  const executeSwap = async (params: QuoteParams): Promise<any> => {
    const sdkInstance = (window as any).__walletSDKInstance;
    if (!sdkInstance) {
      throw new Error("SDK instance not found");
    }

    const userName = sdkInstance.getUsername();
    if (!userName) {
      throw new Error("User not logged in");
    }

    // Show processing toast
    const loadingToast = toast.loading("Processing transaction...");

    try {
      // Get token details from tokenOptions for metadata

      let fromTokenDetails = tokenOptions.find((token) =>
        token.chainIds?.some(
          (chain) =>
            chain.address.toLowerCase() ===
              params.fromToken.tokenAddress.toLowerCase() &&
            chain.chainId === params.fromToken.chainId
        )
      );

      let toTokenDetails = tokenOptions.find((token) =>
        token.chainIds?.some(
          (chain) =>
            chain.address.toLowerCase() ===
              params.toToken.tokenAddress.toLowerCase() &&
            chain.chainId === params.toToken.chainId
        )
      );

      // If either token details are not found, try to fetch them using searchToken
      if (!fromTokenDetails) {
        if (params.fromToken.metadata) {
          fromTokenDetails = {
            name: params.fromToken.metadata.tokenName,
            symbol: params.fromToken.metadata.tokenSymbol,
            decimals: params.fromToken.metadata.decimals,
            logoURI: params.fromToken.metadata.logoURI,
            chainIds: params.fromToken.metadata.chainIds.map((chain) => ({
              chainId: chain.chainId,
              address: chain.address,
            })),
          } as TokenInfo;
        } else {
          const fromTokenSearch = await searchToken(
            params.fromToken.tokenAddress,
            params.fromToken.chainId,
            sdkKey
          );
          if (fromTokenSearch?.success) {
            fromTokenDetails = {
              name: fromTokenSearch.data.name,
              symbol: fromTokenSearch.data.symbol,
              decimals: fromTokenSearch.data.decimals,
              logoURI: fromTokenSearch.data.logoURI,
              chainIds: fromTokenSearch.data.chainIds.map((chain) => ({
                chainId: parseInt(chain.chainId),
                address: chain.address,
              })),
            } as TokenInfo;
          }
        }
      }

      if (!toTokenDetails) {
        if (params.toToken.metadata) {
          toTokenDetails = {
            name: params.toToken.metadata.tokenName,
            symbol: params.toToken.metadata.tokenSymbol,
            decimals: params.toToken.metadata.decimals,
            logoURI: params.toToken.metadata.logoURI,
            chainIds: params.toToken.metadata.chainIds.map((chain) => ({
              chainId: chain.chainId,
              address: chain.address,
            })),
          } as TokenInfo;
        } else {
          const toTokenSearch = await searchToken(
            params.toToken.tokenAddress,
            params.toToken.chainId,
            sdkKey
          );
          if (toTokenSearch?.success) {
            toTokenDetails = {
              name: toTokenSearch.data.name,
              symbol: toTokenSearch.data.symbol,
              decimals: toTokenSearch.data.decimals,
              logoURI: toTokenSearch.data.logoURI,
              chainIds: toTokenSearch.data.chainIds.map((chain) => ({
                chainId: parseInt(chain.chainId),
                address: chain.address,
              })),
            } as TokenInfo;
          }
        }
      }

      if (!fromTokenDetails || !toTokenDetails) {
        throw new Error(
          "Token details not found for " +
            params.fromToken.tokenAddress +
            " or " +
            params.toToken.tokenAddress
        );
      }

      const metadata = {
        inputToken: {
          tokenName: fromTokenDetails.name,
          tokenSymbol: fromTokenDetails.symbol,
          tokenAddress: params.fromToken.tokenAddress,
          decimals: fromTokenDetails.decimals,
          logoURI: fromTokenDetails.logoURI || fromTokenDetails.icon || "",
        },
        outputToken: {
          tokenName: toTokenDetails.name,
          tokenSymbol: toTokenDetails.symbol,
          tokenAddress: params.toToken.tokenAddress,
          decimals: toTokenDetails.decimals,
          logoURI: toTokenDetails.logoURI || toTokenDetails.icon || "",
        },
      };

      // Get user balance for the input token
      const userBalance = cryptoBalance?.data?.find((token: any) =>
        token.chainIds?.some((chain: any) =>
          fromTokenDetails?.chainIds?.some(
            (tokenChain) =>
              tokenChain.address.toLowerCase() ===
                chain.address.toLowerCase() &&
              tokenChain.chainId === chain.chainId
          )
        )
      );

      // Default balance if user doesn't have the token
      const defaultBalance = [
        {
          chainId: params.fromToken.chainId,
          address: params.fromToken.tokenAddress,
          balance: "0",
        },
      ];

      let data;
      if (params.fromToken.chainId === BITCOIN_MAINNET_CHAIN_ID) {
        data = await executeFromBTCSwapFlow(
          {
            username: userName,
            amount: params.fromToken.amount,
            outputToken: params.toToken.tokenAddress,
            outputChainId: params.toToken.chainId,
            metadata,
          },
          sdkKey
        );
      } else if (params.toToken.chainId === BITCOIN_MAINNET_CHAIN_ID) {
        data = await executeToBTCSwapFlow(
          {
            username: userName,
            userBalance: userBalance?.chainIds || defaultBalance,
            inputAmount: params.fromToken.amount,
            proMode: false,
            metadata,
          },
          sdkKey
        );
      } else {
        // Create swap parameters
        const swapParams = {
          username: userName,
          userBalance: userBalance?.chainIds || defaultBalance,
          outputToken: params.toToken.tokenAddress,
          outputChainId: params.toToken.chainId,
          inputAmount: params.fromToken.amount,
          metadata,
          proMode: false,
        };

        // Use appropriate swap function based on isBungee parameter
        if (params.provider === ProtocolProvider.BUNGEE) {
          data = await executeBungeeSwapFlow(swapParams, sdkKey);
        } else if (params.provider === ProtocolProvider.LIFI) {
          data = await executeLiFiSwapFlow(swapParams, sdkKey);
        } else {
          data = await executeSwapFlow(swapParams, sdkKey);
        }
      }

      // Dismiss loading toast
      toast.dismiss(loadingToast);

      if (
        data?.transactionDetails?.overallStatus === "PENDING" ||
        data?.transactionDetails?.overallStatus === "COMPLETED"
      ) {
        console.log("Transaction initiated:", data);

        // Show swap initiated toast with loader
        const swapInitiatedToast = toast.loading("Swap initiated...");

        setActivity(data);

        // Start polling transaction status
        const multiTransactionId = data.transactionDetails.multiTransactionId;

        const pollTransactionStatus = async () => {
          let isCompleted = false;
          let attempts = 0;
          const maxAttempts = 60; // Poll for up to 5 minutes (60 * 5 seconds)

          while (!isCompleted && attempts < maxAttempts) {
            try {
              await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5 seconds

              const transactionInfo = await getTransactionInfo(
                multiTransactionId,
                sdkKey
              );

              if (transactionInfo) {
                const status = transactionInfo.overallStatus;

                if (status === "COMPLETED") {
                  toast.dismiss(swapInitiatedToast);
                  const successToastId = toast.success(
                    <div
                      style={{
                        display: "flex",
                        alignItems: "center",
                        gap: "12px",
                      }}
                    >
                      <span>Transaction completed</span>
                      <button
                        onClick={() => {
                          if (walletSDK) {
                            // Store the transaction data in localStorage
                            if (typeof window !== "undefined") {
                              localStorage.setItem(
                                "enclave_transaction_data",
                                JSON.stringify({
                                  transactionId: multiTransactionId,
                                })
                              );
                            }
                            walletSDK.openWalletModal();
                          }
                          toast.dismiss(successToastId);
                        }}
                        style={{
                          color: "white",
                          borderRadius: "20px",
                          padding: "4px 8px",
                          fontSize: "13px",
                          fontWeight: "500",
                          cursor: "pointer",
                          background: "black",
                          border: "1px solid rgba(255, 255, 255, 0.33)",
                        }}
                      >
                        View
                      </button>
                    </div>,
                    { duration: 6000 }
                  );
                  isCompleted = true;
                  // Refresh balance after successful completion
                  refreshBalance(false);
                } else if (status === "FAILED") {
                  toast.dismiss(swapInitiatedToast);
                  toast.error("Transaction failed");
                  isCompleted = true;
                }
                // If status is still PENDING, continue polling
              }

              attempts++;
            } catch (error) {
              console.error("Error polling transaction status:", error);
              attempts++;
            }
          }

          // If polling times out
          if (!isCompleted) {
            toast.dismiss(swapInitiatedToast);
            toast.error("Transaction status check timed out");
          }
        };

        // Start polling in background
        pollTransactionStatus();
      } else {
        console.error("Transaction failed:", data);

        // Show error toast
        toast.error("Transaction failed");
      }

      return data;
    } catch (error) {
      console.error("Error executing swap:", error);

      // Dismiss loading toast and show error toast
      toast.dismiss(loadingToast);
      toast.error("Transaction failed");

      throw error;
    }
  };

  const value = {
    walletSDK,
    isLoggedIn,
    username,
    walletAddress,
    solanaAddress,
    bitcoinWalletAddress,
    balance,
    connect,
    disconnect,
    swap,
    tokenOptions,
    tokensLoading,
    refreshTokenOptions,
    cryptoBalance,
    loading,
    setLoading,
    refreshBalance,
    showActivity,
    setShowActivity,
    activity,
    setActivity,
    calculateQuote,
    executeSwap,
  };

  return (
    <ToastProvider>
      <WalletContext.Provider value={value}>
        <div style={{ fontFamily: "Inter, sans-serif" }}>{children}</div>
      </WalletContext.Provider>
    </ToastProvider>
  );
};
