package com.atmob.keyboard_android.ext

import com.atmob.keyboard_android.model.base.BaseResp
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resumeWithException

/**
 * 回调异常
 */
class CallbackException(
    val errorCode: Int,
    val errorMsg: String
) : Exception("errorCode=$errorCode, errorMsg=$errorMsg")

/**
 * 将传统的回调方式转换为Kotlin协程的可挂起函数
 */
@OptIn(ExperimentalCoroutinesApi::class)
suspend inline fun <T> callback2Coroutines(
    crossinline block: (onSuccess: (T) -> Unit, onFail: (Int, String) -> Unit) -> Unit
): T = suspendCancellableCoroutine { continuation ->
    block(
        { result ->
            continuation.resume(result) {
                // 当协程在 resume 之后被取消时的处理
            }
        },
        { code, msg ->
            // 带取消回调的异常恢复
            continuation.resumeWithException(
                CallbackException(code, msg)
            )
        }
    )

    // 协程被取消时的操作
    continuation.invokeOnCancellation {
    }
}

/**
 * 协程转Flow
 */
fun <T> asFlow(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    block: suspend () -> T
): Flow<BaseResp<T>> = flow {
    emit(BaseResp.success<T>(block()))
}.catch { e ->
    emit(
        when (e) {
            is CallbackException -> BaseResp.error(e.errorCode, e.errorMsg)
            else -> BaseResp.error()
        }
    )
}.flowOn(dispatcher)