'use client'
import { __awaiter } from '../../../../../_virtual/_tslib.js';
import { useCallback } from 'react';
import { StorageService, InvalidEmbeddedWalletSessionKeyError, tracing } from '@dynamic-labs/utils';
import { logger } from '@dynamic-labs/wallet-connector-core';
import '@dynamic-labs-sdk/client/core';
import '@dynamic-labs/sdk-api-core';
import '../../../client/client.js';
import '@dynamic-labs-sdk/client';
import '../../../config/ApiEndpoint.js';
import '@dynamic-labs/iconic';
import 'react/jsx-runtime';
import '../../../context/ViewContext/ViewContext.js';
import '../../../shared/logger.js';
import '@dynamic-labs/wallet-book';
import { SECURE_ENCLAVE_WALLET_SESSION_KEYS, SECURE_ENCLAVE_WALLET_SESSION_KEYS_STORAGE_OPTIONS } from '../../constants/localStorage.js';
import '../../constants/colors.js';
import '../../constants/values.js';
import '../../../shared/consts/index.js';
import '@dynamic-labs/multi-wallet';
import 'react-international-phone';
import '../../../store/state/nonce/nonce.js';
import { registerSessionKey } from '../../../data/api/embeddedWallets/embeddedWallets.js';
import '@dynamic-labs/locale';
import '../../../store/state/dynamicContextProps/dynamicContextProps.js';
import '../../../store/state/primaryWalletId/primaryWalletId.js';
import '../../../store/state/connectedWalletsInfo/connectedWalletsInfo.js';
import { toEncodedFormat, p256Sign } from '../../functions/keyService/keyService.js';
import { dynamicEvents } from '../../../events/dynamicEvents.js';
import { useOnboardingCompleteUserProfile } from '../../../client/extension/user/useOnboardingCompleteUser/useOnboardingCompleteUser.js';
import { getTurnkeyWalletId } from '../../functions/getTurnkeyWalletId/getTurnkeyWalletId.js';
import { generateAndSaveSessionKey, generateSessionKey } from '../../functions/embeddedWalletSessionKeysUtils/embeddedWalletSessionKeysUtils.js';

