DefaultTmdbConfigurationRepository.kt

package com.louisfn.somovie.data.repository

import androidx.annotation.AnyThread
import com.louisfn.somovie.core.common.annotation.DefaultDispatcher
import com.louisfn.somovie.core.common.extension.isExpired
import com.louisfn.somovie.core.common.provider.DateTimeProvider
import com.louisfn.somovie.data.datastore.datasource.DataStoreLocalDataSource
import com.louisfn.somovie.data.datastore.model.TmdbConfigurationData
import com.louisfn.somovie.data.mapper.TmdbConfigurationMapper
import com.louisfn.somovie.data.network.datasource.ConfigurationRemoteDataSource
import com.louisfn.somovie.domain.model.TmdbConfiguration
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.invoke
import java.util.concurrent.TimeUnit
import javax.inject.Inject

interface TmdbConfigurationRepository {

    @AnyThread
    fun tmdbConfigurationChanges(): Flow<TmdbConfiguration>

    @AnyThread
    suspend fun getTmdbConfiguration(): TmdbConfiguration

    @AnyThread
    suspend fun updateTmdbConfiguration(transform: suspend (t: TmdbConfiguration) -> TmdbConfiguration)

    @AnyThread
    suspend fun refreshIfCacheExpired(cacheTimeout: Long = DEFAULT_CACHE_TIMEOUT)

    companion object {
        private val DEFAULT_CACHE_TIMEOUT = TimeUnit.DAYS.toMillis(1)
    }
}

internal class DefaultTmdbConfigurationRepository @Inject constructor(
    private val remoteDataSource: ConfigurationRemoteDataSource,
    private val localDataSource: DataStoreLocalDataSource<TmdbConfigurationData>,
    private val mapper: TmdbConfigurationMapper,
    private val dateTimeProvider: DateTimeProvider,
    @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
) : TmdbConfigurationRepository {

    override fun tmdbConfigurationChanges(): Flow<TmdbConfiguration> =
        localDataSource.dataChanges()
            .map(mapper::mapToDomain)
            .flowOn(defaultDispatcher)

    override suspend fun getTmdbConfiguration(): TmdbConfiguration = defaultDispatcher {
        mapper.mapToDomain(localDataSource.getData())
    }

    override suspend fun updateTmdbConfiguration(transform: suspend (t: TmdbConfiguration) -> TmdbConfiguration) {
        localDataSource.updateData {
            mapper.mapToData(transform(mapper.mapToDomain(it)))
        }
    }

    override suspend fun refreshIfCacheExpired(cacheTimeout: Long) {
        val isExpired = getTmdbConfiguration().updatedAt.isExpired(dateTimeProvider.now(), cacheTimeout)
        if (isExpired) {
            localDataSource.updateData {
                mapper.mapToData(remoteDataSource.getConfiguration())
            }
        }
    }
}