package com.suno.android.media import arrow.core.getOrElse import com.suno.android.common_core_utils.ApplicationCoroutineScope import com.suno.android.common_core_utils.Id import com.suno.android.common_core_utils.SunoLogger import com.suno.android.common_core_utils.constants.ReactionType import com.suno.android.common_data.mappers.clips.LocalClipData import com.suno.android.common_data.mappers.clips.SongListData import com.suno.android.common_data.repos.ClipsRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import javax.inject.Inject interface MediaMetadataManager { fun mediaReactionFlow(): Flow fun mediaVisibilityFlow(): Flow fun mediaRemixabilityFlow(): Flow suspend fun broadcastMediaReaction( reaction: MediaReaction, ) suspend fun broadcastMediaVisibility( visibility: MediaVisibility, ) suspend fun broadcastMediaRemixability( canRemix: MediaRemixability, ) } class MediaMetadataManagerImpl @Inject constructor( loggerFactory: SunoLogger.Factory, @ApplicationCoroutineScope private val coroutineScope: CoroutineScope, val mediaManager: MediaManager, private val clipsRepository: ClipsRepository, ) : MediaMetadataManager { private val logger = loggerFactory.create(this@MediaMetadataManagerImpl) private val _mediaReactionSharedFlow = MutableSharedFlow() private val _mediaVisibilitySharedFlow = MutableSharedFlow() private val _mediaRemixabilitySharedFlow = MutableSharedFlow() override fun mediaReactionFlow(): Flow = _mediaReactionSharedFlow.asSharedFlow() override fun mediaVisibilityFlow(): Flow = _mediaVisibilitySharedFlow.asSharedFlow() override fun mediaRemixabilityFlow(): Flow = _mediaRemixabilitySharedFlow.asSharedFlow() override suspend fun broadcastMediaReaction( reaction: MediaReaction, ) { coroutineScope.launch(Dispatchers.Default) { val clip = getClip(reaction.clipId) ?: return@launch mediaManager.updateClip( oldLocalClipData = clip, newLocalClipData = clip.copy(reaction = reaction.reaction), ) } _mediaReactionSharedFlow.emit(reaction) } override suspend fun broadcastMediaVisibility( visibility: MediaVisibility, ) { coroutineScope.launch(Dispatchers.Default) { val clip = getClip(visibility.clipId) ?: return@launch mediaManager.updateClip( oldLocalClipData = clip, newLocalClipData = clip.copy(isPublic = visibility.isPublic), ) } _mediaVisibilitySharedFlow.emit(visibility) } override suspend fun broadcastMediaRemixability( canRemix: MediaRemixability, ) { coroutineScope.launch(Dispatchers.Default) { val clip = getClip(canRemix.clipId) ?: return@launch mediaManager.updateClip( oldLocalClipData = clip, newLocalClipData = clip.copy(canRemix = canRemix.canRemix), ) } _mediaRemixabilitySharedFlow.emit(canRemix) } private suspend fun getClip( clipId: Id, ): LocalClipData? = clipsRepository.getClipById(clipId) .getOrElse { error -> logger.e(error) return null } } data class MediaReaction( val clipId: Id, val reaction: ReactionType?, ) data class MediaVisibility( val clipId: Id, val isPublic: Boolean, ) data class MediaRemixability( val clipId: Id, val canRemix: Boolean, )