const useEmbeddedWalletSessionKeys = ({ environmentId, projectSettings, }) => {
    const user = useOnboardingCompleteUserProfile();
    const getDecodedSessionKeys = useCallback(() => {
        const sessionKeysSS = StorageService.getItem(SECURE_ENCLAVE_WALLET_SESSION_KEYS, SECURE_ENCLAVE_WALLET_SESSION_KEYS_STORAGE_OPTIONS);
        return sessionKeysSS
            ? JSON.parse(Buffer.from(sessionKeysSS, 'base64').toString())
            : undefined;
    }, []);
    const registerHelper = useCallback((_a) => __awaiter(void 0, [_a], void 0, function* ({ walletId, environmentId, privateKey, privateKeyJwk, publicKey, prevSessionKeySignature, user, }) {
        let resp;
        const turnkeyWalletId = getTurnkeyWalletId(walletId, user.verifiedCredentials);
        try {
            resp = yield registerSessionKey({
                environmentId,
                prevSessionKeySignature,
                publicKey,
                walletId: turnkeyWalletId,
            });
        }
        catch (error) {
            if (error instanceof InvalidEmbeddedWalletSessionKeyError) {
                // this can happen if the public key passed during initial registration
                // does not match the root session public key that the backend expects
                logger.warn('Invalid embedded wallet session key. Re-authentication is required to create new session keys.');
                dynamicEvents.emit('triggerLogout');
            }
            logger.error('Error registering session key', { error });
            throw error;
        }
        const expirationDate = new Date(resp.expiresAt * 1000);
        tracing.logEvent('session-key', 'Created new session key', tracing.formatObject({
            expirationDate,
            publicKey,
        }));
        StorageService.setItem(SECURE_ENCLAVE_WALLET_SESSION_KEYS, toEncodedFormat(publicKey, privateKey, privateKeyJwk, true, expirationDate), SECURE_ENCLAVE_WALLET_SESSION_KEYS_STORAGE_OPTIONS);
        return {
            expirationDate,
            privateKey,
            publicKey,
        };
    }), []);
    // scenario 1: first time session register on first transaction.
    // scenario 2: refresh/rerender page - session still active and registered (keys were previously generated
    //             but never registered with the backend)
    // scenario 3: session expired and needs to be refreshed
    // Helper to get decoded session keys from storage
    /**
     * @param ignoreRestore - if true, will not use the session keys if they are found in storage and didn't expire
     * @returns - the session key
     */
    const registerEmbeddedWalletSessionKey = useCallback((_b) => __awaiter(void 0, [_b], void 0, function* ({ ignoreRestore = false, walletId, }) {
        if (!user) {
            throw new Error('User not found');
        }
        // check if session keys are already stored in session storage
        let existingSessionKey = getDecodedSessionKeys();
        tracing.logEvent('session-key', 'registerEmbeddedWalletSessionKey', tracing.formatObject({
            hasDecodedSessionKeys: Boolean(existingSessionKey),
            publicKey: existingSessionKey === null || existingSessionKey === void 0 ? void 0 : existingSessionKey.publicKey,
        }));
        if (!existingSessionKey) {
            // We should never be in a situation where we don't have session keys in storage.
            // But right now we are in a situation where we don't have session keys in storage,
            // in many situations.
            // but this is a fallback to ensure that we always have session keys
            // To note that this would only work if chaining is disabled in the backend
            // since the original key was registered for chaining, and now we don't have it
            // it will fail when interacting with the backend if chaining is enabled.
            // This logic will be removed and reverted back to logging out once we find
            // the culprit that cause many of these to happen.
            logger.error('Session keys not found in storage. Generating new session keys.', { userId: user === null || user === void 0 ? void 0 : user.userId });
            existingSessionKey = yield generateAndSaveSessionKey();
        }
        const isSessionKeyValid = existingSessionKey.registered &&
            existingSessionKey.expirationDate &&
            new Date() <= new Date(existingSessionKey.expirationDate) &&
            !ignoreRestore;
        tracing.logEvent('session-key', 'isSessionKeyValid', tracing.formatObject({
            isSessionKeyValid: Boolean(isSessionKeyValid),
        }));
        if (isSessionKeyValid) {
            // scenario 1 - session is valid and registered
            return existingSessionKey;
        }
        tracing.logEvent('session-key', 'decodedSessionKeys', tracing.formatObject({
            publicKey: existingSessionKey.publicKey,
            registered: existingSessionKey.registered,
        }));
        let publicKey;
        let privateKey;
        let privateKeyJwk;
        let prevSessionKeySignature = undefined;
        if (!existingSessionKey.registered) {
            // scenario 2 - session needs to be registered
            ({ publicKey, privateKey, privateKeyJwk } = existingSessionKey);
        }
        else {
            // scenario 3 - session expired singing with the old key and generating a new one
            prevSessionKeySignature = yield p256Sign(existingSessionKey.privateKeyJwk, user.sessionId);
            //  Note that the key is only generated here, it will get saved to storage in the registerHelper
            ({ publicKey, privateKey, privateKeyJwk } = yield generateSessionKey());
            tracing.logEvent('session-key', 'Loaded prevSessionKeySignature', tracing.formatObject({ prevSessionKeySignature, publicKey }));
        }
        return registerHelper({
            environmentId,
            prevSessionKeySignature,
            privateKey,
            privateKeyJwk,
            publicKey,
            user,
            walletId,
        });
    }), [environmentId, getDecodedSessionKeys, registerHelper, user]);
    const getSessionPublicKey = useCallback(() => {
        const decodedSessionKeys = getDecodedSessionKeys();
        if (!(decodedSessionKeys === null || decodedSessionKeys === void 0 ? void 0 : decodedSessionKeys.publicKey)) {
            tracing.logEvent('session-key', 'getSessionPublicKey', 'Could not find session keys.');
            throw new Error('Could not find session keys.');
        }
        tracing.logEvent('session-key', 'getSessionPublicKey', tracing.formatObject({
            publicKey: decodedSessionKeys === null || decodedSessionKeys === void 0 ? void 0 : decodedSessionKeys.publicKey,
        }));
        return decodedSessionKeys === null || decodedSessionKeys === void 0 ? void 0 : decodedSessionKeys.publicKey;
    }, [getDecodedSessionKeys]);
    // this is a temporary fix to ensure that session keys are registered on signin for all wallets
    // the user is undefined at this point in the auth flow and we cant check the users wallet version
    // so we just return true for now
    const shouldRegisterSessionKeysOnSignin = useCallback(() => true, []);
    const getSignedSessionId = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
        const sessionId = user === null || user === void 0 ? void 0 : user.sessionId;
        if (!sessionId) {
            throw new Error('Session ID not found');
        }
        const decodedSessionKeys = getDecodedSessionKeys();
        if (!(decodedSessionKeys === null || decodedSessionKeys === void 0 ? void 0 : decodedSessionKeys.privateKeyJwk)) {
            throw new Error('Private key JWK not found');
        }
        const signedSessionId = yield p256Sign(decodedSessionKeys.privateKeyJwk, sessionId);
        return signedSessionId;
    }), [getDecodedSessionKeys, user === null || user === void 0 ? void 0 : user.sessionId]);
    const removeSessionKey = useCallback(() => {
        tracing.logEvent('session-key', 'removeSessionKey');
        StorageService.removeItem(SECURE_ENCLAVE_WALLET_SESSION_KEYS, SECURE_ENCLAVE_WALLET_SESSION_KEYS_STORAGE_OPTIONS);
    }, []);
    return {
        generateSessionKey: generateAndSaveSessionKey,
        getSessionPublicKey,
        getSignedSessionId,
        registerEmbeddedWalletSessionKey,
        removeSessionKey,
        shouldRegisterSessionKeysOnSignin,
    };
};

export { useEmbeddedWalletSessionKeys };
