'use client'
import { __awaiter } from '../../../../../_virtual/_tslib.js';
import { useState, useCallback, useEffect } from 'react';
import { HardwareWalletEnum } from '@dynamic-labs/sdk-api-core';
import { CustomError, MissingPublicAddressError } from '@dynamic-labs/utils';
import { getWalletConnectorByKey, logger, isHardwareWalletConnector, getWalletProvider } from '@dynamic-labs/wallet-connector-core';
import { useInternalUserWallets } from '../../../context/UserWalletsContext/UserWalletsContext.js';
import '@dynamic-labs-sdk/client/core';
import '../../../client/client.js';
import '@dynamic-labs-sdk/client';
import '../../../config/ApiEndpoint.js';
import '../../constants/values.js';
import '@dynamic-labs/multi-wallet';
import 'react-international-phone';
import '../../constants/colors.js';
import '@dynamic-labs/iconic';
import 'react/jsx-runtime';
import '../../../context/ViewContext/ViewContext.js';
import '../../../shared/logger.js';
import '@dynamic-labs/wallet-book';
import '../../../shared/consts/index.js';
import '../../../store/state/nonce/nonce.js';
import { shouldManuallyReconnectOnRefresh } from '../../functions/shouldManuallyReconnectOnRefresh/shouldManuallyReconnectOnRefresh.js';
import { updatePrimaryWalletId } from '../../functions/updatePrimaryWalletId/updatePrimaryWalletId.js';
import { getWalletUniqueId } from '../../functions/getWalletUniqueId/getWalletUniqueId.js';
import { useConnectedWalletsInfo, setConnectedWalletsInfo } from '../../../store/state/connectedWalletsInfo/connectedWalletsInfo.js';
import '../../../store/state/dynamicContextProps/dynamicContextProps.js';
import { resetPrimaryWalletId } from '../../../store/state/primaryWalletId/primaryWalletId.js';
import { dynamicEvents } from '../../../events/dynamicEvents.js';
import { createVisit } from '../../../data/api/session/session.js';
import '@dynamic-labs/locale';
import { getAuthMode } from '../../../store/state/authMode/authMode.js';
import { getWalletConnectorForWallet } from '../../functions/getWalletConnectorForWallet/getWalletConnectorForWallet.js';
import { isConnectOnly } from '../authenticationHooks/helpers/isConnectOnly.js';
import { useDebounce } from '../useDebounce/useDebounce.js';
import { HandleConnectWalletRejectionError } from './HandleConnectWalletRejectionError.js';
import { updateUserWalletsFromConnectedWallets } from './updateUserWalletsFromConnectedWallets/updateUserWalletsFromConnectedWallets.js';

