package com.ydl.ydlcommon.base.config

import android.annotation.SuppressLint
import android.content.Context
import android.text.TextUtils
import com.facebook.stetho.okhttp3.StethoInterceptor
import com.google.gson.Gson
import com.ydl.pushserver.pushagent.YdlPushAgent
import com.ydl.ydlcommon.app.Apm
import com.ydl.ydlcommon.base.BaseApp
import com.ydl.ydlcommon.bean.AuthBean
import com.ydl.ydlcommon.bean.GatewayRequestDTO
import com.ydl.ydlcommon.data.PlatformDataManager
import com.ydl.ydlcommon.data.http.BaseAPIResponse
import com.ydl.ydlcommon.data.http.EncryptUtils
import com.ydl.ydlcommon.data.http.GsonProvider
import com.ydl.ydlcommon.data.http.RxUtils
import com.ydl.ydlcommon.data.http.api.ApiRequestUtil
import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.utils.LogUtil
import com.ydl.ydlcommon.utils.YDLCacheUtils
import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils
import com.ydl.ydlcommon.utils.log.AliYunLogConfig
import com.ydl.ydlcommon.utils.log.AliYunRichLogsHelper
import com.ydl.ydlnet.builder.config.OkHttpConfig
import com.ydl.ydlnet.builder.factory.ApiFactory
import com.ydl.ydlnet.builder.interceptor.log.RequestHandler
import com.ydl.ydlnet.builder.interceptor.log.RequestLogInterceptor
import com.yidianling.common.tools.RxAppTool
import com.yidianling.common.tools.RxDeviceTool
import com.yidianling.common.tools.ToastUtil
import io.reactivex.functions.Action
import okhttp3.*
import okio.Buffer
import okio.BufferedSink
import java.io.EOFException
import java.nio.charset.Charset
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap


/**
 * Created by haorui on 2019-08-30 .
 * Des: 默认Http配置参数类
 */
class HttpConfig {

    companion object {

        //====================应用默认配置====================

        private const val AUTHORIZATION_NAME = "Authorization"
        private const val AUTHORIZATION_JAVA_NAME = "AuthorizationJava"
        private const val SESSION_KEY = "dc59cf294f37d237c1f06240568ffe21"
        private var DYNAMIC_SESSION_KEY: String = "dc59cf294f37d237c1f06240568ffe21"

        private const val YDL = "Ydl"
        private const val UID = "uid"
        private const val FFROM = "ffrom"
        private const val IS_FROM_APP = "isFromApp"
        private const val OS_BUILD = "osBuild"
        private const val TS = "ts"
        private const val VERSION = "version"
        private const val TOKEN = "accessToken"
        private val OS_TYPE = "osType"// 1.ios 2.android
        private val APP_NAME =
            "appName"//用于标识 是哪个应用 yidianling:用户版 xinlizixun：心理咨询 haoshi：情感壹点灵 zhuanjia：专家版

        //验证签名失败
        private val AUTH_INEFFECTIVE_CODE = "-201"

        //====================网络环境====================
        /**
         * PHP API 地址
         */
        var PHP_BASE_URL = ""

        /**
         * Java API 地址
         */
        var JAVA_BASE_URL = ""

        /**
         * YDL H5 地址
         */
        var YDL_H5 = ""

        /**
         * H5 地址
         */
        var H5_URL = ""

        /**
         * M站 地址
         */
        var MH5_URL = ""

        /**
         * 新 H5 地址
         */
        var WEB_URL = ""

        /**
         * Java API 域名
         */
        var JAVA_URL = ""


        /**
         * 行为数据 地址
         */
        var ACTION_URL = ""

        var JAVA_LOGIN_BASE_URL = "" // 登录注册动态配置的网关地址

        var ENCRYPTION_APP_KEY = ""     // 接口加密appKey
        var ENCRYPTION_APP_SECRET = ""  // 接口加密appSecret
        var isEncryption = false
        var appDebug:Boolean=false

        fun getInstance(): HttpConfig {
            return Holder.INSTANCE
        }
    }

