package com.grkj.iscs_mc.util

import android.content.Context
import cn.zhxu.data.Mapper
import cn.zhxu.okhttps.HTTP
import cn.zhxu.okhttps.HttpResult
import cn.zhxu.okhttps.HttpTask
import cn.zhxu.okhttps.OkHttps
import cn.zhxu.okhttps.gson.GsonMsgConvertor
import cn.zhxu.okhttps.okhttp.OkHttpClientWrapper
import com.grkj.iscs_mc.MyApplication
import com.grkj.iscs_mc.R
import com.grkj.iscs_mc.model.Constants.DEVICE_TYPE
import com.grkj.iscs_mc.model.Constants.DEVICE_TYPE_HYBRID
import com.grkj.iscs_mc.model.Constants.DEVICE_TYPE_MATERIAL
import com.grkj.iscs_mc.model.Constants.DEVICE_TYPE_NORMAL
import com.grkj.iscs_mc.model.Constants.DEVICE_TYPE_PORTABLE
import com.grkj.iscs_mc.model.Token
import com.grkj.iscs_mc.model.UrlConsts
import com.grkj.iscs_mc.model.UrlConsts.LOGIN_CARD
import com.grkj.iscs_mc.util.log.LogUtil
import okhttp3.logging.HttpLoggingInterceptor
import java.net.SocketTimeoutException
import java.util.concurrent.TimeUnit

class NetHttpManager {
    lateinit var myHttp: HTTP
    var context: Context? = null
    var exceptionCount: Int = 0

    companion object {
        fun getInstance() = InstanceHelper.sSingle
        val tagAuth = "Auth"
    }

    object InstanceHelper {
        val sSingle = NetHttpManager()
    }

    fun initCtx(ctx: Context) {
        context = ctx
        myHttp = HTTP.builder()
            .config {
                it.connectTimeout(60, TimeUnit.SECONDS)
                it.writeTimeout(20, TimeUnit.SECONDS)
                it.readTimeout(20, TimeUnit.SECONDS)
            }
            .addMsgConvertor(GsonMsgConvertor())
            .bodyType("application/json")
            .baseUrl(UrlConsts.BASE_URL)
            .responseListener { task: HttpTask<*>?, result: HttpResult? ->
                if (result?.status != 200) {
                    LogUtil.d(
                        "Api fail : Url : ${task?.url}, " +
                                "Status : ${result?.status}, " +
                                "Params : ${task?.urlParas ?: task?.bodyParas}"
                    )
                }
                true
            }
            .config {
                it.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            }
            .addSerialPreprocessor { itPreChain ->
                itPreChain.task.addHeader("Module", when (DEVICE_TYPE) {
                    2 -> DEVICE_TYPE_MATERIAL
                    3 -> DEVICE_TYPE_PORTABLE
                    4 -> DEVICE_TYPE_HYBRID
                    else -> DEVICE_TYPE_NORMAL
                })
                if (!itPreChain.task.isTagged(tagAuth)) {
                    itPreChain.proceed()
                    return@addSerialPreprocessor
                }
                requestTokenAndRefreshIfExpired {
                    itPreChain.task.addHeader("Authorization", it)
                    itPreChain.proceed()
                }
            }
            .build()
    }

    /**
     * 获取TOKEN，若过期则刷新（代码中的字符串可以替换为常量）
     */
    fun requestTokenAndRefreshIfExpired(callback: (String?) -> Unit) {
        val token = Token.fromSp(context!!)
        if (token == null) {
            doLogin(callback)
            return
        }
        if (token.isValid()) {
            callback(token.token)
            return
        }
        if (context == null || SPUtils.getLoginUser(context!!) == null) {
            ToastUtils.tip(context!!.resources.getString(R.string.please_login))
            return
        }
        doLogin(callback)
    }

    private fun doLogin(callback: (String?) -> Unit) {
        if (context == null) {
            return
        }
        val loginUser = SPUtils.getLoginUser(context!!)
        if (loginUser == null) {
            ToastUtils.tip(context!!.resources.getString(R.string.please_login))
            return
        }
        myHttp.async(LOGIN_CARD)
            .skipPreproc()
            .addBodyPara("cardNfc", loginUser.cardNfc)
            .nextOnIO()
            .setOnResponse {
                exceptionCount = 0
                if (!it.isSuccessful) {
                    callback(null)
                    return@setOnResponse
                }
                try {
                    val newToken = it.body.toBean(Token::class.java)
                    newToken.saveToSp(context!!)
                    callback(newToken.token)
                } catch (e: Exception) {
                    callback(null)
                    return@setOnResponse
                }
            }
            .setOnException {
                evictHttpConnectPool(it)
                callback(null)
            }
            .post()
    }

