package com.framework.mvvm.model.data

import kotlinx.coroutines.flow.*


/**
 * 数据源函数
 *
 * @param     fromDb: 数据库获取数据
 * @param     fromNt: 网络获取数据
 * @param   isFromNt: 判断是否需要从网络获取数据
 * @param  onSuccess: 获取资源成功回调
 * @param  onFailure: 获取资源失败回调
 *
 * @param <RequestType> : 网络请求返回类型的封装
 * @param  <ResultType> : 最终数据返回类型的封装
 *
 */

inline fun <ResultType, RequestType> dataSource(
    crossinline fromDb:  () -> Flow<ResultType>,
    crossinline fromNt: suspend () -> RequestType,
    crossinline onSuccess: suspend (RequestType) -> Unit,
    crossinline onFailure: (Throwable) -> Unit = {},
    crossinline isFromNt: (ResultType) -> Boolean = { true }
) = flow<Data<ResultType>> {
    emit(Data.loading())

    val data = fromDb().firstOrNull()
    val isNt = data == null || isFromNt(data)

    val flow = if (!isNt) {
        fromDb().map { Data.success(it) }
    } else {
        try {
            onSuccess(fromNt())
            fromDb().map { Data.success(it) }
        } catch (t: Throwable) {
            onFailure(t)
            fromDb().map { Data.failure(t.message ?: "") }
        }
    }

    emitAll(flow)
}
/*
fun <RequestType, ResultType> dataSource(
    fromDb: suspend () -> LiveData<ResultType>,
    fromNt: suspend () -> Response<RequestType>,
    isFromNt: (ResultType) -> Boolean = { true },
    onSuccess: suspend (RequestType) -> Unit,
    onFailure: ((FailureResponse<RequestType>) -> Unit)? = null,
): LiveData<Data<ResultType>> {
    return DataSource(
        fromDb = fromDb,
        fromNt = fromNt,
        isFromNt = isFromNt,
        onSuccess = onSuccess,
        onFailure = onFailure
    ).asLiveData().distinctUntilChanged()
}


*/
/**
 * 数据源
 *//*

private class DataSource<RequestType, ResultType> @MainThread constructor(
    private val fromDb: suspend () -> LiveData<ResultType>,          // 磁盘获取数据
    private val fromNt: suspend () -> Response<RequestType>,      // 远程获取数据
    private val isFromNt: (ResultType) -> Boolean = { true },        // 是否获取数据
    private val onSuccess: suspend (RequestType) -> Unit,                    // 远程数据回调
    private val onFailure: ((FailureResponse<RequestType>) -> Unit)? = null
) {
    private val result = liveData<Data<ResultType>> {
        if (latestValue?.state != State.SUCCESS) {
            emit(Data.loading())
        }
        val db: LiveData<ResultType> = fromDb()
        val init = db.await()
        val isFromNt = init == null || isFromNt(init)

        if (!isFromNt) {
            emitSource(db.map { Data.success(it) })
        } else {
            doFromNt(db, this)
        }
    }

    private suspend fun doFromNt(
        db: LiveData<ResultType>,
        scope: LiveDataScope<Data<ResultType>>
    ) {
        val dbSource = scope.emitSource(db.map { Data.loading() })
        when (val response = fromNt()) {
            is EmptyResponse -> {
                scope.emitSource(fromDb().map { Data.success(it) })
            }
            is SuccessResponse -> {
                val data = response.data
                dbSource.dispose()
                onSuccess(data)
                scope.emitSource(fromDb().map { Data.success(it) })
            }
            is FailureResponse -> {
                onFailure?.invoke(response)
                scope.emitSource(db.map { Data.failure(response.msg) })
            }
        }
    }

    fun asLiveData(): LiveData<Data<ResultType>> = result

    private suspend fun <T> LiveData<T>.await() = withContext(Dispatchers.Main) {
        val deferred = CompletableDeferred<T?>()
        val observer = Observer<T> {
            if (deferred.isActive) {
                deferred.complete(it)
            }
        }

        try {
            observeForever(observer)
            return@withContext deferred.await()
        } finally {
            removeObserver(observer)
        }
    }
}*/