    fun createOkHttp(): OkHttpClient {
        val globalConfig = BaseApp.instance.getGlobalConfig()
        val context = BaseApp.getApp()
        appDebug = globalConfig.appDebug
        val appName = globalConfig.appName

        //获取证书
        val cerInputStream = context.resources.openRawResource(com.ydl.ydlcommon.R.raw.ydl_trust);
        val builder = OkHttpConfig.Builder(context)
        if (appDebug) {
            builder.setNetInterceptor(StethoInterceptor())
        } else {
            //使用预埋证书，校验服务端证书
            builder.setSslSocketFactory(cerInputStream)
        }

        return builder
            .setInterceptor(
                commonParams(),
                requestHead(appName),
                addEncryptionHeaderParamsInterceptor(),
                responseErrorInterceptor(),
                respErrorInterceptor()
            )
            .setRequestHandler(getRequestHandler())
            .setReadTimeout(15)
            .setWriteTimeout(15)
            .setConnectTimeout(15)
            .setDebug(appDebug)
            .build()
    }

    /**
     * 开启动态网关，请求头某些参数加密
     */
    private fun addEncryptionHeaderParamsInterceptor(): Interceptor {
        return Interceptor {
            if (isEncryption) {
                val timestamp = System.currentTimeMillis().toString() //值应该为毫秒数的字符串形式
                var path = HttpUrl.parse(JAVA_LOGIN_BASE_URL)?.encodedPath()
                when {
                    it.request().url().encodedPath().startsWith("/v3") -> {
                        path += it.request().url().encodedPath().substring(4)
                    }
                    it.request().url().encodedPath().startsWith("/api") -> {
                        path += it.request().url().encodedPath().substring(5)
                    }
                    else -> {
                        path + it.request().url().encodedPath()
                    }
                }
                val sign = EncryptionParams.getSign(path, timestamp)
                val request = it.request().newBuilder()
                    .addHeader("appKey", ENCRYPTION_APP_KEY)
                    .addHeader("sign", sign)
                    .addHeader("timestamp", timestamp)
                    .build()
                it.proceed(request)
            } else {
                it.proceed(it.request())
            }
        }
    }

