
package com.ypsx.yppos.http

import com.elvishew.xlog.XLog
import com.ypsx.base.util.dXLog
import com.ypsx.base.util.eXLog
import okhttp3.*
import okhttp3.internal.http.HttpHeaders
import okio.Buffer
import java.io.EOFException
import java.io.IOException
import java.nio.charset.Charset
import java.text.MessageFormat
import java.util.concurrent.TimeUnit


class HttpLoggingInterceptor : Interceptor {
    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        try {
            logRequest(request)
        } catch (e: Exception) {
            XLog.e("", e)
        }

        // log resposne
        val startNs = System.nanoTime()
        val response = chain.proceed(request)
        try {
            logResponse(request, response, startNs)
        } catch (e: Exception) {
            e.eXLog()
        }
        return response
    }

    @Throws(Exception::class)
    private fun logResponse(request: Request, response: Response, startNs: Long) {
        val logBody = true
        val sb = StringBuffer()
        val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
        val responseBody = response.body()
        val contentLength = responseBody!!.contentLength()
        sb.append(
            "<-- " + response.code() + ' ' + response.request().url() + " (" + tookMs + "ms" + ')'
        ).append("\n")
        val headers = response.headers()
//        sb.append(
//            MessageFormat.format(
//                "{0}={1}\n",
//                HeadInterceptor.HEADER_REQUEST_ID,
//                request.header(HeadInterceptor.HEADER_REQUEST_ID)
//            )
//        )
        var i = 0
        val count = headers.size()
        while (i < count) {
            sb.append(headers.name(i) + ": " + headers.value(i)).append("\n")
            i++
        }
        if (!logBody || !HttpHeaders.hasBody(response)) {
            sb.append("<-- END HTTP").append("\n")
        } else if (bodyEncoded(response.headers())) {
            sb.append("<-- END HTTP (encoded body omitted)").append("\n")
        } else {
            val source = responseBody.source()
            source.request(Long.MAX_VALUE) // Buffer the entire body.
            val buffer = source.buffer()
            var charset = UTF8
            val contentType = responseBody.contentType()
            if (contentType != null) {
                charset = contentType.charset(UTF8)
            }
            if (!isPlaintext(contentType, buffer)) {
                XLog.d(sb.toString())
            } else if (contentLength != 0L) {
                sb.append(buffer.clone().readString(charset))
            }
        }
        sb.toString().dXLog()
    }

    @Throws(Exception::class)
    private fun logRequest(request: Request) {
        val logBody = true
        val requestBody = request.body()
        val hasRequestBody = requestBody != null
        val sb = StringBuffer()
        sb.append("--> " + request.method() + ' ' + request.url()).append("\n")
        val headers = request.headers()
        var i = 0
        val count = headers.size()
        while (i < count) {
            val name = headers.name(i)
            sb.append(name + ": " + headers.value(i)).append("\n")
            i++
        }
        if (!logBody || !hasRequestBody) {
            sb.append("--> END " + request.method()).append("\n")
        } else if (bodyEncoded(request.headers())) {
            sb.append("--> END " + request.method() + " (encoded body omitted)").append("\n")
        } else {
            val buffer = Buffer()
            requestBody!!.writeTo(buffer)
            var charset = UTF8
            val contentType = requestBody.contentType()
            if (contentType != null) {
                charset = contentType.charset(UTF8)
            }
            if (isPlaintext(contentType, buffer)) {
                sb.append(buffer.readString(charset)).append("\n")
            } else {
                sb.append(
                    "--> END " + request.method() + " (binary "
                            + requestBody.contentLength() + "-byte body omitted)"
                ).append("\n")
            }
        }
        sb.toString().dXLog()
    }

    private fun bodyEncoded(headers: Headers): Boolean {
        val contentEncoding = headers["Content-Encoding"]
        return contentEncoding != null && !contentEncoding.equals("identity", ignoreCase = true)
    }

    companion object {
        private val UTF8 = Charset.forName("UTF-8")

        /**
         * Returns true if the body in question probably contains human readable text. Uses a small sample
         * of code points to detect unicode control characters commonly used in binary file signatures.
         */
        private fun isPlaintext(contentType: MediaType?, buffer: Buffer): Boolean {
            if (contentType != null) {
                val contentTypeString = contentType.toString().toLowerCase()
                if (contentTypeString.contains("image") || contentTypeString.contains("multipart")) {
                    return false
                } else if (contentTypeString.contains("json") || contentTypeString.contains("text") || contentTypeString.contains(
                        "html"
                    )
                ) {
                    return true
                }
            }
            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.
            }
        }
    }
}