Android Kotlin. Мне необходимо в buildList, если MaxiPaySubscriptionStatus != CREATED, то в начало списка additionalInfoList добавляется элемент MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FAQ, КАК ЭТО СДЕЛАТЬ? package ru.maxi.mobile.feature.maxiPay.ui.subscriptionsList

import android.app.Application
import android.net.Uri
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ru.maxi.mobile.R
import ru.maxi.mobile.core.data.exception.BusinessLogicException
import ru.maxi.mobile.core.data.exception.BusinessLogicValidException
import ru.maxi.mobile.feature.analytics.model.AnalyticType
import ru.maxi.mobile.feature.analytics.model.actions.AnalyticMaxiPayActions
import ru.maxi.mobile.feature.analytics.model.events.AnalyticsGeneralEvent
import ru.maxi.mobile.feature.analytics.usecase.AnalyticsUseCase
import ru.maxi.mobile.feature.appDialogs.usecase.NotificationsPermissionUseCase
import ru.maxi.mobile.feature.maxiPay.domain.CreateMaxiPaySubscriptionUseCase
import ru.maxi.mobile.feature.maxiPay.domain.GetMaxiPayAdditionalInfoListUseCase
import ru.maxi.mobile.feature.maxiPay.domain.GetMaxiPayAvailableMerchantsListUseCase
import ru.maxi.mobile.feature.maxiPay.domain.GetMaxiPayClientSettingsUseCase
import ru.maxi.mobile.feature.maxiPay.domain.GetMaxiPayMerchantsListDataUseCase
import ru.maxi.mobile.feature.maxiPay.domain.MaxiPaySseUseCase
import ru.maxi.mobile.feature.maxiPay.domain.SendMaxiPaySavePaymentSettingsRequestUseCase
import ru.maxi.mobile.feature.maxiPay.domain.UpdateMaxiPayClientSettingsUseCase
import ru.maxi.mobile.feature.maxiPay.domain.UpdateMaxiPayPrimarySubscriptionUseCase
import ru.maxi.mobile.feature.maxiPay.domain.UpdateMaxiPaySubscriptionBankDataUseCase
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayAdditionalInfo
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayClientSettings
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayMerchantWithSubscriptions
import ru.maxi.mobile.feature.maxiPay.model.MaxiPayMerchantsListData
import ru.maxi.mobile.feature.maxiPay.model.MaxiPaySseEvent
import ru.maxi.mobile.feature.maxiPay.model.MaxiPaySubscription
import ru.maxi.mobile.feature.maxiPay.ui.subscriptionsList.model.MaxiPaySubscriptionListNavigationEvent
import ru.maxi.mobile.feature.maxiPay.ui.subscriptionsList.model.MaxiPaySubscriptionsListViewItem
import ru.maxi.mobile.feature.sbp.model.BankAppInfo
import ru.maxi.mobile.feature.sbp.model.SbpBankChooserResult
import java.util.UUID
import javax.inject.Inject

/**
 * [ViewModel] экрана экрана списка счетов Макси Pay
 */