    private fun respErrorInterceptor(): Interceptor {
        return Interceptor {
            val req = it.request()
            val resp = it.proceed(req)
            if (appDebug) return@Interceptor resp
            val code = resp.code()
            val message = resp.message()
            val api = req.url().encodedPath()
            if (!resp.isSuccessful) {
                Apm.reportCustom("network_request_error", "${api}(${code},${message})", Exception("${api}(${code},${message})"))
            } else {
                try {
                    val body = resp.body() ?: return@Interceptor resp
                    val buffer = body.source().buffer()
                    if(!isPlaintext(buffer)) return@Interceptor resp
                    val readString = buffer.clone().readString(Charset.forName("UTF-8"))
                    val fromJson = Gson().fromJson<BaseAPIResponse<Any>>(readString, BaseAPIResponse::class.java)
                    if (fromJson.code != "200" && fromJson.code != "0") {
                        Apm.reportCustom("network_business_error", "${api}(${fromJson.code},${fromJson.msg})", Exception("${api}(${fromJson.code},${fromJson.msg})"))
                    }
                } catch (throwable: Throwable) {
                    LogUtil.e(throwable.message)
                }

            }
            return@Interceptor resp
        }
    }
    private fun isPlaintext(buffer: Buffer): Boolean {
        return try {
            val prefix = Buffer()
            val byteCount = if (buffer.size() < 64L) buffer.size() else 64L
            buffer.copyTo(prefix, 0L, byteCount)
            var i = 0
            while (i < 16 && !prefix.exhausted()) {
                val codePoint = prefix.readUtf8CodePoint()
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false
                }
                ++i
            }
            true
        } catch (var6: EOFException) {
            false
        }
    }

    /**
     * 接口返回错误日志埋点
     */
    private fun responseErrorInterceptor(): Interceptor {
        return Interceptor {
            val request: Request = it.request()
            val originalResponse: Response = it.proceed(request)
            if (!appDebug){
                val code = originalResponse.code()
                // 接口返回错误的情况下，埋点告诉服务器原因
                if (code != 200) {
                    var params = ""
                    if (request.method() == "GET") {
                        params = request.url().query().toString()
                    } else if (request.method() == "POST") {
                        params = RequestLogInterceptor.parseParams(request)
                    }
                    val message = originalResponse.message()
                    val api = request.url().encodedPath()
                    ActionCountUtils.baiDuCount("ydl_user_error_business","error_log",api,params,message)
                    //阿里云忽略埋点接口
                    if (!api.contains("maidian/writeMaiDianData")){
                        AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.API, "error_log---api:$api---params:$params ---- message:$message")
                    }else{
                        AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.API, "writeMaiDianDataError:$api---params:$params ---- message:$message")
                    }
                }else{
                    var params = ""
                    if (request.method() == "GET") {
                        params = request.url().query().toString()
                    } else if (request.method() == "POST") {
                        params = RequestLogInterceptor.parseParams(request)
                    }
                    val message = originalResponse.message()
                    val api = request.url().encodedPath()
                    if (api.contains("maidian/writeMaiDianData")){
                        AliYunRichLogsHelper.getInstance().sendRichLog(AliYunLogConfig.API, "writeMaiDianDatacode$code:$api---params:$params ---- message:$message")
                    }
                }
            }
            originalResponse
        }
    }

    private fun getRequestHandler(): RequestHandler {
        return object : RequestHandler {
            override fun onHttpResultResponse(
                httpResult: String,
                chain: Interceptor.Chain,
                response: Response
            ): Response {
                try {
                    val gson = GsonProvider.getGson()
                    val resultData = gson.fromJson(httpResult, BaseAPIResponse::class.java)
                    when (resultData.code) {
                        AUTH_INEFFECTIVE_CODE -> {
                            //签证签名失败
                            ToastUtil.toastShort(resultData.msg)
                            //更新动态密钥
                            if (resultData.data != null) {
                                var authBean = gson.fromJson<AuthBean>(
                                    gson.toJson(resultData.data),
                                    AuthBean::class.java
                                )
                                DYNAMIC_SESSION_KEY = authBean?.appKey.toString()
                            }
                        }
                    }
                } catch (e: Exception) {
                    LogUtil.e("getRequestHandler:$e")
                }
                return response
            }

            override fun onHttpRequestBefore(chain: Interceptor.Chain?, request: Request): Request {
                return request
            }

        }
    }

    // 添加公共参数
    private fun commonParams(): Interceptor {
        return Interceptor {
            var request = it.request()
            //如果是POST请求，则再在Body中增加公共参数
            if ("POST" == request.method()) {
                var body = request.body()

                if (body is FormBody) {
                    val paramsName = mutableSetOf<String>()
                    val bodyBuild = FormBody.Builder()

                    for (i in 0 until body.size()) {
                        val name = body.name(i)
                        paramsName.add(name)
                        val value = body.value(i)
                        // 去除假参数和值为空的参数
                        if (YDLConstants.HOLDER_PARAM != name && !TextUtils.isEmpty(value)) {
                            bodyBuild.addEncoded(name, value)
                        }
                    }

                    val paramsValue = getCommonParams(paramsName)

                    paramsValue.forEach { entry ->
                        bodyBuild.addEncoded(entry.key, entry.value)
                    }

                    body = bodyBuild.build()
                }
                it.proceed(request.newBuilder().post(body!!).build())
            } else if ("GET" == request.method()) {
                val url = request.url()
                val newBuilder = url.newBuilder()

                val paramsName = url.queryParameterNames()
                val paramsValue = getCommonParams(paramsName)
                paramsValue.forEach { entry ->
                    newBuilder.addQueryParameter(entry.key, entry.value)
                }

                it.proceed(request.newBuilder().url(newBuilder.build()).build())
            } else {
                it.proceed(request)
            }

        }
    }

    /**
     * 获取公共参数
     * 在原有请求中没有该参数的情况下才添加
     */
    private fun getCommonParams(paramsName: MutableSet<String>): HashMap<String, String> {
        val paramsValue = hashMapOf<String, String>()

        if (!paramsName.contains(FFROM)) {
            paramsValue[FFROM] = PlatformDataManager.getRam().getChannelName()
        }
        if (!paramsName.contains(IS_FROM_APP)) {
            paramsValue[IS_FROM_APP] = "1"
        }
        if (!paramsName.contains(OS_BUILD)) {
            paramsValue[OS_BUILD] =
                """${RxDeviceTool.getBuildBrandModel()},${RxDeviceTool.getSDKVersionName()},${RxAppTool.getAppVersionName(
                    BaseApp.getApp()
                )}"""
        }
        if (!paramsName.contains(TS)) {
            paramsValue[TS] = (System.currentTimeMillis() / 1000).toString()
        }
        if (!paramsName.contains(VERSION)) {
            paramsValue[VERSION] = RxAppTool.getAppVersionName(
                BaseApp.getApp()
            )
        }
        val loginBean = ModularServiceManager.getPlatformUserService()?.getUser()
        if (loginBean != null && !paramsName.contains(UID)) {
            paramsValue[UID] = loginBean.userId
        }
        if (loginBean != null && !paramsName.contains(TOKEN)) {
            paramsValue[TOKEN] = loginBean.token
        }

        return paramsValue
    }


    private data class Param(val name: String, val value: String)

    // 添加请求头
    private fun requestHead(appName: String): Interceptor {
        return Interceptor {
            val request = it.request()
            val paramsString = StringBuilder()
            val params = ArrayList<Param>()
            if ("POST" == request.method()) {
                val body = request.body()
                if (body is FormBody) {
                    (0 until body.size()).mapTo(destination = params) {
                        Param(
                            body.name(it),
                            body.value(it)
                        )
                    }

                } else if (body is MultipartBody) {
                    for (part in body.parts()) {
                        if ("text/plain; charset=utf-8" == part.body().contentType()!!.toString()) {
                            val headerStr = part.headers()!!.toString()
                            val name =
                                headerStr.split("\"\\n".toRegex()).dropLastWhile { it.isEmpty() }
                                    .toTypedArray()[0].split("=\"".toRegex())
                                    .dropLastWhile { it.isEmpty() }.toTypedArray()[1]
                            val buffer = Buffer()
                            part.body().writeTo(buffer as BufferedSink)
                            val value = buffer.readUtf8()
                            params.add(Param(name, value))
                        }
                    }
                }
            } else if ("GET" == request.method()) {
                val url = request.url()
                val queryParameterNames = url.queryParameterNames()
                queryParameterNames.forEach { string ->
                    params.add(Param(string, url.queryParameter(string) ?: ""))
                }
            }

            params.sortWith(Comparator { o1, o2 ->
                val res = o1.name.compareTo(o2.name)
                when {
                    res == 0 -> 0
                    res > 0 -> -1
                    else -> 1
                }
            })
            params.withIndex().forEach { (index, param) ->
                if (index != 0) paramsString.append("&")
                paramsString.append("${param.name}=${param.value}")
            }

            val oldAuth = getOldAuth(paramsString)
            val newAuth = getNewAuth(paramsString)

            val builder = it.request()
                .newBuilder()
                .header(AUTHORIZATION_NAME, oldAuth)
                .header(AUTHORIZATION_JAVA_NAME, newAuth)
                .addHeader("Connection", "close")
                .addHeader(FFROM, PlatformDataManager.getRam().getChannelName())
                .addHeader(IS_FROM_APP, "1")
                .addHeader(VERSION, RxDeviceTool.getAppVersionName(BaseApp.getApp()))
                .addHeader(
                    OS_BUILD,
                    RxDeviceTool.getBuildMANUFACTURER() + "," + RxDeviceTool.getBuildBrandModel() + "#" + RxDeviceTool.getOsBuileVersion() + "#" + RxDeviceTool.getAppVersionName(
                        BaseApp.getApp()
                    )
                )
                .addHeader(OS_TYPE, "2")
                .addHeader(APP_NAME, appName)
            val loginBean = ModularServiceManager.getPlatformUserService()?.getUser()
            if (loginBean != null) {
                builder.addHeader(TOKEN, loginBean.token)
                    .addHeader(UID, loginBean.userId)
            }
            it.proceed(builder.build())
        }
    }

    private fun getOldAuth(paramsString: StringBuilder): String {
        return "$YDL ${EncryptUtils.encryptMD5ToString(
            paramsString.toString() + SESSION_KEY
        )}"
    }

    private fun getNewAuth(paramsString: StringBuilder): String {
        //md5({静态秘钥} + {参数} + md5{动态秘钥(明文)}
        return "$YDL ${EncryptUtils.encryptMD5ToString(
            SESSION_KEY + paramsString.toString() + EncryptUtils.encryptMD5ToString(
                DYNAMIC_SESSION_KEY
            )
        )}"
    }

    //初始化网络环境
    fun initEnv(c: Context, env: String) {
        var appEnv = env
        //判断是否使用上次缓存环境
        if (!TextUtils.isEmpty(YDLCacheUtils.getCacheApi())) {
            appEnv = YDLCacheUtils.getCacheApi()
        }

        val properties = Properties()

        //从properties文件中读取网络环境信息
        val inputStr = c.resources.openRawResource(com.ydl.ydlcommon.R.raw.api)
        properties.load(inputStr)
        inputStr.close()

        PHP_BASE_URL = properties.getProperty("serverurl.$appEnv")
        JAVA_BASE_URL = properties.getProperty("javaapi.$appEnv")
        JAVA_LOGIN_BASE_URL = JAVA_BASE_URL
        ENCRYPTION_APP_KEY = properties.getProperty("appKey.$appEnv")
        ENCRYPTION_APP_SECRET = properties.getProperty("appSecret.$appEnv")

        ACTION_URL = properties.getProperty("actionurl.$appEnv")
        H5_URL = properties.getProperty("h5url.$appEnv")
        MH5_URL = properties.getProperty("mh5url.$appEnv")
        YDL_H5 = properties.getProperty("ydlh5url.$appEnv")
        WEB_URL = properties.getProperty("weburl.$appEnv")
        JAVA_URL = properties.getProperty("javaurl.$appEnv")
    }

    @SuppressLint("CheckResult")
    fun initAuth() {
        ApiRequestUtil.getDynamicToken()
            .compose(RxUtils.applySchedulers())
            .compose(RxUtils.resultJavaData())
            .subscribe({
                if (!TextUtils.isEmpty(it.appKey)) {
                    DYNAMIC_SESSION_KEY = it?.appKey.toString()
                }
            }) {
                LogUtil.i("HttpConfig", it.toString())
            }
    }

    @SuppressLint("CheckResult")
    fun initSocketConfig(finallyAction: Action) {
        ApiRequestUtil.getJavaGlobalInfo()
            .compose(RxUtils.applySchedulers())
            .compose(RxUtils.resultJavaData())
            .doFinally(finallyAction)
            .subscribe({
                if (it != null && !TextUtils.isEmpty(it.ip)) {
                    YdlPushAgent.setDebugAdree(it.ip, it.port.toInt())
                }
            }) {
                LogUtil.i("HttpConfig", it.toString())
            }
    }

    /**
     * 注册登录动态获取网关
     *
     */
    @SuppressLint("CheckResult")
    fun initLoginBaseUrlConfig(urlMap: HashMap<String, String>) {
        val map = HashMap<String, Any>()
        val list = ArrayList<GatewayRequestDTO>()
        val gatewayRequestDTO = GatewayRequestDTO("login")
        list.add(gatewayRequestDTO)
        map["gatewayRequestDTOList"] = list
        ApiRequestUtil.getBaseUrl(map)
            .compose(RxUtils.applySchedulers())
            .compose(RxUtils.resultJavaData())
            .subscribe({
                if (it.baseUrlGatewayDTOList.isNotEmpty()) {
                    isEncryption = it.baseUrlGatewayDTOList[0].goGateway
                    if (isEncryption) {
                        JAVA_LOGIN_BASE_URL = it.baseUrlGatewayDTOList[0].baseUrl + "/"
                        urlMap[YDL_DOMAIN_LOGIN_BASE_URL] = JAVA_LOGIN_BASE_URL
                        ApiFactory.getInstance().setMultipleUrlMap(urlMap)
                    }
                }
            }) {
                LogUtil.i("HttpConfig", it.toString())
            }
    }

    private object Holder {
        val INSTANCE = HttpConfig()
    }
}
