package com.suno.android.common_data.repos import androidx.room.Transaction import arrow.core.getOrElse import com.suno.android.common_core_utils.SunoLogger import com.suno.android.common_data.mappers.clips.xAsDb import com.suno.android.common_data.mappers.playlists.xAsDbPlaylist import com.suno.android.common_db.database.daos.ClipsDao import com.suno.android.common_db.database.daos.PlaylistsDao import com.suno.android.common_db.database.entities.LocalPlaylistClipCrossRefEntity import com.suno.android.common_db.database.entities.PlaylistWithClips import com.suno.android.common_networking.extensions.toThrowable import com.suno.android.common_networking.remote.entities.PlaylistCreateSpec import com.suno.android.common_networking.remote.entities.PlaylistTrashSpec import com.suno.android.common_networking.remote.playlist.PlaylistService import kotlinx.coroutines.flow.Flow import javax.inject.Inject interface PlaylistsRepo { fun myPlaylistsFlow(): Flow> fun getPlaylistByIdFlow( id: String, ): Flow suspend fun fetchRemotePlaylistById( id: String, ) suspend fun syncMyPlaylists( page: Int, ) suspend fun deletePlaylistById( id: String, ) suspend fun createPlaylist( playlistName: String, isPublic: Boolean?, ) suspend fun updatePlaylistTitle( playlistName: String, ) suspend fun updatePlaylistPublicity( isPublic: Boolean, ) } class PlaylistsRepoImpl @Inject constructor( loggerFactory: SunoLogger.Factory, private val playlistsDao: PlaylistsDao, private val clipsDao: ClipsDao, private val playlistService: PlaylistService, ) : PlaylistsRepo { private val logger = loggerFactory.create(this@PlaylistsRepoImpl) override fun myPlaylistsFlow(): Flow> = playlistsDao.getAll() override fun getPlaylistByIdFlow( id: String, ): Flow = playlistsDao.getPlaylistById( playlistId = id, ) @Transaction override suspend fun fetchRemotePlaylistById( id: String, ) { val playlist = playlistService.getPlaylistById(id).getOrElse { error -> logger.e(error.toThrowable()) return } try { val playlistToInsert = playlist.xAsDbPlaylist() playlistsDao.insert(playlistToInsert) val clipsToInsert = playlist.playlistClips.map { it.clip.xAsDb() } clipsDao.insertAll(clipsToInsert) val crossRefsToInsert = playlist.playlistClips.map { LocalPlaylistClipCrossRefEntity(playlist.id, it.clip.id) } playlistsDao.insertAllPlaylistClipCrossRef(crossRefsToInsert) } catch (e: Exception) { logger.e(e) } } override suspend fun updatePlaylistTitle( playlistName: String, ) { TODO("Not yet implemented") } override suspend fun updatePlaylistPublicity( isPublic: Boolean, ) { TODO("Not yet implemented") } @Transaction override suspend fun syncMyPlaylists( page: Int, ) { val myPlaylist = playlistService.getMyPlaylist(page).getOrElse { error -> logger.e(error.toThrowable()) return } try { val playlists = myPlaylist.playlists val playlistsToInsert = playlists.map { it.xAsDbPlaylist() } playlistsDao.insertAll(playlistsToInsert) val clipsToInsert = playlists.map { playlist -> playlist.playlistClips.map { playlistClip -> playlistClip.clip.xAsDb() } }.flatten() clipsDao.insertAll(clipsToInsert) val crossRefsToInsert = playlists.map { playlist -> playlist.playlistClips.map { playlistClip -> LocalPlaylistClipCrossRefEntity( playlistId = playlist.id, clipId = playlistClip.clip.id, ) } }.flatten() playlistsDao.insertAllPlaylistClipCrossRef(crossRefsToInsert) } catch (exception: Exception) { logger.e(exception) } } override suspend fun createPlaylist( playlistName: String, isPublic: Boolean?, ) { val spec = PlaylistCreateSpec(playlistName, isPublic) val playlist = playlistService.createPlaylist(spec).getOrElse { error -> logger.e(error.toThrowable()) return } try { playlistsDao.insert(playlist.xAsDbPlaylist()) } catch (exception: Exception) { logger.e(exception) } } override suspend fun deletePlaylistById( id: String, ) { val spec = PlaylistTrashSpec(id, undoTrash = false) playlistService.trashPlaylist(spec).getOrElse { error -> logger.e(error.toThrowable()) return } try { playlistsDao.deleteById(id) } catch (exception: Exception) { logger.e(exception) } } }