package com.tangem.features.hotwallet.createhardwarewallet

import com.tangem.common.core.TangemError
import com.tangem.common.core.TangemSdkError
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.dialog.Dialogs
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.analytics.IntroductionProcess
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.error.SaveWalletError
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.features.hotwallet.CreateHardwareWalletComponent
import com.tangem.features.hotwallet.createhardwarewallet.entity.CreateHardwareWalletUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.logging.TangemLogger
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

private const val HIDE_PROGRESS_DELAY = 400L

@Suppress("LongParameterList")
@ModelScoped
internal class CreateHardwareWalletModel @Inject constructor(
    paramsContainer: ParamsContainer,
    override val dispatchers: CoroutineDispatcherProvider,
    private val router: Router,
    private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
    private val urlOpener: UrlOpener,
    private val settingsRepository: SettingsRepository,
    private val cardSdkConfigRepository: CardSdkConfigRepository,
    private val uiMessageSender: UiMessageSender,
    private val scanCardProcessor: ScanCardProcessor,
    private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
    private val saveWalletUseCase: SaveWalletUseCase,
    private val userWalletsListRepository: UserWalletsListRepository,
    private val trackingContextProxy: TrackingContextProxy,
    private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {

    private val params = paramsContainer.require<CreateHardwareWalletComponent.Params>()

    val uiState: StateFlow<CreateHardwareWalletUM>
        field = MutableStateFlow(
            CreateHardwareWalletUM(
                onBackClick = { router.pop() },
                onBuyTangemWalletClick = ::onBuyTangemWalletClick,
                onScanDeviceClick = ::onScanDeviceClick,
            ),
        )

    init {
        trackingContextProxy.addHotWalletContext()
        analyticsEventHandler.send(WalletSettingsAnalyticEvents.CreateWalletScreenOpened())
    }

    override fun onDestroy() {
        trackingContextProxy.removeContext()
        super.onDestroy()
    }

    private fun onBuyTangemWalletClick() {
        analyticsEventHandler.send(Basic.ButtonBuy(source = params.source))
        modelScope.launch {
            generateBuyTangemCardLinkUseCase
                .invoke(GenerateBuyTangemCardLinkUseCase.Source.Upgrade).let { urlOpener.openUrl(it) }
        }
    }

    private fun onScanDeviceClick() {
        analyticsEventHandler.send(
            event = IntroductionProcess.ButtonScanCard(params.source),
        )
        scanCard()
    }

    private fun scanCard() {
        modelScope.launch {
            setLoading(true)

            val shouldSaveAccessCodes = settingsRepository.shouldSaveAccessCodes()
            cardSdkConfigRepository.setAccessCodeRequestPolicy(
                isBiometricsRequestPolicy = shouldSaveAccessCodes,
            )

            scanCardProcessor.scan(
                analyticsSource = params.source,
                shouldCheckIsAlreadyActivated = true,
                onProgressStateChange = { showProgress ->
                    if (!showProgress) {
                        delay(HIDE_PROGRESS_DELAY)
                        setLoading(false)
                    } else {
                        setLoading(true)
                    }
                },
                onFailure = { error ->
                    handleScanError(error)
                    delay(HIDE_PROGRESS_DELAY)
                    setLoading(false)
                },
                onSuccess = { scanResponse ->
                    proceedWithScanResponse(scanResponse)
                },
            )
        }
    }

    private suspend fun proceedWithScanResponse(scanResponse: ScanResponse) {
        val userWallet = coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build()

        if (userWallet == null) {
            TangemLogger.e("User wallet not created")
            setLoading(false)
            return
        }

        saveWalletUseCase(
            userWallet = userWallet,
            analyticsSource = params.source,
        ).fold(
            ifLeft = { saveWalletError ->
                delay(HIDE_PROGRESS_DELAY)
                setLoading(false)
                when (saveWalletError) {
                    is SaveWalletError.DataError -> TangemLogger.e("Unable to save user wallet: $saveWalletError")
                    is SaveWalletError.WalletAlreadySaved -> handleAlreadySavedCard(
                        saveWalletError.messageId,
                        walletId = userWallet.walletId,
                        scanResponse = scanResponse,
                    )
                }
            },
            ifRight = {
                setLoading(false)
                router.replaceAll(AppRoute.Wallet)
            },
        )
    }

    private fun setLoading(isLoading: Boolean) {
        uiState.update { it.copy(isScanInProgress = isLoading) }
    }

    private fun handleScanError(error: TangemError) {
        when (error) {
            is TangemSdkError.NfcFeatureIsUnavailable -> handleNfcFeatureUnavailable()
            is TangemSdkError -> TangemLogger.e("Scan error occurred", error)
            else -> TangemLogger.e("Error happened", error)
        }
    }

    private fun handleNfcFeatureUnavailable() {
        uiMessageSender.send(Dialogs.nfcFeatureUnavailable())
    }

    private suspend fun handleAlreadySavedCard(messageId: Int, walletId: UserWalletId, scanResponse: ScanResponse) {
        uiMessageSender.send(
            message = DialogMessage(
                message = resourceReference(messageId),
            ),
        )
        userWalletsListRepository.unlock(
            userWalletId = walletId,
            unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(
                scanResponse = scanResponse,
                source = params.source,
            ),
        ).onRight {
            router.replaceAll(AppRoute.Wallet)
        }
    }
}