package com.tangem.feature.wallet.child.wallet.model

import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.arkivanov.decompose.router.slot.activate
import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.pay.model.MainScreenCustomerInfo
import com.tangem.domain.pay.model.TangemPayCustomerInfoError
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
import com.tangem.domain.settings.*
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.domain.wallets.usecase.*
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.*
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.hotwallet.HotWalletFeatureToggles
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.tangempay.TangemPayFeatureToggles
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.*
import timber.log.Timber
import javax.inject.Inject

private const val TANGEM_PAY_UPDATE_INTERVAL = 60_000L

@Suppress("LongParameterList", "LargeClass")
@Stable
@ModelScoped
internal class WalletModel @Inject constructor(
    override val dispatchers: CoroutineDispatcherProvider,
    private val stateHolder: WalletStateController,
    private val clickIntents: WalletClickIntents,
    private val walletEventSender: WalletEventSender,
    private val walletsUpdateActionResolver: WalletsUpdateActionResolver,
    private val walletScreenContentLoader: WalletScreenContentLoader,
    private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
    private val getWalletsUseCase: GetWalletsUseCase,
    private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase,
    private val shouldShowMarketsTooltipUseCase: ShouldShowMarketsTooltipUseCase,
    private val setWalletFirstTimeUsageUseCase: SetWalletFirstTimeUsageUseCase,
    private val canUseBiometryUseCase: CanUseBiometryUseCase,
    private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled,
    private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
    private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
    private val walletNameMigrationUseCase: WalletNameMigrationUseCase,
    private val refreshMultiCurrencyWalletQuotesUseCase: RefreshMultiCurrencyWalletQuotesUseCase,
    private val walletImageResolver: WalletImageResolver,
    private val tokenListStore: MultiWalletTokenListStore,
    private val onrampStatusFactory: OnrampStatusFactory,
    private val analyticsEventsHandler: AnalyticsEventHandler,
    private val walletContentFetcher: WalletContentFetcher,
    private val observeAndClearNFTCacheIfNeedUseCase: ObserveAndClearNFTCacheIfNeedUseCase,
    private val walletDeepLinkActionListener: WalletDeepLinkActionListener,
    private val notificationsRepository: NotificationsRepository,
    private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
    private val setNotificationsEnabledUseCase: SetNotificationsEnabledUseCase,
    private val shouldSaveUserWalletsSyncUseCase: ShouldSaveUserWalletsSyncUseCase,
    private val getIsHuaweiDeviceWithoutGoogleServicesUseCase: GetIsHuaweiDeviceWithoutGoogleServicesUseCase,
    private val hotWalletFeatureToggles: HotWalletFeatureToggles,
    private val userWalletsListRepository: UserWalletsListRepository,
    private val tangemPayFeatureToggles: TangemPayFeatureToggles,
    private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
    private val tangemPayOnboardingRepository: OnboardingRepository,
    private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
    private val accountsFeatureToggles: AccountsFeatureToggles,
    private val cardDetailsRepository: TangemPayCardDetailsRepository,
    private val tangemPayMainInfoManager: TangemPayMainInfoManager,
    val screenLifecycleProvider: ScreenLifecycleProvider,
    val innerWalletRouter: InnerWalletRouter,
) : Model() {

    val askBiometryModelCallbacks = AskBiometryModelCallbacks()
    val askForPushNotificationsModelCallbacks = AskForPushNotificationsCallbacks()
    val uiState: StateFlow<WalletScreenState> = stateHolder.uiState

    private val walletsUpdateJobHolder = JobHolder()
    private val refreshWalletJobHolder = JobHolder()
    private var needToRefreshWallet = false
    private val clearNFTCacheJobHolder = JobHolder()
    private val updateTangemPayJobHolder = JobHolder()

    private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()

    init {
        analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened)

        screenLifecycleProvider.isBackgroundState
            .onEach { isBackground ->
                if (isBackground.not()) {
                    suggestToEnableBiometrics()
                }
            }.launchIn(modelScope)

        suggestToOpenMarkets()

        maybeMigrateNames()
        maybeSetWalletFirstTimeUsage()
        updateYieldSupplyApy()
        subscribeToUserWalletsUpdates()
        subscribeOnBalanceHiding()
        subscribeOnSelectedWalletFlow()
        subscribeToScreenBackgroundState()
        subscribeOnPushNotificationsPermission()
        subscribeTangemPayOnWalletState()
        subscribeOnTangemPayInfoUpdates()
        enableNotificationsIfNeeded()

        clickIntents.initialize(innerWalletRouter, modelScope)
    }

    private fun updateYieldSupplyApy() {
        if (yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled) {
            modelScope.launch(dispatchers.default) {
                yieldSupplyApyUpdateUseCase()
            }
        }
    }

    private fun maybeMigrateNames() {
        modelScope.launch {
            walletNameMigrationUseCase()
        }
    }

    private fun maybeSetWalletFirstTimeUsage() {
        modelScope.launch {
            setWalletFirstTimeUsageUseCase()
        }
    }

    override fun onDestroy() {
        super.onDestroy()

        tokenListStore.clear()
        stateHolder.clear()
        walletScreenContentLoader.cancelAll()
    }

    private suspend fun suggestToEnableBiometrics() {
        if (shouldShowAskBiometryBottomSheet()) {
            delay(timeMillis = 1_800)

            innerWalletRouter.dialogNavigation.activate(
                configuration = WalletDialogConfig.AskForBiometry,
            )
        }
    }

    private fun suggestToOpenMarkets() {
        modelScope.launch {
            withContext(dispatchers.io) { delay(timeMillis = 1_800) }

            if (shouldShowMarketsTooltipUseCase()) {
                stateHolder.update {
                    it.copy(showMarketsOnboarding = true)
                }
            }

            shouldShowMarketsTooltipUseCase(isShown = true)
        }
    }

    private suspend fun shouldShowAskBiometryBottomSheet(): Boolean {
        return if (hotWalletFeatureToggles.isHotWalletEnabled) {
            userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } &&
                innerWalletRouter.isWalletLastScreen() &&
                shouldShowAskBiometryUseCase() &&
                canUseBiometryUseCase()
        } else {
            innerWalletRouter.isWalletLastScreen() && shouldShowAskBiometryUseCase() && canUseBiometryUseCase()
        }
    }

    private fun subscribeToUserWalletsUpdates() = channelFlow<Unit> {
        val firstWalletsUseCaseEmit = MutableStateFlow(false)
        suspend fun waitFirstWalletsUseCaseEmit() = firstWalletsUseCaseEmit.filter { it }.first()

        // deepLinkActionFlow must wait for fist getWalletsUseCase() emit to correct handle Action.InitializeWallets
        walletDeepLinkActionListener.selectWalletFlow
            .onEach { waitFirstWalletsUseCaseEmit() }
            .onEach(::selectWalletById)
            .launchIn(this)

        getWalletsUseCase()
            .conflate()
            .distinctUntilChanged()
            .map {
                walletsUpdateActionResolver.resolve(
                    wallets = it,
                    currentState = stateHolder.value,
                )
            }
            .onEach(::updateWallets)
            .onEach { firstWalletsUseCaseEmit.update { true } }
            .launchIn(this)
        awaitClose()
    }
        .flowOn(dispatchers.default)
        .launchIn(modelScope)
        .saveIn(walletsUpdateJobHolder)

    private fun subscribeOnBalanceHiding() {
        getBalanceHidingSettingsUseCase()
            .conflate()
            .distinctUntilChanged()
            .onEach {
                stateHolder.update(transformer = UpdateBalanceHidingModeTransformer(it.isBalanceHidden))
            }
            .flowOn(dispatchers.main)
            .launchIn(modelScope)
    }

    private fun subscribeOnPushNotificationsPermission() {
        modelScope.launch {
            val shouldAskNotificationPermissionsViaBs = notificationsRepository.shouldAskNotificationPermissionsViaBs()
            val shouldShow = notificationsRepository.shouldShowSubscribeOnNotificationsAfterUpdate()
            val isBiometricsEnabled = shouldSaveUserWalletsSyncUseCase()
            val isHuaweiDevice = getIsHuaweiDeviceWithoutGoogleServicesUseCase()
            Timber.d(
                "push BS afterUpdate: $shouldShow," +
                    "isBiometricsEnabled $isBiometricsEnabled," +
                    "isHuaweiDevice $isHuaweiDevice",
            )
            if (!shouldAskNotificationPermissionsViaBs) {
                notificationsRepository.setShouldAskNotificationPermissionsViaBs(true)
                return@launch
            }
            if (!isBiometricsEnabled) return@launch
            if (!shouldShow) {
                return@launch
            }

            delay(timeMillis = 1_800)

            innerWalletRouter.dialogNavigation.activate(
                configuration = WalletDialogConfig.AskForPushNotifications,
            )
        }
    }

    // It's okay here because we need to be able to observe the selected wallet changes
    @Suppress("DEPRECATION")
    private fun subscribeOnSelectedWalletFlow() {
        getSelectedWalletUseCase().onRight {
            it
                .conflate()
                .distinctUntilChanged()
                .onEach { selectedWallet ->
                    if (selectedWallet.isMultiCurrency) {
                        selectedWalletAnalyticsSender.send(selectedWallet)
                    }

                    subscribeOnExpressTransactionsUpdates(selectedWallet)
                    observeAndClearNFTCacheIfNeedUseCase(selectedWallet)
                }
                .flowOn(dispatchers.main)
                .launchIn(modelScope)
        }
    }

    private fun selectWalletById(selectedWalletId: UserWalletId) {
        val currentWalletId = stateHolder.getSelectedWalletId()

        if (currentWalletId == selectedWalletId) return

        val currentIndex = stateHolder.getWalletIndexByWalletId(userWalletId = currentWalletId) ?: return
        val newIndex = stateHolder.getWalletIndexByWalletId(userWalletId = selectedWalletId) ?: return

        scrollToWallet(prevIndex = currentIndex, newIndex = newIndex) {
            stateHolder.update { it.copy(selectedWalletIndex = newIndex) }
        }
    }

    // We need to update the current wallet quotes if the application was in the background for more than 10 seconds
    // and then returned to the foreground
    private fun subscribeToScreenBackgroundState() {
        screenLifecycleProvider.isBackgroundState
            .onEach { isBackground ->
                refreshWalletJobHolder.cancel()
                when {
                    isBackground -> needToRefreshTimer()
                    needToRefreshWallet && !isBackground -> {
                        triggerRefreshWalletQuotes()
                    }
                }
            }
            .launchIn(modelScope)
    }

    private fun subscribeOnExpressTransactionsUpdates(userWallet: UserWallet) {
        if (!userWallet.isMultiCurrency) {
            expressTxStatusTaskScheduler.cancelTask()
            expressTxStatusTaskScheduler.scheduleTask(
                modelScope,
                PeriodicTask(
                    isDelayFirst = false,
                    delay = EXPRESS_STATUS_UPDATE_DELAY,
                    task = {
                        runCatching {
                            onrampStatusFactory.updateOnrmapTransactionStatuses(userWallet)
                        }
                    },
                    onSuccess = { /* no-op */ },
                    onError = { /* no-op */ },
                ),
            )
        }
    }

    private fun observeAndClearNFTCacheIfNeedUseCase(selectedWallet: UserWallet) {
        observeAndClearNFTCacheIfNeedUseCase
            .invoke(selectedWallet.walletId)
            .launchIn(modelScope)
            .saveIn(clearNFTCacheJobHolder)
    }

    private fun subscribeTangemPayOnWalletState() {
        /**
         * Update state each time a user opens/returns to wallet screen
         * and every minute while user stays on the main screen
         */
        if (!tangemPayFeatureToggles.isTangemPayEnabled) return

        combine(
            flow = screenLifecycleProvider.isBackgroundState,
            flow2 = uiState.mapNotNull {
                it.wallets.getOrNull(it.selectedWalletIndex)?.walletCardState?.id
            }.distinctUntilChanged(),
            transform = ::Pair,
        ).onEach { (inBackground, userWalletId) ->
            if (inBackground) return@onEach

            val savedCustomerInfo =
                tangemPayOnboardingRepository.getSavedCustomerInfo(userWalletId)

            val isShouldLaunchPeriodicUpdate = savedCustomerInfo?.cardInfo == null &&
                tangemPayOnboardingRepository.isTangemPayInitialDataProduced(userWalletId)

            if (isShouldLaunchPeriodicUpdate) {
                updateTangemPayJobHolder.cancel()
                modelScope.launch {
                    tangemPayMainInfoManager.refreshTangemPayInfo(userWalletId)
                    while (isActive) {
                        delay(TANGEM_PAY_UPDATE_INTERVAL)
                        tangemPayMainInfoManager.refreshTangemPayInfo(userWalletId)
                    }
                }.saveIn(updateTangemPayJobHolder)
            } else {
                // Don't refresh customer info periodically if the card was already issued, only update on swipe to refresh
                tangemPayMainInfoManager.refreshTangemPayInfo(userWalletId)
            }
        }.launchIn(modelScope)
    }

    private fun subscribeOnTangemPayInfoUpdates() {
        if (!tangemPayFeatureToggles.isTangemPayEnabled) return
        tangemPayMainInfoManager.mainScreenCustomerInfo
            .filterNotNull()
            .distinctUntilChanged()
            .onEach { (userWalletId, mainInfoData) ->
                mainInfoData.onLeft { tangemPayError ->
                    when (tangemPayError) {
                        TangemPayCustomerInfoError.RefreshNeededError -> {
                            stateHolder.update(
                                transformer = TangemPayRefreshNeededStateTransformer(
                                    userWalletId = userWalletId,
                                    onRefreshClick = { clickIntents.onRefreshPayToken(userWalletId) },
                                ),
                            )
                        }
                        TangemPayCustomerInfoError.UnavailableError -> {
                            stateHolder.update(
                                transformer = TangemPayUnavailableStateTransformer(userWalletId),
                            )
                        }
                        else -> {
                            // hide TangemPay block
                            Timber.e("Failed when loading main screen TangemPay info: $tangemPayError")
                            stateHolder.update(
                                transformer = TangemPayHiddenStateTransformer(userWalletId),
                            )
                        }
                    }
                }.onRight { data -> updateTangemPay(data, userWalletId) }
            }
            .launchIn(modelScope)
    }

    private suspend fun updateTangemPay(data: MainScreenCustomerInfo, userWalletId: UserWalletId) {
        val cardFrozenState =
            data.info.productInstance?.cardId?.let { cardDetailsRepository.cardFrozenStateSync(it) }
                ?: TangemPayCardFrozenState.Unfrozen
        stateHolder.update(
            transformer = TangemPayUpdateInfoStateTransformer(
                userWalletId = userWalletId,
                value = data,
                cardFrozenState = cardFrozenState,
                onClickKyc = innerWalletRouter::openTangemPayOnboarding,
                onIssuingCard = clickIntents::onIssuingCardClicked,
                onIssuingFailed = clickIntents::onIssuingFailedClicked,
                openDetails = { config ->
                    innerWalletRouter.openTangemPayDetails(
                        userWalletId = userWalletId,
                        config = config,
                    )
                },
            ),
        )
    }

    private fun needToRefreshTimer() {
        modelScope.launch {
            delay(REFRESH_WALLET_BACKGROUND_TIMER_MILLIS)
            needToRefreshWallet = true
        }.saveIn(refreshWalletJobHolder)
    }

    private fun triggerRefreshWalletQuotes() {
        needToRefreshWallet = false
        val state = stateHolder.uiState.value
        val wallet = state.wallets.getOrNull(state.selectedWalletIndex) ?: return
        modelScope.launch {
            awaitAll(
                async {
                    refreshMultiCurrencyWalletQuotesUseCase(wallet.walletCardState.id).getOrElse {
                        Timber.e("Failed to refreshMultiCurrencyWalletQuotesUseCase $it")
                    }
                },
                async {
                    getWalletsUseCase.invokeSync()
                        .firstOrNull { it.walletId == wallet.walletCardState.id }
                        ?.let(::subscribeOnExpressTransactionsUpdates)
                },
            )
        }.saveIn(refreshWalletJobHolder)
    }

    private suspend fun updateWallets(action: WalletsUpdateActionResolver.Action) {
        when (action) {
            is WalletsUpdateActionResolver.Action.InitializeWallets -> initializeWallets(action)
            is WalletsUpdateActionResolver.Action.ReinitializeWallet -> reinitializeWallet(action)
            is WalletsUpdateActionResolver.Action.AddWallet -> addWallet(action)
            is WalletsUpdateActionResolver.Action.DeleteWallet -> deleteWallet(action)
            is WalletsUpdateActionResolver.Action.UnlockWallet -> unlockWallet(action)
            is WalletsUpdateActionResolver.Action.UpdateWalletCardCount -> {
                // refresh loader to use actual user wallet
                walletScreenContentLoader.load(
                    userWallet = action.selectedWallet,
                    clickIntents = clickIntents,
                    isRefresh = true,
                    coroutineScope = modelScope,
                )

                stateHolder.update(
                    transformer = UpdateWalletCardsCountTransformer(
                        userWallet = action.selectedWallet,
                        walletImageResolver = walletImageResolver,
                    ),
                )
            }
            is WalletsUpdateActionResolver.Action.RenameWallets -> {
                stateHolder.update(transformer = RenameWalletsTransformer(renamedWallets = action.renamedWallets))
            }
            is WalletsUpdateActionResolver.Action.ReloadWallets -> {
                reloadWarnings(action)
            }
            WalletsUpdateActionResolver.Action.EmptyWallets -> {
                Timber.w("Wallets list is empty!")
            }
            is WalletsUpdateActionResolver.Action.Unknown -> {
                Timber.w("Unable to perform action: $action")
            }
        }
    }

    private fun reloadWarnings(action: WalletsUpdateActionResolver.Action.ReloadWallets) {
        action.wallets.forEach {
            walletScreenContentLoader.load(
                userWallet = it,
                clickIntents = clickIntents,
                coroutineScope = modelScope,
                isRefresh = true,
            )
        }
    }

    private suspend fun initializeWallets(action: WalletsUpdateActionResolver.Action.InitializeWallets) {
        stateHolder.update(
            transformer = InitializeWalletsTransformer(
                selectedWalletIndex = action.selectedWalletIndex,
                wallets = action.wallets,
                clickIntents = clickIntents,
                walletImageResolver = walletImageResolver,
            ),
        )

        walletScreenContentLoader.load(
            userWallet = action.selectedWallet,
            clickIntents = clickIntents,
            coroutineScope = modelScope,
        )

        fetchWalletContent(userWallet = action.selectedWallet)

        val otherWallets = action.wallets.minus(action.selectedWallet)

        otherWallets
            .filterNot(UserWallet::isLocked)
            .onEach { userWallet ->
                modelScope.launch { walletContentFetcher(userWalletId = userWallet.walletId) }
            }

        if (action.wallets.size > 1 && isWalletsScrollPreviewEnabled()) {
            val direction = if (action.selectedWalletIndex == action.wallets.lastIndex) {
                Direction.RIGHT
            } else {
                Direction.LEFT
            }

            demonstrateWalletsScrollPreview(direction = direction)
        }
    }

    private suspend fun reinitializeWallet(action: WalletsUpdateActionResolver.Action.ReinitializeWallet) {
        walletScreenContentLoader.cancel(action.prevWalletId)
        tokenListStore.remove(action.prevWalletId)

        walletScreenContentLoader.load(
            userWallet = action.selectedWallet,
            clickIntents = clickIntents,
            coroutineScope = modelScope,
        )

        fetchWalletContent(userWallet = action.selectedWallet)

        stateHolder.update(
            ReinitializeWalletTransformer(
                prevWalletId = action.prevWalletId,
                newUserWallet = action.selectedWallet,
                clickIntents = clickIntents,
                walletImageResolver = walletImageResolver,
            ),
        )
    }

    private suspend fun addWallet(action: WalletsUpdateActionResolver.Action.AddWallet) {
        if (accountsFeatureToggles.isFeatureEnabled) {
            fetchWalletContent(userWallet = action.selectedWallet)

            stateHolder.update(
                AddWalletTransformer(
                    userWallet = action.selectedWallet,
                    clickIntents = clickIntents,
                    walletImageResolver = walletImageResolver,
                ),
            )

            walletScreenContentLoader.load(
                userWallet = action.selectedWallet,
                clickIntents = clickIntents,
                coroutineScope = modelScope,
            )
        } else {
            walletScreenContentLoader.load(
                userWallet = action.selectedWallet,
                clickIntents = clickIntents,
                coroutineScope = modelScope,
            )

            fetchWalletContent(userWallet = action.selectedWallet)

            stateHolder.update(
                AddWalletTransformer(
                    userWallet = action.selectedWallet,
                    clickIntents = clickIntents,
                    walletImageResolver = walletImageResolver,
                ),
            )
        }

        scrollToWallet(prevIndex = action.prevWalletIndex, newIndex = action.selectedWalletIndex) {
            stateHolder.update {
                it.copy(selectedWalletIndex = action.selectedWalletIndex)
            }
        }

        enableNotificationsIfNeeded()
    }

    private fun deleteWallet(action: WalletsUpdateActionResolver.Action.DeleteWallet) {
        walletScreenContentLoader.cancel(action.deletedWalletId)
        tokenListStore.remove(action.deletedWalletId)

        walletScreenContentLoader.load(
            userWallet = action.selectedWallet,
            clickIntents = clickIntents,
            coroutineScope = modelScope,
        )

        val newSelectedWalletIndex = if (action.selectedWalletIndex - action.deletedWalletIndex == 1) {
            action.deletedWalletIndex
        } else {
            action.selectedWalletIndex
        }

        scrollToWallet(
            prevIndex = action.deletedWalletIndex,
            newIndex = action.selectedWalletIndex,
            onConsume = {
                updateStateByDeleteWalletTransformer(
                    selectedWalletIndex = newSelectedWalletIndex,
                    deletedWalletId = action.deletedWalletId,
                )
            },
        )
    }

    private fun updateStateByDeleteWalletTransformer(selectedWalletIndex: Int, deletedWalletId: UserWalletId) {
        stateHolder.update(
            DeleteWalletTransformer(
                selectedWalletIndex = selectedWalletIndex,
                deletedWalletId = deletedWalletId,
            ),
        )
    }

    private suspend fun unlockWallet(action: WalletsUpdateActionResolver.Action.UnlockWallet) {
        withContext(dispatchers.io) { delay(timeMillis = 700) }

        stateHolder.update(
            transformer = UnlockWalletTransformer(
                unlockedWallets = action.unlockedWallets,
                clickIntents = clickIntents,
                walletImageResolver = walletImageResolver,
            ),
        )

        walletScreenContentLoader.load(
            userWallet = action.selectedWallet,
            clickIntents = clickIntents,
            coroutineScope = modelScope,
        )

        action.unlockedWallets.onEach { userWallet ->
            modelScope.launch { fetchWalletContent(userWallet = userWallet) }
        }
    }

    private fun demonstrateWalletsScrollPreview(direction: Direction) {
        modelScope.launch(dispatchers.mainImmediate) {
            delay(timeMillis = 1_800)

            walletEventSender.send(
                event = WalletEvent.DemonstrateWalletsScrollPreview(direction = direction),
            )
        }
    }

    private fun scrollToWallet(prevIndex: Int, newIndex: Int, onConsume: () -> Unit = {}) {
        // Should not show scroll animation if WalletScreen isn't in the background.
        if (screenLifecycleProvider.isBackgroundState.value) {
            onConsume()
            stateHolder.update(
                ScrollToWalletTransformer(
                    prevIndex = prevIndex,
                    newIndex = newIndex,
                    currentStateProvider = Provider(action = stateHolder::value),
                    withScrollAnimation = false,
                    stateUpdater = { newState -> stateHolder.update { newState } },
                    onConsume = {},
                ),
            )
        } else {
            stateHolder.update(
                ScrollToWalletTransformer(
                    prevIndex = prevIndex,
                    newIndex = newIndex,
                    currentStateProvider = Provider(action = stateHolder::value),
                    stateUpdater = { newState -> stateHolder.update { newState } },
                    onConsume = onConsume,
                ),
            )
        }
    }

    private suspend fun fetchWalletContent(userWallet: UserWallet) {
        if (userWallet.isLocked) return

        /*
         * Updating the balance of the current wallet is an essential part of InitializationWallets,
         * so the coroutine is launched in the current context
         */
        supervisorScope {
            launch { walletContentFetcher(userWalletId = userWallet.walletId) }
        }
    }

    private fun enableNotificationsIfNeeded() {
        modelScope.launch {
            val isUserAllowToEnableNotifications = notificationsRepository.isUserAllowToSubscribeOnPushNotifications()
            if (isUserAllowToEnableNotifications) {
                val alreadyEnabledWallets = notificationsRepository.getWalletAutomaticallyEnabledList().map {
                    UserWalletId(it)
                }
                val walletsListWhichShouldBeEnabled = getWalletsListForEnablingUseCase(alreadyEnabledWallets)
                walletsListWhichShouldBeEnabled.forEach { userWalletId ->
                    setNotificationsEnabledUseCase(userWalletId, true).onRight {
                        notificationsRepository.setNotificationsWasEnabledAutomatically(userWalletId.stringValue)
                    }.onLeft {
                        Timber.e(it)
                    }
                }
            }
        }
    }

    inner class AskBiometryModelCallbacks : AskBiometryComponent.ModelCallbacks {
        override fun onAllowed() {
            analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.On))
            innerWalletRouter.dialogNavigation.dismiss()
        }

        override fun onDenied() {
            analyticsEventsHandler.send(MainScreenAnalyticsEvent.EnableBiometrics(AnalyticsParam.OnOffState.Off))
            innerWalletRouter.dialogNavigation.dismiss()
        }
    }

    inner class AskForPushNotificationsCallbacks : PushNotificationsModelCallbacks {

        override fun onAllowSystemPermission() {
            innerWalletRouter.dialogNavigation.dismiss()
            enableNotificationsIfNeeded()
        }

        override fun onDenySystemPermission() {
            innerWalletRouter.dialogNavigation.dismiss()
            enableNotificationsIfNeeded()
        }

        override fun onDismiss() {
            innerWalletRouter.dialogNavigation.dismiss()
        }
    }

    private companion object {
        const val REFRESH_WALLET_BACKGROUND_TIMER_MILLIS = 10000L
        const val EXPRESS_STATUS_UPDATE_DELAY = 10000L
    }
}