package com.suno.android.common_data.billing import arrow.core.Either import arrow.core.getOrElse import arrow.core.left import arrow.core.right import com.suno.android.common_core_utils.SunoLogger import com.suno.android.common_core_utils.environment.UserPrefsDataStoreManager import com.suno.android.common_data.billing.mappers.xAsPlanTransitionRules import com.suno.android.common_data.billing.models.PlanTransitionRules import com.suno.android.common_data.mappers.cms.SubscriptionPageCmsContent import com.suno.android.common_data.mappers.cms.toSubscriptionPageCmsContent import com.suno.android.common_networking.cms.CMSService import com.suno.android.common_networking.extensions.toThrowable import com.suno.android.common_networking.remote.billing.BillingService import com.suno.android.common_networking.remote.entities.SubscriptionInfoResponse import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.update import javax.inject.Inject interface SunoBillingRepo { fun billingStateFlow(): StateFlow fun refreshBillingStateFlow(): Flow suspend fun refreshBillingState(): Boolean suspend fun getSubscriptionInfoResponse(): Either suspend fun getPlanTransitionRules(): Either suspend fun getSubscriptionScreenContent(): Result } class SunoBillingRepoImpl @Inject constructor( loggerFactory: SunoLogger.Factory, private val billingService: BillingService, private val cmsService: CMSService, private val userPrefsDataStoreManager: UserPrefsDataStoreManager, ) : SunoBillingRepo { private val logger = loggerFactory.create(this@SunoBillingRepoImpl) private val _billingStateFlow: MutableStateFlow = MutableStateFlow(null) override fun billingStateFlow(): StateFlow = _billingStateFlow.asStateFlow() override suspend fun refreshBillingState(): Boolean { val response = billingService.getSubscriptionInfo().getOrElse { error -> logger.e(error.toThrowable()) { "Failed to fetch billing info" } userPrefsDataStoreManager.setSelectedModelIfEmpty(selectedModel = SelectedModelProvider.FALLBACK_MODEL_NAME) return false } _billingStateFlow.update { response } setDefaultSelectedModelIfEmpty(response) return true } private suspend fun setDefaultSelectedModelIfEmpty( response: SubscriptionInfoResponse, ) { val defaultModel = response.models.find { it.isDefaultModel == true && !it.externalKey.isNullOrBlank() } val selectedModelName = defaultModel?.name ?: SelectedModelProvider.FALLBACK_MODEL_NAME userPrefsDataStoreManager.setSelectedModelIfEmpty(selectedModel = selectedModelName) } override fun refreshBillingStateFlow(): Flow = flow { emit( refreshBillingState(), ) } override suspend fun getSubscriptionInfoResponse(): Either { val subscription = billingService.getSubscriptionInfo().getOrElse { error -> logger.e(error.toThrowable()) return error.toThrowable().left() } return subscription.right() } override suspend fun getPlanTransitionRules(): Either { val usagePlanConfig = billingService.getUsagePlanConfig().getOrElse { error -> logger.e(error.toThrowable()) return error.toThrowable().left() } return usagePlanConfig.xAsPlanTransitionRules() } override suspend fun getSubscriptionScreenContent(): Result { val response = cmsService.getSubscriptionPageCms().getOrElse { error -> val throwable = error.toThrowable() return Result.failure(throwable) } val content = response.toSubscriptionPageCmsContent() return Result.success(content) } }