package com.streamflixreborn.streamflix.fragments.people import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.streamflixreborn.streamflix.database.AppDatabase import com.streamflixreborn.streamflix.models.Movie import com.streamflixreborn.streamflix.models.People import com.streamflixreborn.streamflix.models.TvShow import com.streamflixreborn.streamflix.utils.UserPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.launch class PeopleViewModel(private val id: String, database: AppDatabase) : ViewModel() { private val _state = MutableStateFlow(State.Loading) @OptIn(ExperimentalCoroutinesApi::class) val state: Flow = combine( _state, _state.transformLatest { state -> when (state) { is State.SuccessLoading -> { val movies = state.people.filmography .filterIsInstance() if (movies.isEmpty()) { emit(emptyList()) } else { emitAll(database.movieDao().getByIds(movies.map { it.id })) } } else -> emit(emptyList()) } }, _state.transformLatest { state -> when (state) { is State.SuccessLoading -> { val tvShows = state.people.filmography .filterIsInstance() if (tvShows.isEmpty()) { emit(emptyList()) } else { emitAll(database.tvShowDao().getByIds(tvShows.map { it.id })) } } else -> emit(emptyList()) } }, ) { state, moviesDb, tvShowsDb -> when (state) { is State.SuccessLoading -> { val moviesById = moviesDb.associateBy { it.id } val tvShowsById = tvShowsDb.associateBy { it.id } State.SuccessLoading( people = state.people.copy( filmography = state.people.filmography.map { item -> when (item) { is Movie -> moviesById[item.id] ?.takeIf { !item.isSame(it) } ?.let { item.copy().merge(it) } ?: item is TvShow -> tvShowsById[item.id] ?.takeIf { !item.isSame(it) } ?.let { item.copy().merge(it) } ?: item } } ), hasMore = state.hasMore ) } else -> state } }.flowOn(Dispatchers.IO) private var page = 1 sealed class State { data object Loading : State() data object LoadingMore : State() data class SuccessLoading(val people: People, val hasMore: Boolean) : State() data class FailedLoading(val error: Exception) : State() } init { getPeople(id) } fun getPeople(id: String) = viewModelScope.launch(Dispatchers.IO) { _state.emit(State.Loading) try { val people = UserPreferences.currentProvider!!.getPeople(id) page = 1 _state.emit(State.SuccessLoading(people, true)) } catch (e: Exception) { Log.e("PeopleViewModel", "getPeople: ", e) _state.emit(State.FailedLoading(e)) } } fun loadMorePeopleFilmography() = viewModelScope.launch(Dispatchers.IO) { val currentState = _state.value if (currentState is State.SuccessLoading) { _state.emit(State.LoadingMore) try { val people = UserPreferences.currentProvider!!.getPeople(id, page + 1) page += 1 _state.emit( State.SuccessLoading( people = currentState.people.copy( filmography = currentState.people.filmography + people.filmography ), hasMore = people.filmography.isNotEmpty(), ) ) } catch (e: Exception) { Log.e("PeopleViewModel", "loadMorePeopleFilmography: ", e) _state.emit(State.FailedLoading(e)) } } } }