package com.adealink.weparty.webview.loader

import android.os.Build
import android.os.SystemClock
import android.webkit.WebResourceResponse
import com.adealink.frame.log.Log
import com.adealink.frame.util.getHost
import com.adealink.weparty.webview.constant.TAG_WEB_VIEW_RES_LOAD
import com.adealink.weparty.webview.jsbridge.manager.JSBridgeManager
import com.adealink.weparty.webview.stat.WebViewStatEvent
import com.adealink.weparty.webview.util.getCharsetFromHeaders
import com.adealink.weparty.webview.util.getMimeTypeFromHeader
import com.adealink.weparty.webview.util.getMimeTypeFromUrl
import okhttp3.Cache
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
import java.net.HttpURLConnection.HTTP_NOT_MODIFIED
import java.util.concurrent.TimeUnit

class OkHttpResourceLoader(val config: IWebResourceConfig) : IResourceLoader {

    private val okHttpClient by lazy {
        val builder = OkHttpClient.Builder()
            .connectTimeout(20_000L, TimeUnit.MILLISECONDS)
            .readTimeout(config.timeoutMS, TimeUnit.MILLISECONDS)
            .writeTimeout(0, TimeUnit.MILLISECONDS)
        val cachePath = config.cachePath
        if (cachePath != null) {
            builder.cache(Cache(File(cachePath), config.cacheSizeKB * 1024))
        }
        builder.build()
    }

    override fun load(request: ResourceRequest): WebResourceResponse? {
        var response: WebResourceResponse? = null
        val method = request.method
        if (method == "POST") {
            return response
        }

        val url = request.url
        if (!JSBridgeManager.instance.isWhite(url)) {
            return response
        }

        try {
            val startTime = SystemClock.elapsedRealtime()
            val builder = Headers.Builder()
            request.headers.forEach { (key, value) ->
                builder.add(key, value)
            }
            val httpRequest = Request.Builder()
                .url(url)
                .method(method, null)
                .headers(builder.build())
                .build()
            val res = okHttpClient.newCall(httpRequest).execute()
            val body = res.body
            var mimeType = body?.contentType()?.run {
                "${type}/${subtype}"
            }
            if (mimeType == null) {
                mimeType = getMimeTypeFromHeader(res.headers)
            }
            if (mimeType == null) {
                mimeType = getMimeTypeFromUrl(url)
            }
            var responseHeaders = res.headers.toMap()
            response = WebResourceResponse(
                mimeType,
                getCharsetFromHeaders(res.headers),
                res.body?.byteStream()
            ).apply {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    this.responseHeaders = responseHeaders
                }
            }
            var requestHeaders = res.request.headers
            var protocol = res.protocol
            var resCode = res.code
            var cacheType = WebCacheType.NO_LOCAL_CACHE
            val cacheResponse = res.cacheResponse
            val networkResponse = res.networkResponse
            if (cacheResponse != null && networkResponse == null) {
                requestHeaders = cacheResponse.request.headers
                protocol = cacheResponse.protocol
                resCode = cacheResponse.code
                cacheType = WebCacheType.DIRECT_LOCAL_CACHE
                responseHeaders = cacheResponse.headers.toMap()
            }
            if (networkResponse != null) {
                requestHeaders = networkResponse.request.headers
                protocol = networkResponse.protocol
                resCode = networkResponse.code
                responseHeaders = networkResponse.headers.toMap()
                if (networkResponse.code == HTTP_NOT_MODIFIED) {
                    cacheType = WebCacheType.REDIRECT_LOCAL_CACHE
                }
            }
            val duration = SystemClock.elapsedRealtime() - startTime
            Log.d(
                TAG_WEB_VIEW_RES_LOAD,
                "okhttp load, url:$url, method:${method}, redirect:${request.isRedirect}, forMainFrame:${request.isForMainFrame}, requestHeaders:${requestHeaders.toMap()}, protocol:${res.protocol}, responseCode:${resCode}, cacheType:${cacheType.cache}, responseHeaders:${responseHeaders}, duration:$duration"
            )
            if (config.reportHosts.contains(getHost(url))) {
                report(
                    LoaderType.OKHTTP,
                    url,
                    method,
                    cacheType,
                    request.isRedirect,
                    resCode,
                    protocol.toString(),
                    requestHeaders.toMap(),
                    responseHeaders,
                    duration
                )
            }
        } catch (e: Throwable) {
            Log.e(TAG_WEB_VIEW_RES_LOAD, "okhttp load, url:$url, e:$e")
            WebViewStatEvent(WebViewStatEvent.Action.LOAD)
                .apply {
                    uri to url
                    type to LoaderType.OKHTTP.type
                    this.method to method
                    this.error to e.localizedMessage
                    addEventMap(filterHeaders(request.headers))
                }
                .send()
        }
        return response
    }

}