    fun doRequestNet(
        urlStr: String, isSkipPreproc: Boolean,
        bodyParas: Map<String, *>,
        callback: (HttpResult.Body?, String?, Int) -> Unit,
        isGet: Boolean, isAuth: Boolean,
        mapperCallBack: ((Mapper) -> Unit)? = null
    ) {
        var httpTask = myHttp.async(urlStr).setOnException {
            evictHttpConnectPool(it)
            callback(null, context?.getString(R.string.common_net_dis), 0)
        }
        if (isAuth) httpTask.tag(tagAuth)
        if (isSkipPreproc) {
            httpTask.skipPreproc()
        }
        httpTask.nextOnIO()
            .setOnResponse {
                exceptionCount = 0
                if (it.isSuccessful) {
                    MyApplication.instance?.applicationContext?.let { itCtx ->
                        Token.refresh(itCtx)
                    }
                    // TODO 待观察
                    try {
                        it.body.cache()
                        val baseVO = getBaseVO<Any>(it.body)
                        if (baseVO?.code in 200 until 300) {
                            callback(it.body, null, it.status)
                        } else {
                            baseVO?.msg?.let { itMsg ->
                                ToastUtils.tip(itMsg)
                            }
                            val bobyStr = it.body.toString()
                            callback(
                                null, if (bobyStr.isEmpty()) {
                                    it.toString()
                                } else {
                                    bobyStr
                                }, it.status
                            )
                        }
                    } catch (e: Exception) {
                        println("非标准返回数据，特殊处理：${e.message}")
                        callback(it.body, null, it.status)
                    }
                } else {
                    var bobyStr = it.body.toString()
                    callback(
                        null, if (bobyStr.isNullOrEmpty()) {
                            it.toString()
                        } else {
                            bobyStr
                        }, it.status
                    )
                }
            }
            .setOnException {
                evictHttpConnectPool(it)
                callback(null, context?.getString(R.string.common_net_dis), 0)
            }
        mapperCallBack?.let {
            httpTask.setOnResMapper {
                mapperCallBack.invoke(it)
            }
        }
//            .setOnResMapper {
//                mapperCallBack?.invoke(it)
//            }
        if (isGet) {
            httpTask.addUrlPara(bodyParas)
            httpTask.get()
        } else {
            httpTask.addBodyPara(bodyParas)
            httpTask.post()
        }
    }

    fun downloadFileWithProcess(
        url: String,
        downloadCallBack: DownloadCallBack,
        filePath: String? = null,
        folderPath: String? = null
    ) {
        try {
            if (filePath.isNullOrBlank() && folderPath.isNullOrBlank()) {
                downloadCallBack.onResult(
                    false,
                    errorMsg = context?.getString(R.string.common_download_erro_notag)
                )
                return
            }
            OkHttps.async(url).setOnResponse {
                exceptionCount = 0
                if (it.isSuccessful) {
                    if (filePath != null && filePath.isNotEmpty()) {
                        it.body
                            .stepRate(0.01)
                            .setOnProcess {
                                downloadCallBack.onProcess(it)
                            }
                            .toFile(filePath)
                            .setOnSuccess {
                                downloadCallBack.onResult(true, file = it)
                            }
                            .setOnFailure {
                                downloadCallBack.onResult(false, errorMsg = it.exception.toString())
                            }.start()
                    } else if (folderPath != null && folderPath.isNotEmpty()) {
                        it.body
                            .stepRate(0.01)
                            .setOnProcess {
                                downloadCallBack.onProcess(it)
                            }
                            .toFolder(folderPath)
                            .setOnSuccess {
                                downloadCallBack.onResult(true, file = it)
                            }
                            .setOnFailure {
                                downloadCallBack.onResult(false, errorMsg = it.exception.toString())
                            }.start()
                    }
                } else {
                    downloadCallBack.onResult(
                        false,
                        it.status,
                        errorMsg = context?.getString(R.string.common_net_download)
                    )
                }
            }.setOnException {
                evictHttpConnectPool(it)
                downloadCallBack.onResult(
                    false,
                    errorMsg = context?.getString(R.string.common_net_download)
                )
            }.get()
        } catch (e: Exception) {
            downloadCallBack.onResult(false, errorMsg = e.message)
        }
    }

    private fun evictHttpConnectPool(e: Exception) {
        try {
            if (e is SocketTimeoutException) {
                exceptionCount += 1
                if (exceptionCount >= 5) {
                    exceptionCount = 0
                    (myHttp as OkHttpClientWrapper).okClient().connectionPool().evictAll()
                }
            } else {
                exceptionCount = 0
            }
        } catch (e: Exception) {
        }
    }


}