const useConnectWallet = ({ enableVisitTrackingOnConnectOnly, environmentId, primaryWalletId, walletConnectorOptions, handleConnectedWallet, setShowAuthFlow, isBridgeFlow, }) => {
    const connectedWalletsInfo = useConnectedWalletsInfo();
    const [connectedWallets, _setConnectedWallets] = useState([]);
    const { setUserWallets, addedWalletsIds, removedWalletsIds } = useInternalUserWallets();
    /** This wrapper is to ensure userWallets is always properly updated alongside connectedWallets */
    const setConnectedWallets = useCallback((newWallets) => {
        // Prevent unnecessary re-renders when state is empty
        _setConnectedWallets((prevWallets) => {
            if (prevWallets.length === 0 && newWallets.length === 0) {
                return prevWallets;
            }
            return newWallets;
        });
        setUserWallets((userWallets) => updateUserWalletsFromConnectedWallets(userWallets, newWallets));
    }, [setUserWallets]);
    const disconnectWallet = useCallback((walletId) => __awaiter(void 0, void 0, void 0, function* () {
        const walletToDisconnect = connectedWallets.find((wallet) => wallet.id === walletId);
        yield (walletToDisconnect === null || walletToDisconnect === void 0 ? void 0 : walletToDisconnect.connector.endSession());
        const updatedConnectedWalletsInfo = connectedWalletsInfo.filter((wallet) => wallet.id !== walletId);
        setConnectedWalletsInfo(updatedConnectedWalletsInfo);
        removedWalletsIds.current.push(walletId);
        if (walletId !== primaryWalletId) {
            return;
        }
        const hasConnectedWallet = updatedConnectedWalletsInfo.length > 0;
        if (isBridgeFlow || !hasConnectedWallet) {
            resetPrimaryWalletId();
        }
        else {
            updatePrimaryWalletId(updatedConnectedWalletsInfo[0].id);
        }
    }), [
        connectedWallets,
        connectedWalletsInfo,
        isBridgeFlow,
        primaryWalletId,
        removedWalletsIds,
    ]);
    // The function to update and define the connectedWallets list.
    // It should be called on the first render, when connectedWalletsInfo updates and when walletConnectorOptions updates.
    // To avoid unnecessary updates that are causing issues with connectors added asynchronously,
    // we're debouncing this function
    const updateConnectedWalletsList = useDebounce(() => __awaiter(void 0, void 0, void 0, function* () {
        const walletConnectors = walletConnectorOptions.map((wallet) => wallet.walletConnector);
        if (!walletConnectors.length) {
            return;
        }
        const updatedConnectedWallets = (yield Promise.all(connectedWalletsInfo.map((storedConnectedWalletInfo) => __awaiter(void 0, void 0, void 0, function* () {
            const walletConnector = getWalletConnectorByKey(walletConnectors, storedConnectedWalletInfo.walletConnectorKey, storedConnectedWalletInfo.walletChain);
            if (!walletConnector) {
                logger.error('Could not find walletConnector: ' +
                    storedConnectedWalletInfo.walletConnectorKey);
                yield disconnectWallet(storedConnectedWalletInfo.id);
                return null;
            }
            if (isHardwareWalletConnector(walletConnector) &&
                storedConnectedWalletInfo.hardwareWallet ===
                    HardwareWalletEnum.Ledger) {
                walletConnector.isHardwareWalletEnabled = true;
            }
            const [walletAddress] = yield walletConnector.getConnectedAccounts();
            if (!walletAddress) {
                yield disconnectWallet(storedConnectedWalletInfo.id);
                return null;
            }
            const additionalAddresses = yield walletConnector.getAdditionalAddresses(walletAddress);
            const walletChain = walletConnector.connectedChain;
            const walletObject = walletConnector.createWallet({
                additionalAddresses,
                address: walletAddress,
                chain: walletChain,
                connector: walletConnector,
                id: storedConnectedWalletInfo.id,
                isAuthenticated: false,
                key: walletConnector.key,
            });
            return walletObject;
        })))).filter((wallet) => Boolean(wallet));
        setConnectedWallets(updatedConnectedWallets);
    }), 300);
    // Generate the connectedWallets list when connectedWalletsInfo (localStorage) or memoized wallet connector updates
    useEffect(() => {
        updateConnectedWalletsList();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [connectedWalletsInfo, walletConnectorOptions]);
    const updateConnectedWalletById = useCallback((walletId, walletObject) => {
        var _a;
        const clonedConnectedWalletsList = [...connectedWallets];
        const connectedWalletIndex = clonedConnectedWalletsList.findIndex((connectedWallet) => connectedWallet.id === walletId);
        if (connectedWalletIndex < 0) {
            return;
        }
        clonedConnectedWalletsList[connectedWalletIndex] =
            clonedConnectedWalletsList[connectedWalletIndex].connector.createWallet(Object.assign(Object.assign({}, clonedConnectedWalletsList[connectedWalletIndex]), { address: (_a = walletObject.address) !== null && _a !== void 0 ? _a : clonedConnectedWalletsList[connectedWalletIndex].address, connector: getWalletConnectorForWallet(clonedConnectedWalletsList[connectedWalletIndex]) }));
        setConnectedWallets(clonedConnectedWalletsList);
    }, [connectedWallets, setConnectedWallets]);
    // Keeps connected wallet data inside localStorage
    const applyConnectedWalletToStore = useCallback(({ walletId, walletConnectorKey, walletChain, provider, }) => {
        const updatedConnectedWalletsInfo = [...connectedWalletsInfo];
        const walletConnectors = walletConnectorOptions.map((wallet) => wallet.walletConnector);
        const walletConnector = getWalletConnectorByKey(walletConnectors, walletConnectorKey, walletChain);
        const hardwareWallet = walletConnector &&
            isHardwareWalletConnector(walletConnector) &&
            walletConnector.isHardwareWalletEnabled
            ? HardwareWalletEnum.Ledger
            : undefined;
        updatedConnectedWalletsInfo.push({
            hardwareWallet,
            id: walletId,
            provider,
            walletChain,
            walletConnectorKey,
        });
        setConnectedWalletsInfo(updatedConnectedWalletsInfo);
    }, [connectedWalletsInfo, walletConnectorOptions]);
    // Updates connected wallet (network and address) and verifies connected wallet connection
    const refreshConnectedWallet = (walletId, walletConnector) => __awaiter(void 0, void 0, void 0, function* () {
        if (shouldManuallyReconnectOnRefresh(walletConnector)) {
            yield walletConnector.connect();
        }
        const currentWalletConnectorAddress = yield walletConnector.getAddress();
        if (currentWalletConnectorAddress && handleConnectedWallet) {
            const shouldProceedWithConnection = yield handleConnectedWallet({
                address: currentWalletConnectorAddress,
                chain: walletConnector.connectedChain,
                connector: walletConnector,
            });
            if (!shouldProceedWithConnection) {
                logger.info('Connection was not established because handleConnectedWallet returned false');
                disconnectWallet(walletId);
                setShowAuthFlow(false, { emitCancelAuth: true });
                return;
            }
        }
        updateConnectedWalletById(walletId, {
            address: currentWalletConnectorAddress,
        });
    });
    const connectWallet = useCallback((walletConnector, getAddressOpts, options) => __awaiter(void 0, void 0, void 0, function* () {
        const { applyHandleConnectedWallet = true } = options !== null && options !== void 0 ? options : {};
        let walletAddress;
        try {
            walletAddress = yield walletConnector.getAddress(getAddressOpts);
        }
        catch (error) {
            if (error instanceof CustomError) {
                dynamicEvents.emit('walletConnectionFailed', walletConnector, error);
                throw error;
            }
            dynamicEvents.emit('walletConnectionFailed', walletConnector, error);
            throw new MissingPublicAddressError();
        }
        // Throw a generic error for any errors we're not processing above
        if (!walletAddress) {
            const missingPublicAddressError = new MissingPublicAddressError();
            dynamicEvents.emit('walletConnectionFailed', walletConnector, missingPublicAddressError);
            throw missingPublicAddressError;
        }
        if (handleConnectedWallet && applyHandleConnectedWallet) {
            const shouldProceedWithConnection = yield handleConnectedWallet({
                address: walletAddress,
                chain: walletConnector.connectedChain,
                connector: walletConnector,
            });
            if (!shouldProceedWithConnection) {
                logger.info('Connection was not established because handleConnectedWallet returned false');
                setShowAuthFlow(false);
                dynamicEvents.emit('walletConnectionFailed', walletConnector, new HandleConnectWalletRejectionError(walletConnector));
                return;
            }
        }
        const isWalletStored = connectedWalletsInfo.some(({ walletConnectorKey: storedWalletConnectorKey }) => storedWalletConnectorKey === walletConnector.key);
        const walletChain = walletConnector.connectedChain;
        const authMode = getAuthMode();
        const shouldCreateVisit = !isWalletStored &&
            (authMode !== 'connect-only' || enableVisitTrackingOnConnectOnly);
        if (shouldCreateVisit) {
            // send information to backend to kick off background jobs
            // so verify/sign on the next step could go by more quickly
            // this is async work, but DO NOT AWAIT
            void createVisit({
                authMode,
                chain: walletChain || '',
                environmentId,
                publicWalletAddress: walletAddress,
                walletName: walletConnector.key,
                walletProvider: getWalletProvider(walletConnector),
            });
        }
        // On connect and sign, we don't want to add this wallet to userWallets
        // until it has been signed. Therefore, we don't add it to
        // the connectedWallets array because userWallets draws from it.
        // In this scenario we only need the wallet address, since the id will come from somewhere else.
        // In fact, there is a refactor planned to never use the connect-wallet-X ids anymore:
        // https://linear.app/dynamic-labs/issue/QNTM-784/rework-the-way-we-compute-wallet-ids
        if (!isConnectOnly())
            return { address: walletAddress, id: '' };
        const walletId = getWalletUniqueId({
            address: walletAddress,
            chain: walletConnector.connectedChain,
            connectorKey: walletConnector.key,
        });
        if (!primaryWalletId) {
            updatePrimaryWalletId(walletId);
        }
        addedWalletsIds.current.push(walletId);
        if (isWalletStored) {
            yield updateConnectedWalletsList();
        }
        else {
            applyConnectedWalletToStore({
                provider: getWalletProvider(walletConnector),
                walletAddress: walletAddress,
                walletChain,
                walletConnectorKey: walletConnector.key,
                walletId,
            });
        }
        return { address: walletAddress, id: walletId };
    }), [
        addedWalletsIds,
        applyConnectedWalletToStore,
        connectedWalletsInfo,
        enableVisitTrackingOnConnectOnly,
        environmentId,
        handleConnectedWallet,
        primaryWalletId,
        setShowAuthFlow,
        updateConnectedWalletsList,
    ]);
    const getConnectedWalletById = useCallback((walletId) => connectedWallets.find((wallet) => wallet.id === walletId), [connectedWallets]);
    return {
        connectWallet,
        connectedWallets,
        connectedWalletsInfo,
        disconnectWallet,
        getConnectedWalletById,
        refreshConnectedWallet,
    };
};

export { useConnectWallet };
