/*
 * Copyright (C) 2015 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.qimai.android.fetch.callAdapter

import android.util.Log
import com.google.gson.Gson
import com.qimai.android.fetch.convert.BizException
import com.qimai.android.fetch.convert.GsonParseErrorException
import com.qimai.android.fetch.convert.ServerResponseException
import com.qimai.android.fetch.interceptors.GsonParseError
import com.qimai.android.fetch.interceptors.ServerError
import okhttp3.Headers
import okhttp3.MediaType
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
import okio.Buffer
import retrofit2.*
import java.io.EOFException
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.nio.charset.Charset
import java.util.*

/****
 * 针对于使用协程请求的方法，写的代理最终发起网络请求的类  模仿自 DefaultCallAdapterFactory  主要是为了获取请求失败时候的call
 *
 * @param ServerError  目前订的是服务器返回false 的回调
 * @param gsonParseError  gson解析异常时候的回调
 */
class KotlinCoroutinesCallAdapterFactory(
    var serverError: ServerError? = null,
    var gsonParseError: GsonParseError? = null,
    var successResponseInfo: ((
        requestUrl: String,  //请求url
        requestInfo: String?   //请求信息
        , responseInfo: String?  //响应信息

    ) -> Unit)? = null
) :
    CallAdapter.Factory() {
    override fun get(
        returnType: Type, annotations: Array<Annotation>, retrofit: Retrofit
    ): CallAdapter<*, *>? {
        if (getRawType(returnType) != Call::class.java) {
            return null
        }
        require(returnType is ParameterizedType) { "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>" }
        val responseType = Utils.getParameterUpperBound(0, returnType)
        return object : CallAdapter<Any?, Call<*>> {
            override fun responseType(): Type {
                return responseType
            }

            override fun adapt(call: Call<Any?>): Call<Any?> {
                return DelegateCall(call, serverError, gsonParseError,successResponseInfo)
            }
        }
    }

    internal class DelegateCall<T>(
        val delegate: Call<T>, var serverError: ServerError? = null,
        var gsonParseError: GsonParseError? = null,
        var successResponseInfo: ((
            requestUrl: String,  //请求url
            requestInfo: String?   //请求信息
            , responseInfo: String?  //响应信息

        ) -> Unit)? = null
    ) : Call<T> by delegate {
        private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
            val contentEncoding = headers["Content-Encoding"]
            return (contentEncoding != null && !contentEncoding.equals("identity", ignoreCase = true)
                    && !contentEncoding.equals("gzip", ignoreCase = true))
        }
        fun isPlaintext(buffer: Buffer): Boolean {
            return try {
                val prefix = Buffer()
                val byteCount = if (buffer.size() < 64) buffer.size() else 64
                buffer.copyTo(prefix, 0, byteCount)
                for (i in 0..15) {
                    if (prefix.exhausted()) {
                        break
                    }
                    val codePoint = prefix.readUtf8CodePoint()
                    if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                        return false
                    }
                }
                true
            } catch (e: EOFException) {
                false // Truncated UTF-8 sequence.
            }
        }


        override fun enqueue(callback: Callback<T>) {
            Log.d(TAG, "enqueue11111: ")
            delegate.enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    //获取请求信息
                    try {
                        val request = call.request()
                        val requestUrl = request.url().toString()

                        //响应信息
                        val body = response.body()
                        val gson = Gson()
                        val responseInfo = gson.toJson(body)
                        val requestMessage = getRequestInfo(request)
                        successResponseInfo?.invoke(requestUrl, requestMessage, responseInfo)

                    } catch (e: Exception) {
                        Log.d(TAG, "onResponse: e= $e")
                    }
                    callback.onResponse(call, response)


                }

                override fun onFailure(call: Call<T>, t: Throwable) {
                    callback.onFailure(call, t)
                    val gson = Gson()
                    val request = call.request()
                    val requestStartMessage =
                        RequestInfoBean(
                            request.url().toString(),
                            request.method(),
                            getRequestInfo(request)
                        )

                    //业务逻辑错误
                    if (t is BizException) {
                        serverError?.apply {
                            this.serverError(request.url().toString(),
                                "${t.message}",
                                gson.toJson(requestStartMessage),
                                t.responseMsg
                            )
                        }

                    } else if (t is GsonParseErrorException) {
                        //gson解析异常
                        gsonParseError?.apply {
                            this.parseError(request.url().toString(),
                                "${t.message}",
                                gson.toJson(requestStartMessage),
                                t.responseMsg
                            )
                        }
                    } else if (t is ServerResponseException) {
                        serverError?.apply {
                            this.serverError(request.url().toString(),
                                "${t.message}",
                                gson.toJson(requestStartMessage),
                                t.responseMsg
                            )
                        }
                    }
                }
            })
        }


        //获取请求信息及参数
        private fun getRequestInfo(request: Request): String {
            val requestMessage = StringBuilder("")
            //以下信息copy from  HttpLoggingInterceptor类
            requestMessage.append(
                "--> "
                        + request.method()
                        + ' ' + request.url()
            ).append("\n")
            val requestBody = request.body()
            val hasRequestBody = requestBody != null
            if (hasRequestBody) {
                // Request body headers are only present when installed as a network interceptor. Force
                // them to be included (when available) so there values are known.
                if (requestBody?.contentType() != null) {
                    requestMessage.append("Content-Type: " + requestBody.contentType())
                        .append("\n")
                }
                if (requestBody?.contentLength() != -1L) {
                    requestMessage.append("Content-Length: " + requestBody?.contentLength())
                        .append("\n")
                }
            }

            val headers: Headers = request.headers()

            var i = 0
            val count: Int = headers.size()
            while (i < count) {
                val name: String = headers.name(i)
                val value = headers.value(i)
                // Skip headers from the request body as they are explicitly logged above.
                if (!"Content-Type".equals(
                        name,
                        ignoreCase = true
                    ) && !"Content-Length".equals(name, ignoreCase = true)
                ) {

                    requestMessage.append("headName= $name headValue = $value \n")
                }
                i++
            }

            if (!hasRequestBody) {
                requestMessage.append("--> END " + request.method() + "\n")

            } else if (bodyHasUnknownEncoding(request.headers())) {
                requestMessage.append("--> END " + request.method() + " (encoded body omitted)")
            }else {
                val buffer = Buffer()
                requestBody?.writeTo(buffer)
                var charset = Charset.forName("UTF-8")
                val contentType: MediaType? = requestBody?.contentType()
                if (contentType != null) {
                    charset = contentType.charset(Charset.forName("UTF-8"))
                }
                if (isPlaintext(buffer)) {
                    Log.d(TAG, "getRequestInfo: requestMessage = $requestMessage   requestMessage size = ${requestMessage.length} bytes buffer size = ${buffer.size()}")
                    requestMessage.append(buffer.readString(charset))
                    buffer.clear()
                    requestMessage.append(
                        "--> END " + request.method()
                                + " (" + requestBody?.contentLength() + "-byte body)"
                    )
                } else {
                    requestMessage.append(
                        ("--> END " + request.method() + " (binary "
                                + requestBody?.contentLength() + "-byte body omitted)")
                    )
                }
            }
            return requestMessage.toString()
        }
    }

    //请求信息
    class RequestInfoBean(
        var url: String,
        var method: String,
        var requestInfo: String? = null,
        var responseInfo: String? = null
    )

    companion object {
        private const val TAG = "DefaultCallAdapterFacto"
    }




}