@HiltViewModel
internal class MaxiPaySubscriptionsListViewModel @Inject constructor(
    application: Application,
    getMerchantsListData: GetMaxiPayMerchantsListDataUseCase,
    getMerchantsList: GetMaxiPayAvailableMerchantsListUseCase,
    getClientSettings: GetMaxiPayClientSettingsUseCase,
    getAdditionalInfoList: GetMaxiPayAdditionalInfoListUseCase,
    private val createSubscription: CreateMaxiPaySubscriptionUseCase,
    private val updatePrimarySubscription: UpdateMaxiPayPrimarySubscriptionUseCase,
    private val updateSubscriptionBankData: UpdateMaxiPaySubscriptionBankDataUseCase,
    private val sendSavePaymentSettingsRequest: SendMaxiPaySavePaymentSettingsRequestUseCase,
    private val updateMaxiPayClientSettings: UpdateMaxiPayClientSettingsUseCase,
    private val notificationsPermissionUseCase: NotificationsPermissionUseCase,
    private val sseUseCase: MaxiPaySseUseCase,
    private val analyticsUseCase: AnalyticsUseCase,
) : AndroidViewModel(application), MaxiPayPaymentConfirmationActions {

    private enum class NotificationPermissionRequestSource {
        FROM_TOGGLE,
        FROM_RESUME,
    }

    private var hasActiveSseConnection = false

    private var notificationPermissionRequestState: NotificationPermissionRequestSource? = null

    private val _blockingLoaderState = MutableStateFlow(false)

    private val _navigationEvent = Channel<MaxiPaySubscriptionListNavigationEvent>(Channel.BUFFERED)

    private val _messageEvent = Channel<String>(Channel.BUFFERED)

    private val paymentConfirmationSaveButtonState =
        MutableStateFlow(MaxiPaySubscriptionsListViewItem.PaymentConfirmationBlock.SaveButtonState.IDLE)

    private val merchantsGroupsState = MutableStateFlow<List<MerchantsGroup>>(emptyList())

    private val clientSettingsState: StateFlow<MaxiPayClientSettings?> = getClientSettings()
        .stateIn(viewModelScope, SharingStarted.Lazily, null)

    /** События навигации */
    val navigationEvent = _navigationEvent.receiveAsFlow()

    /** События сообщений */
    val messageEvent = _messageEvent.receiveAsFlow()

    /** События SSE-соединения */
    val sseEvents: Flow<MaxiPaySseEvent> = sseUseCase.sseEvents

    /** Состояние отображения блокирующего лоадера */
    val blockingLoaderState: Flow<Boolean> = _blockingLoaderState
        .stateIn(viewModelScope, SharingStarted.Lazily, _blockingLoaderState.value)

    /** Состояние экрана */
    val viewState: Flow<List<MaxiPaySubscriptionsListViewItem>> = combine(
        combine(getMerchantsListData(), getMerchantsList(), ::prepareMerchantsGroups),
        clientSettingsState.filterNotNull(),
        paymentConfirmationSaveButtonState,
        getAdditionalInfoList(),
    ) { merchantsGroups, clientSettings, paymentConfirmationSaveButtonState, additionalInfoList ->
        val subscriptions = merchantsGroups.flatMap { group ->
            group.merchants.flatMap(MaxiPayMerchantWithSubscriptions::subscriptions)
        }
        if (subscriptions.isEmpty()) {
            _navigationEvent.send(MaxiPaySubscriptionListNavigationEvent.OpenAboutScreen)
        }

        showAddOtherSubscriptionDialogIfNeeded(
            oldMerchants = merchantsGroupsState.value.flatMap(MerchantsGroup::merchants),
            newMerchants = merchantsGroups.flatMap(MerchantsGroup::merchants),
        )

        merchantsGroupsState.update { merchantsGroups }

        if (subscriptions.all { it.status == MaxiPaySubscription.Status.CONFIRMED }) {
            disconnectFromSseConnectionIfAllowed()
        } else {
            connectToSseConnectionIfAllowed()
        }

        val atLeastOneSubscriptionWithCreatedStatus = subscriptions
            .any { it.status == MaxiPaySubscription.Status.CREATED }
        return@combine buildList {

            // add(MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FAQ)
            add(
                MaxiPaySubscriptionsListViewItem.Title(
                    getApplication<Application>().getString(R.string.maxi_pay_payment_confirmation_block_title),
                ),
            )
            add(
                MaxiPaySubscriptionsListViewItem.PaymentConfirmationBlock(
                    clientSettings,
                    paymentConfirmationSaveButtonState,
                ),
            )
            addAll(
                merchantsGroups.flatMap {
                    it.toViewItems(forceCreateButtonHidden = atLeastOneSubscriptionWithCreatedStatus)
                },
            )
            addAll(
                additionalInfoList.map {
                    if (subscriptions.any {it.status == MaxiPaySubscription.Status.CREATED}) {
                        remove(MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FAQ)
                        MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FaqAndAboutService
                    } else {
                        MaxiPaySubscriptionsListViewItem.AdditionalInfoType.AboutService
                    }
                    // when (it) {
                    //     is MaxiPayAdditionalInfo.Faq -> MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FAQ
                    //     is MaxiPayAdditionalInfo.AboutService -> MaxiPaySubscriptionsListViewItem.AdditionalInfoType.AboutService
                    //     is MaxiPayAdditionalInfo.FaqAndAboutService -> MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FaqAndAboutService
                    // }
                },
            )

            // if (subscriptions.any {it.status == MaxiPaySubscription.Status.CONFIRMED}) {
            //     remove(MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FAQ)
            //     add(
            //         MaxiPaySubscriptionsListViewItem.AdditionalInfoType.FaqAndAboutService,
            //     )
            // } else {
            //     add(MaxiPaySubscriptionsListViewItem.AdditionalInfoType.AboutService)
            // }

        }
    }.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())

    private fun showAddOtherSubscriptionDialogIfNeeded(
        oldMerchants: List<MaxiPayMerchantWithSubscriptions>,
        newMerchants: List<MaxiPayMerchantWithSubscriptions>,
    ) {
        if (oldMerchants.isEmpty()) return

        val atLeastOneOldSubscriptionWithConfirmedStatus = oldMerchants.any { merchant ->
            merchant.subscriptions.any { it.status == MaxiPaySubscription.Status.CONFIRMED }
        }
        if (atLeastOneOldSubscriptionWithConfirmedStatus) return

        val merchantsWithConfirmedSubscriptions = newMerchants.mapNotNull { merchant ->
            merchant.merchant.takeIf {
                merchant.subscriptions.any { it.status == MaxiPaySubscription.Status.CONFIRMED }
            }
        }

        if (merchantsWithConfirmedSubscriptions.isEmpty() ||
            merchantsWithConfirmedSubscriptions.any { !it.isMain } ||
            newMerchants.none { it.merchant.isMain }
        ) {
            return
        }

        _navigationEvent.trySend(MaxiPaySubscriptionListNavigationEvent.ShowAddOtherSubscriptionDialog)
    }

    private fun prepareMerchantsGroups(
        merchantsListData: MaxiPayMerchantsListData,
        merchants: List<MaxiPayMerchantWithSubscriptions>,
    ): List<MerchantsGroup> = buildList {
        val (mainMerchants, otherMerchants) = merchants.partition { it.merchant.isMain }

        if (mainMerchants.isNotEmpty()) {
            add(MerchantsGroup(merchantsListData.mainMerchantsListTitle, mainMerchants))
        }

        if (otherMerchants.isNotEmpty()) {
            add(MerchantsGroup(merchantsListData.otherMerchantsListTitle, otherMerchants))
        }
    }

    private fun List<MerchantsGroup>.getMerchantNameBySubscriptionUuid(subscriptionUuid: UUID): String? {
        return firstNotNullOfOrNull { merchantsGroup ->
            merchantsGroup.merchants.firstNotNullOfOrNull { merchant ->
                merchant.merchant.name
                    .takeIf { merchant.subscriptions.any { it.uuid == subscriptionUuid } }
            }
        }
    }

    /**
     * Обработать клик по счёту с идентификатором [uuid]
     */
    fun onSubscriptionClick(uuid: UUID) {
        val subscription = merchantsGroupsState.value.firstNotNullOfOrNull { merchantsGroup ->
            merchantsGroup.merchants.firstNotNullOfOrNull { merchant ->
                merchant.subscriptions.firstOrNull { it.uuid == uuid }
            }
        } ?: return

        when (subscription.status) {
            MaxiPaySubscription.Status.CREATED -> subscription.subscriptionUrl?.let { subscriptionUrl ->
                launchBankChooser(
                    uuid,
                    subscriptionUrl,
                    requireNotNull(merchantsGroupsState.value.getMerchantNameBySubscriptionUuid(uuid)),
                )
            }

            MaxiPaySubscription.Status.IN_PROCESS -> {}

            MaxiPaySubscription.Status.CONFIRMED -> viewModelScope.launch {
                try {
                    updatePrimarySubscription(uuid)
                } catch (exception: Exception) {
                    val errorMessage = (exception as? BusinessLogicException)?.message
                        ?: exception.localizedMessage
                    _messageEvent.send(errorMessage)
                }
            }
        }
    }

    /**
     * Обработать клик по кнопке добавления нового счёта для мерчанта с кодом [merchantCode]
     */
    fun onCreateSubscriptionClick(merchantCode: String) {
        val merchant = merchantsGroupsState.value
            .flatMap { it.merchants }
            .find { it.merchant.code == merchantCode }
            ?.merchant
            ?: return

        val analyticAction = if (merchant.isMain) {
            AnalyticMaxiPayActions.PRODUCT_PAY_BUTTON_ADD
        } else {
            AnalyticMaxiPayActions.ALKO_PAY_BUTTON_ADD
        }

        analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
            analyticType = AnalyticType.MAXI_PAY,
            analyticAction = analyticAction,
        )

        connectToSseConnectionIfAllowed()
        viewModelScope.launch {
            try {
                _blockingLoaderState.update { true }
                createSubscription(merchantCode)
            } catch (exception: Exception) {
                val errorMessage = (exception as? BusinessLogicValidException)?.message
                    ?: (exception as? BusinessLogicException)?.message
                    ?: exception.localizedMessage
                _messageEvent.send(errorMessage)
            } finally {
                _blockingLoaderState.update { false }
            }
        }
    }

    /**
     * Обработать разрешение отображения пуш-уведомлений
     */
    fun onNotificationsPermissionGranted() {
        val currentSettings = clientSettingsState.value ?: return
        val notificationsPermissionState = notificationPermissionRequestState ?: return
        if (notificationsPermissionState == NotificationPermissionRequestSource.FROM_RESUME) {
            notificationPermissionRequestState = null
            return
        }
        viewModelScope.launch {
            try {
                sendSavePaymentSettingsRequest()
            } catch (exception: Exception) {
                val errorMessage = (exception as? BusinessLogicException)?.message
                    ?: exception.localizedMessage
                _messageEvent.send(errorMessage)
                updateMaxiPayClientSettings(currentSettings.copy(usePaymentConfirmation = false))
            } finally {
                notificationPermissionRequestState = null
            }
        }
    }

    /**
     * Обработать запрет отображения пуш-уведомлений
     */
    fun onNotificationsPermissionDenied() {
        val currentSettings = clientSettingsState.value ?: return
        val notificationsPermissionState = notificationPermissionRequestState ?: return
        viewModelScope.launch {
            updateMaxiPayClientSettings(currentSettings.copy(usePaymentConfirmation = false))
            if (notificationsPermissionState == NotificationPermissionRequestSource.FROM_RESUME) {
                try {
                    sendSavePaymentSettingsRequest()
                } catch (exception: Exception) {
                    val errorMessage = (exception as? BusinessLogicException)?.message
                        ?: exception.localizedMessage
                    _messageEvent.send(errorMessage)
                    updateMaxiPayClientSettings(currentSettings)
                }
            }
            notificationPermissionRequestState = null
        }
    }

    /**
     * Проверить разрешение на отправку пуш-уведомлений
     */
    fun checkNotificationPermission() {
        val currentSettings = clientSettingsState.value ?: return
        if (!currentSettings.usePaymentConfirmation) return

        when (notificationPermissionRequestState) {
            NotificationPermissionRequestSource.FROM_TOGGLE ->
                if (notificationsPermissionUseCase.isNotificationPermissionGranted) {
                    onNotificationsPermissionGranted()
                } else {
                    onNotificationsPermissionDenied()
                }

            NotificationPermissionRequestSource.FROM_RESUME -> {
                notificationPermissionRequestState = null
            }

            null -> if (!notificationsPermissionUseCase.isNotificationPermissionGranted) {
                notificationPermissionRequestState = NotificationPermissionRequestSource.FROM_RESUME
                viewModelScope.launch {
                    _navigationEvent.send(MaxiPaySubscriptionListNavigationEvent.LaunchNotificationsPermissionRequest)
                }
            }
        }
    }

    override fun onPaymentConfirmationMinSumChange(minSum: Long) {
        val currentSettings = clientSettingsState.value ?: return

        analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
            analyticType = AnalyticType.MAXI_PAY,
            analyticAction = AnalyticMaxiPayActions.PROOF_PAY_BUTTON_SAVE,
            index = AnalyticsGeneralEvent.Index.IntIndex(minSum.toInt()),
        )

        viewModelScope.launch {
            try {
                paymentConfirmationSaveButtonState.update {
                    MaxiPaySubscriptionsListViewItem.PaymentConfirmationBlock.SaveButtonState.LOADING
                }
                updateMaxiPayClientSettings(currentSettings.copy(paymentConfirmationMinSum = minSum))
                sendSavePaymentSettingsRequest()
                _messageEvent.send(SAVE_BUTTON_MESSAGE_TEXT)
            } catch (exception: Exception) {
                val errorMessage = (exception as? BusinessLogicException)?.message
                    ?: exception.localizedMessage
                _messageEvent.send(errorMessage)
                updateMaxiPayClientSettings(currentSettings)
            } finally {
                paymentConfirmationSaveButtonState.update {
                    MaxiPaySubscriptionsListViewItem.PaymentConfirmationBlock.SaveButtonState.IDLE
                }
            }
        }
    }

    override fun onPaymentConfirmationStateChange(isEnabled: Boolean) {
        val currentSettings = clientSettingsState.value ?: return

        val analyticAction =
            if (isEnabled) AnalyticMaxiPayActions.PROOF_PAY_CONFIRM else AnalyticMaxiPayActions.PROOF_PAY_CANCEL
        analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
            analyticType = AnalyticType.MAXI_PAY,
            analyticAction = analyticAction,
        )

        viewModelScope.launch {
            try {
                updateMaxiPayClientSettings(currentSettings.copy(usePaymentConfirmation = isEnabled))
                if (isEnabled && !notificationsPermissionUseCase.isNotificationPermissionGranted) {
                    notificationPermissionRequestState = NotificationPermissionRequestSource.FROM_TOGGLE
                    _navigationEvent.send(MaxiPaySubscriptionListNavigationEvent.LaunchNotificationsPermissionRequest)
                    return@launch
                }
                sendSavePaymentSettingsRequest()
            } catch (exception: Exception) {
                val errorMessage = (exception as? BusinessLogicException)?.message
                    ?: exception.localizedMessage
                _messageEvent.send(errorMessage)
                updateMaxiPayClientSettings(currentSettings)
            }
        }
    }

    /**
     * Обработать событие [event] SSE-соединения
     */
    fun onSseEvent(event: MaxiPaySseEvent) {
        when (event) {
            is MaxiPaySseEvent.SubscriptionStateChange -> handleSubscriptionStateChangeEvent(event)
        }
    }

    private fun handleSubscriptionStateChangeEvent(event: MaxiPaySseEvent.SubscriptionStateChange) {
        viewModelScope.launch {
            try {
                sseUseCase.handleSubscriptionStateChangeEvent(event)
                if (event is MaxiPaySseEvent.SubscriptionStateChange.InProgress) {
                    launchBankChooser(
                        event.uuid,
                        event.subscriptionUrl,
                        requireNotNull(merchantsGroupsState.value.getMerchantNameBySubscriptionUuid(event.uuid)),
                    )
                }
            } catch (exception: Exception) {
                val errorMessage = (exception as? BusinessLogicException)?.message
                    ?: exception.localizedMessage
                _messageEvent.trySend(errorMessage)
            }
        }
    }

    private fun launchBankChooser(subscriptionUuid: UUID, subscriptionUrl: String, merchantName: String) {
        try {
            val uri = Uri.parse(subscriptionUrl)
            check(uri.isHierarchical && uri.host == "sub.nspk.ru")
            _navigationEvent.trySend(
                MaxiPaySubscriptionListNavigationEvent.OpenBankChooserScreen(subscriptionUuid, uri, merchantName),
            )
        } catch (ignored: Exception) {
        }
    }

    /**
     * Обработать результат [result] выбора банка для привязки счёта с идентификатором [subscriptionUuid]
     */
    fun onBankChooserResult(subscriptionUuid: UUID, result: SbpBankChooserResult) {
        when (result) {
            SbpBankChooserResult.ChooserDismissed -> {}
            is SbpBankChooserResult.OpenedInBankApp -> onBankChooserSelectedBankApp(subscriptionUuid, result.appInfo)
        }
    }

    /**
     * Обработать клик по редактированию мерчанта с кодом [merchantCode]
     */
    fun onEditMerchantClick(merchantCode: String) {
        val merchant = merchantsGroupsState.value
            .flatMap { it.merchants }
            .find { it.merchant.code == merchantCode }
            ?.merchant
            ?: return

        val analyticAction = if (merchant.isMain) {
            AnalyticMaxiPayActions.PRODUCT_PAY_BUTTON_CHANGE
        } else {
            AnalyticMaxiPayActions.ALKO_PAY_BUTTON_CHANGE
        }

        analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
            analyticType = AnalyticType.MAXI_PAY,
            analyticAction = analyticAction,
        )

        viewModelScope.launch {
            _navigationEvent.trySend(
                MaxiPaySubscriptionListNavigationEvent.OpenEditMerchantSubscriptionsScreen(merchantCode),
            )
        }
    }

    /**
     * Обработать клик по элементу [item] дополнительной информации
     */
    fun onAdditionalInfoItemClick(item: MaxiPayAdditionalInfo) {
        when (item) {
            is MaxiPayAdditionalInfo.Faq -> _navigationEvent.trySend(
                MaxiPaySubscriptionListNavigationEvent.OpenFaqScreen,
            )

            is MaxiPayAdditionalInfo.AboutService -> {
                Log.d("TESTING", "MaxiPaySubscriptionsListViewModel onAddititionalInfoItemClick AboutService")
                analyticsUseCase log AnalyticsGeneralEvent.SelectContent(
                    analyticType = AnalyticType.MAXI_PAY,
                    analyticAction = AnalyticMaxiPayActions.ABOUT_SERVICE_BUTTON_CLICK
                )
                _navigationEvent.trySend(
                    MaxiPaySubscriptionListNavigationEvent.OpenAboutServiceScreen
                )
            }

            is MaxiPayAdditionalInfo.FaqAndAboutService -> {}
        }
    }

    private fun onBankChooserSelectedBankApp(subscriptionUuid: UUID, bankData: BankAppInfo) {
        viewModelScope.launch {
            try {
                updateSubscriptionBankData(subscriptionUuid, bankData)
            } catch (ignored: Exception) {
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        disconnectFromSseConnectionIfAllowed()
    }

    private fun connectToSseConnectionIfAllowed() {
        if (hasActiveSseConnection) return
        hasActiveSseConnection = true
        sseUseCase.connectToSse(MaxiPaySseUseCase.SUBSCRIPTIONS_LIST_SCREEN_SSE_CLIENT_TAG)
    }

    private fun disconnectFromSseConnectionIfAllowed() {
        if (!hasActiveSseConnection) return
        hasActiveSseConnection = false
        sseUseCase.disconnectFromSse(MaxiPaySseUseCase.SUBSCRIPTIONS_LIST_SCREEN_SSE_CLIENT_TAG)
    }

    private companion object {
        const val SAVE_BUTTON_MESSAGE_TEXT = "Данные сохранены"
    }
}

private data class MerchantsGroup(
    val title: String,
    val merchants: List<MaxiPayMerchantWithSubscriptions>,
)

private fun MerchantsGroup.toViewItems(forceCreateButtonHidden: Boolean): List<MaxiPaySubscriptionsListViewItem> =
    buildList {
        add(titleViewItem)
        addAll(merchants.flatMap { it.toViewItems(forceCreateButtonHidden) })
    }

private val MerchantsGroup.titleViewItem: MaxiPaySubscriptionsListViewItem.Title
    get() = MaxiPaySubscriptionsListViewItem.Title(
        text = title,
    )

private fun MaxiPayMerchantWithSubscriptions.toViewItems(
    forceCreateButtonHidden: Boolean,
): List<MaxiPaySubscriptionsListViewItem> = buildList {
    add(headerViewItem)
    addAll(subscriptions.map { it.toViewItem(isSelected = it.uuid == selectedSubscriptionUuid) })
    add(getFooterViewItem(forceCreateButtonHidden))
}

private val MaxiPayMerchantWithSubscriptions.headerViewItem: MaxiPaySubscriptionsListViewItem.MerchantHeader
    get() = MaxiPaySubscriptionsListViewItem.MerchantHeader(
        merchantCode = merchant.code,
        merchantName = merchant.name,
        editButtonVisible = subscriptions.isNotEmpty(),
    )

private fun MaxiPayMerchantWithSubscriptions.getFooterViewItem(
    forceCreateButtonHidden: Boolean,
) = MaxiPaySubscriptionsListViewItem.MerchantFooter(
    merchantCode = merchant.code,
    createButtonVisible = !forceCreateButtonHidden &&
        subscriptions.all { it.status == MaxiPaySubscription.Status.CONFIRMED },
)

private fun MaxiPaySubscription.toViewItem(isSelected: Boolean) = MaxiPaySubscriptionsListViewItem.Subscription(
    uuid = uuid,
    name = name,
    status = status,
    iconUrl = iconUrl,
    hasSubscriptionUrl = subscriptionUrl != null,
    selected = isSelected,
)