package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory

import arrow.core.Either
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.themedColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingEntryInfo
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.domain.yield.supply.models.YieldSupplyRewardBalance
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.Provider
import kotlinx.collections.immutable.toImmutableList

@Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
internal class TokenDetailsStateFactory(
    private val currentStateProvider: Provider<TokenDetailsState>,
    private val appCurrencyProvider: Provider<AppCurrency>,
    private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
    private val tokenDetailsClickIntents: TokenDetailsClickIntents,
    private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
    private val getUserWalletUseCase: GetUserWalletUseCase,
    private val userWalletId: UserWalletId,
) {

    private val skeletonStateConverter by lazy {
        TokenDetailsSkeletonStateConverter(
            clickIntents = tokenDetailsClickIntents,
            networkHasDerivationUseCase = networkHasDerivationUseCase,
            getUserWalletUseCase = getUserWalletUseCase,
            userWalletId = userWalletId,
        )
    }

    private val notificationConverter by lazy {
        TokenDetailsNotificationConverter(
            userWalletId = userWalletId,
            getUserWalletUseCase = getUserWalletUseCase,
            clickIntents = tokenDetailsClickIntents,
        )
    }

    private val tokenDetailsLoadedBalanceConverter by lazy {
        TokenDetailsLoadedBalanceConverter(
            currentStateProvider = currentStateProvider,
            appCurrencyProvider = appCurrencyProvider,
            clickIntents = tokenDetailsClickIntents,
        )
    }

    private val tokenDetailsButtonsConverter by lazy {
        TokenDetailsActionButtonsConverter(
            currentStateProvider = currentStateProvider,
            clickIntents = tokenDetailsClickIntents,
        )
    }

    private val refreshStateConverter by lazy {
        TokenDetailsRefreshStateConverter(
            currentStateProvider = currentStateProvider,
        )
    }

    private val balanceSelectStateConverter by lazy {
        TokenDetailsBalanceSelectStateConverter(
            currentStateProvider = currentStateProvider,
            appCurrencyProvider = appCurrencyProvider,
            cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
        )
    }

    fun getInitialState(screenArgument: CryptoCurrency): TokenDetailsState {
        return skeletonStateConverter.convert(value = screenArgument)
    }

    fun getCurrencyLoadedBalanceState(
        cryptoCurrencyEither: Either<CurrencyStatusError, CryptoCurrencyStatus>,
    ): TokenDetailsState {
        return tokenDetailsLoadedBalanceConverter.convert(cryptoCurrencyEither)
    }

    fun getStakingInfoState(
        state: TokenDetailsState,
        stakingEntryInfo: StakingEntryInfo?,
        stakingAvailability: StakingAvailability,
        cryptoCurrencyStatus: CryptoCurrencyStatus,
    ): TokenDetailsState {
        return TokenDetailsStakingInfoConverter(
            currentState = state,
            cryptoCurrencyStatus = cryptoCurrencyStatus,
            clickIntents = tokenDetailsClickIntents,
            appCurrencyProvider = appCurrencyProvider,
            stakingEntryInfo = stakingEntryInfo,
        ).convert(stakingAvailability)
    }

    fun getManageButtonsState(actions: List<TokenActionsState.ActionState>): TokenDetailsState {
        return tokenDetailsButtonsConverter.convert(actions)
    }

    fun getRefreshingState(): TokenDetailsState {
        return refreshStateConverter.convert(true)
    }

    fun getRefreshedState(): TokenDetailsState {
        return refreshStateConverter.convert(false)
    }

    fun getStateWithUpdatedHidden(isBalanceHidden: Boolean): TokenDetailsState {
        val currentState = currentStateProvider()

        return currentState.copy(isBalanceHidden = isBalanceHidden)
    }

    fun getStateWithNotifications(warnings: Set<CryptoCurrencyWarning>): TokenDetailsState {
        val state = currentStateProvider()
        return state.copy(notifications = notificationConverter.convert(warnings))
    }

    fun getStateWithRemovedRentNotification(): TokenDetailsState {
        val state = currentStateProvider()
        return state.copy(notifications = notificationConverter.removeRentInfo(state))
    }

    fun getStateWithRemovedHederaAssociateNotification(): TokenDetailsState {
        val state = currentStateProvider()
        return state.copy(notifications = notificationConverter.removeHederaAssociateWarning(state))
    }

    fun getStateWithRemovedRequiredTrustlineNotification(): TokenDetailsState {
        val state = currentStateProvider()
        return state.copy(notifications = notificationConverter.removeRequiredTrustlineWarning(state))
    }

    fun getStateWithRemovedKaspaIncompleteTransactionNotification(): TokenDetailsState {
        val state = currentStateProvider()
        return state.copy(
            notifications = notificationConverter.removeKaspaIncompleteTransactionWarning(state),
        )
    }

    fun getStateWithUpdatedMenu(
        userWallet: UserWallet,
        hasDerivations: Boolean,
        isSupported: Boolean,
        isDynamicAddressesAvailable: Boolean = false,
    ): TokenDetailsState {
        return with(currentStateProvider()) {
            copy(
                topAppBarConfig = topAppBarConfig.copy(
                    tokenDetailsAppBarMenuConfig = topAppBarConfig.tokenDetailsAppBarMenuConfig
                        ?.updateMenu(
                            userWallet = userWallet,
                            hasDerivations = hasDerivations,
                            isSupported = isSupported,
                            isDynamicAddressesAvailable = isDynamicAddressesAvailable,
                        ),
                ),
            )
        }
    }

    fun getStateWithUpdatedBalanceSegmentedButtonConfig(
        buttonConfig: TokenBalanceSegmentedButtonConfig,
    ): TokenDetailsState {
        return balanceSelectStateConverter.convert(buttonConfig)
    }

    fun getStateWithUpdatedYieldSupplyDisplayBalance(
        yieldSupplyRewardBalance: YieldSupplyRewardBalance,
    ): TokenDetailsState {
        val state = currentStateProvider()
        val balanceState = state.tokenBalanceBlockState
        return state.copy(
            tokenBalanceBlockState = when (balanceState) {
                is TokenDetailsBalanceBlockState.Content ->
                    balanceState.copy(
                        displayYieldSupplyFiatBalance = yieldSupplyRewardBalance.fiatBalance,
                        displayYieldSupplyCryptoBalance = yieldSupplyRewardBalance.cryptoBalance,
                    )
                is TokenDetailsBalanceBlockState.Error -> balanceState
                is TokenDetailsBalanceBlockState.Loading -> balanceState
            },
        )
    }

    private fun TokenDetailsAppBarMenuConfig.updateMenu(
        userWallet: UserWallet,
        hasDerivations: Boolean,
        isSupported: Boolean,
        isDynamicAddressesAvailable: Boolean,
    ): TokenDetailsAppBarMenuConfig? {
        if (userWallet is UserWallet.Cold &&
            userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
        ) {
            return null
        }

        return copy(
            items = buildList {
                if (isDynamicAddressesAvailable) {
                    TangemDropdownMenuItem(
                        title = resourceReference(R.string.dynamic_addresses),
                        textColor = themedColor { TangemTheme.colors.text.primary1 },
                        onClick = tokenDetailsClickIntents::onDynamicAddressesClick,
                    ).let(::add)
                }
                if (isSupported && hasDerivations) {
                    TangemDropdownMenuItem(
                        title = resourceReference(R.string.token_details_generate_xpub),
                        textColor = themedColor { TangemTheme.colors.text.primary1 },
                        onClick = tokenDetailsClickIntents::onGenerateExtendedKey,
                    ).let(::add)
                }
                TangemDropdownMenuItem(
                    title = TextReference.Res(id = R.string.token_details_hide_token),
                    textColor = themedColor { TangemTheme.colors.text.warning },
                    onClick = tokenDetailsClickIntents::onHideClick,
                ).let(::add)
            }.toImmutableList(),
        )
    }
}