import { jsx as _jsx } from "react/jsx-runtime";
import { LiFiWalletManagement, readActiveWallets, supportedWallets, addChain as walletAgnosticAddChain, switchChainAndAddToken as walletAgnosticAddToken, switchChain as walletAgnosticSwitchChain, } from '@lifi/wallet-management';
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
import { useWidgetConfig } from '../WidgetProvider';
const liFiWalletManagement = new LiFiWalletManagement();
const stub = () => {
    throw new Error(`You forgot to wrap your component in <${WalletProvider.name}>.`);
};
const initialContext = {
    connect: stub,
    disconnect: stub,
    switchChain: stub,
    addChain: stub,
    addToken: stub,
    account: {},
};
const WalletContext = createContext(initialContext);
export const useWallet = () => useContext(WalletContext);
export const WalletProvider = ({ children }) => {
    const { walletManagement } = useWidgetConfig();
    const [account, setAccount] = useState({});
    const [currentWallet, setCurrentWallet] = useState();
    const handleWalletUpdate = async (wallet) => {
        setCurrentWallet(wallet);
        const account = await extractAccountFromSigner(wallet?.account?.signer);
        setAccount(account);
    };
    const connect = useCallback(async (wallet) => {
        if (walletManagement) {
            const signer = await walletManagement.connect();
            const account = await extractAccountFromSigner(signer);
            setAccount(account);
            return;
        }
        await liFiWalletManagement.connect(wallet);
        wallet.on('walletAccountChanged', handleWalletUpdate);
        handleWalletUpdate(wallet);
    }, [walletManagement]);
    const disconnect = useCallback(async () => {
        if (walletManagement) {
            await walletManagement.disconnect();
            setAccount({});
            return;
        }
        if (currentWallet) {
            await liFiWalletManagement.disconnect(currentWallet);
            currentWallet.removeAllListeners();
            handleWalletUpdate(undefined);
        }
    }, [currentWallet, walletManagement]);
    const switchChain = useCallback(async (chainId) => {
        try {
            if (walletManagement?.switchChain) {
                const signer = await walletManagement.switchChain(chainId);
                const account = await extractAccountFromSigner(signer);
                setAccount(account);
                return signer;
            }
            else if (!currentWallet) {
                const provider = account.signer?.provider;
                if (!provider) {
                    throw new Error(`Switch chain: No provider available`);
                }
                await walletAgnosticSwitchChain(provider, chainId);
            }
            else {
                await currentWallet?.switchChain(chainId);
                handleWalletUpdate(currentWallet);
            }
            // TODO: this will fail if it's not created with ethers 'any' network, replace with the new signer when possible
            return account.signer;
        }
        catch {
            return account.signer;
        }
    }, [account.signer, currentWallet, walletManagement]);
    const addChain = useCallback(async (chainId) => {
        try {
            if (walletManagement?.addChain) {
                return walletManagement.addChain(chainId);
            }
            else if (!currentWallet) {
                const provider = account.signer?.provider;
                if (!provider) {
                    throw new Error(`Add chain: No provider available`);
                }
                await walletAgnosticAddChain(provider, chainId);
            }
            else {
                await currentWallet?.addChain(chainId);
                handleWalletUpdate(currentWallet);
            }
            return true;
        }
        catch {
            return false;
        }
    }, [account.signer?.provider, currentWallet, walletManagement]);
    const addToken = useCallback(async (chainId, token) => {
        try {
            if (walletManagement?.addToken) {
                return walletManagement.addToken(token, chainId);
            }
            else if (!currentWallet) {
                const provider = account.signer?.provider;
                if (!provider) {
                    throw new Error(`Add token: No provider available`);
                }
                await walletAgnosticAddToken(provider, chainId, token);
            }
            else {
                await currentWallet?.addToken(chainId, token);
                handleWalletUpdate(currentWallet);
            }
        }
        catch { }
    }, [account.signer?.provider, currentWallet, walletManagement]);
    useEffect(() => {
        const autoConnect = async () => {
            const persistedActiveWallets = readActiveWallets();
            const activeWallets = supportedWallets.filter((wallet) => persistedActiveWallets.some((perstistedWallet) => perstistedWallet.name === wallet.name));
            if (!activeWallets.length) {
                return;
            }
            await liFiWalletManagement.autoConnect(activeWallets);
            activeWallets[0].on('walletAccountChanged', handleWalletUpdate);
            handleWalletUpdate(activeWallets[0]);
        };
        autoConnect();
    }, []);
    // keep widget in sync with changing external signer object
    useEffect(() => {
        if (walletManagement) {
            let ignore = false;
            const updateAccount = async () => {
                const account = await extractAccountFromSigner(walletManagement?.signer);
                // we should ignore the update if there is a newer one
                if (!ignore) {
                    setAccount(account);
                }
            };
            updateAccount();
            return () => {
                ignore = true;
            };
        }
    }, [walletManagement]);
    const value = useMemo(() => ({
        connect,
        disconnect,
        switchChain,
        addChain,
        addToken,
        account,
    }), [account, addChain, addToken, connect, disconnect, switchChain]);
    return (_jsx(WalletContext.Provider, { value: value, children: children }));
};
export const extractAccountFromSigner = async (signer) => {
    try {
        return {
            address: await signer?.getAddress(),
            isActive: (signer && !!(await signer.getAddress()) === null) || !!signer,
            signer,
            chainId: await signer?.getChainId(),
        };
    }
    catch (error) {
        console.error(error);
        return {};
    }
};
