package com.qimai.android.fetch.extention

import android.text.TextUtils
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import com.qimai.android.fetch.Response.ErrorData
import com.qimai.android.fetch.Response.Resource
import com.qimai.android.fetch.Response.Status
import com.qimai.android.fetch.convert.BizException
import com.qimai.android.fetch.convert.ServerResponseException
import com.qimai.android.fetch.convert.TOKENExpireException
import com.qimai.android.fetch.model.BaseData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLHandshakeException

/**
 *   created by wangwei   ON 2019-09-06  email:wangwei_5521@163.com
 *   @version 1.1.1
 *   @Description
 **/
private const val UNAUTHORIZED = 401
private const val FORBIDDEN = 403
private const val NOT_FOUND = 404
private const val REQUEST_TIMEOUT = 408
private const val INTERNAL_SERVER_ERROR = 500
private const val BAD_GATEWAY = 502
private const val SERVICE_UNAVAILABLE = 503
private const val GATEWAY_TIMEOUT = 504


fun <T> ViewModel.safeCall(liveData: MutableLiveData<Resource<T>>, call: suspend () -> T) {
    viewModelScope.launch {
        liveData.value = Resource.loading(null)
        try {
            val data = call.invoke()
            liveData.value = Resource.success(data)
        } catch (t: Throwable) {
            liveData.value = handleResponseError(t)
        }
    }
}

fun <T> ViewModel.safeCall(call: suspend () -> T) = liveData<Resource<T>>(viewModelScope.coroutineContext) {
    emit(Resource.loading(null))
    try {
        val data = call.invoke()
        emit(Resource.success(data))
    } catch (t: Throwable) {
        emit(handleResponseError(t))
    }
}

/***
 * 基于flow实现 网络请求  这里不需要写 flow 直接写请求
 */
@ExperimentalCoroutinesApi
fun <T> ViewModel.flowSafeCall(request: () -> T) = liveData<Resource<T>> {
    flow {
        emit(request.invoke())
    }.onStart {
        this@liveData.emit(Resource.loading())
    }.flowOn(Dispatchers.IO)
        .catch { message ->
            this@liveData.emit(handleResponseError(message))
        }.collectLatest {
            this@liveData.emit(Resource.success(it))
        }
}

/***
 * 基于flow实现 网络请求  这里需要写 flow{  emit(request)}
 */
 fun <T> ViewModel.flowCall( flow: () -> Flow<T>) =
    liveData<Resource<T>> {
        flow().onStart {
            this@liveData.emit(Resource.loading())
        }
            .flowOn(Dispatchers.IO)
            .catch { message ->
                this@liveData.emit(handleResponseError(message))
            }
            .collectLatest {
                this@liveData.emit(Resource.success(it))
            }
    }

fun <T> handleResponseError(throwable: Throwable?): Resource<T> {
  //  Log.d(TAG, "handleResponseError: throwable= $throwable")
    return when (throwable) {
        null -> Resource.error("程序出错", ErrorData(originThrowable = throwable))
        is HttpException -> when (throwable.code()) {
            UNAUTHORIZED, FORBIDDEN, NOT_FOUND, REQUEST_TIMEOUT,
            INTERNAL_SERVER_ERROR, BAD_GATEWAY, SERVICE_UNAVAILABLE, GATEWAY_TIMEOUT -> {
                Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
            }
            else -> {
                Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
            }
        }
        is TOKENExpireException ->
            Resource.error(throwable.message ?: "token过期", ErrorData(originThrowable = throwable))

        is ServerResponseException -> // 解析json
            Resource.error("服务端返回格式错误", ErrorData(originThrowable = throwable))
        is ConnectException, is UnknownHostException ->
            Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
        is BizException ->
            Resource.error(
                throwable.message!!,
                ErrorData(throwable.code.toString(),errorMsg= throwable.message,originThrowable = throwable)

            )
        is UnknownHostException  ->
            Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
        is SSLHandshakeException  ->
            Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
        is SocketTimeoutException ->
            Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
        is SocketException ->
            Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
        else -> {
            if (!TextUtils.isEmpty(throwable.message)) {
                Resource.error(throwable.message!!, ErrorData(originThrowable = throwable))
            } else {
                Resource.error("当前网络状态不稳定,请检查当前网络", ErrorData(originThrowable = throwable))
            }
        }

    }
}