package com.adealink.weparty.webview

import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.net.http.SslError
import android.os.Build
import android.os.SystemClock
import android.webkit.SslErrorHandler
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.RequiresApi
import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity
import com.adealink.frame.log.Log
import com.adealink.frame.statistics.CommonEventValue
import com.adealink.frame.util.AppUtil
import com.adealink.frame.util.getHost
import com.adealink.frame.util.getUrlPath
import com.adealink.weparty.webview.constant.TAG_WEB_VIEW
import com.adealink.weparty.webview.constant.TAG_WEB_VIEW_LOAD_FLOW
import com.adealink.weparty.webview.loader.ResourceRequest
import com.adealink.weparty.webview.loader.webResourceLoader
import com.adealink.weparty.webview.scheme.WebScheme
import com.adealink.weparty.webview.scheme.WebSchemeHandler
import com.adealink.weparty.webview.stat.WebViewStatEvent
import com.adealink.weparty.webview.util.SSLErrorHandleUtil

/**
 */
open class CommonWebViewClient(val webView: IWebView) : WebViewClient() {

    private var webViewLoadStat: WebViewStatEvent? = null
    private var pageStartTime: Long = 0

    private val schemeUriHandlers = hashMapOf<String, WebSchemeHandler>()

    fun addSchemeHandler(handler: WebSchemeHandler) {
        for (path in handler.paths) {
            if (schemeUriHandlers.containsKey(path)) {
                throw IllegalArgumentException(
                    "Scheme:${path} already register to ${
                        schemeUriHandlers.get(
                            path
                        )
                    }"
                )
            }
            schemeUriHandlers[path] = handler
        }
    }

    fun onPageBack(view: WebView, url: String?) {
        Log.d(TAG_WEB_VIEW_LOAD_FLOW, "page load back, url:$url")
        webViewLoadStat
            ?.apply {
                duration to (SystemClock.elapsedRealtime() - pageStartTime)
                result to CommonEventValue.Result.CANCEL
            }
            ?.send()
        webViewLoadStat = null
    }

    override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
        super.onPageStarted(view, url, favicon)
        Log.d(TAG_WEB_VIEW_LOAD_FLOW, "page load start, url:$url")
        if (webView.webViewCallback?.isHostActive() == true) {
            pageStartTime = SystemClock.elapsedRealtime()
            webViewLoadStat = WebViewStatEvent(WebViewStatEvent.Action.LOAD)
                .apply {
                    page to url
                    result to CommonEventValue.Result.SUCCESS
                    ua to webView.getWebView().settings.userAgentString
                }
            webView.webViewCallback?.onPageStarted(url, favicon)
        }
    }

    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)
        if (webView.webViewCallback?.isHostActive() == true) {
            Log.d(TAG_WEB_VIEW_LOAD_FLOW, "page load finished, url:$url")
            webViewLoadStat
                ?.apply {
                    duration to (SystemClock.elapsedRealtime() - pageStartTime)
                }
                ?.send()
            webViewLoadStat = null
            webView.webViewCallback?.onPageFinished(url)
        }
    }

    private fun isLoadHost(url: String?): Boolean {
        if (url.isNullOrEmpty()) {
            return false
        }

        val loadUrl = webView.getLoadUrl()
        if (loadUrl.isNullOrEmpty()) {
            return false
        }

        val host = getHost(url)
        val originHost = getHost(loadUrl)
        return host == originHost
    }

    @RequiresApi(Build.VERSION_CODES.M)
    override fun onReceivedError(
        view: WebView?,
        request: WebResourceRequest?,
        error: WebResourceError?,
    ) {
        super.onReceivedError(view, request, error)
        val failingUrl = request?.url?.toString()
        val errorCode = error?.errorCode ?: 0
        val description = error?.description?.toString()
        if (webView.webViewCallback?.isHostActive() == true && isLoadHost(failingUrl)) {
            webView.webViewCallback?.onReceivedError(failingUrl, errorCode, description)
        }
        reportError(view?.url ?: "", failingUrl ?: "", errorCode, description ?: "")
        Log.e(
            TAG_WEB_VIEW,
            "onReceivedError, webUrl:${view?.url}, failingUrl:$failingUrl, errorCode:$errorCode, description:$description"
        )
    }

    override fun onReceivedError(
        view: WebView?,
        errorCode: Int,
        description: String?,
        failingUrl: String?,
    ) {
        super.onReceivedError(view, errorCode, description, failingUrl)
        if (webView.webViewCallback?.isHostActive() == true && isLoadHost(failingUrl)) {
            webView.webViewCallback?.onReceivedError(failingUrl, errorCode, description)
        }
        reportError(view?.url ?: "", failingUrl ?: "", errorCode, description ?: "")
        Log.e(
            TAG_WEB_VIEW,
            "onReceivedError, webUrl: ${view?.url}, failingUrl:$failingUrl, errorCode:$errorCode, description:$description"
        )
    }

    override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
        if (webView.webViewCallback?.isHostActive() == true) {
            val activity = AppUtil.currentActivity as? FragmentActivity
            activity?.supportFragmentManager?.let {
                SSLErrorHandleUtil.createSSLErrorHandleDialog(view?.url, onPositive = {
                    handler?.proceed()
                }, onNegative = {
                    super.onReceivedSslError(view, handler, error)
                    webView.webViewCallback?.onReceivedSslError(handler, error)
                    true
                })?.show(it)
            }
        }
        reportError(
            webView.getUrl() ?: "",
            error?.url ?: "",
            -100 /*-100表示ssl error*/,
            error.toString()
        )
        Log.e(
            TAG_WEB_VIEW,
            "onReceivedSslError, url:${webView.getUrl()}, error:$error"
        )
    }

    private fun reportError(webUrl: String, requestUrl: String, code: Int, error: String) {
        Log.d(
            TAG_WEB_VIEW_LOAD_FLOW,
            "page load error, webUrl:$webUrl, requestUrl:${requestUrl}, code:$code, error:$error"
        )
        webViewLoadStat
            ?.apply {
                this.code to code
                this.error to error
                uri to getUrlPath(requestUrl)
                result to CommonEventValue.Result.FAILED
            }
            ?.send()
        WebViewStatEvent(WebViewStatEvent.Action.ERROR)
            .apply {
                uri to getUrlPath(requestUrl)
                this.code to code
                this.error to error
                this.host to getHost(webUrl)
            }
            .send()
    }

    override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
        Log.d(TAG_WEB_VIEW, "shouldOverrideUrlLoading(WebResourceRequest:${request?.url})")
        return super.shouldOverrideUrlLoading(view, request)
    }

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        Log.d(TAG_WEB_VIEW, "shouldOverrideUrlLoading($url)")
        if (url != null && !url.startsWith("http")) {
            val uri = url.toUri()
            if (onWebScheme(uri)) {
                return true
            }
            try {
                val intent = Intent(Intent.ACTION_VIEW)
                intent.flags = (Intent.FLAG_ACTIVITY_NEW_TASK
                        or Intent.FLAG_ACTIVITY_SINGLE_TOP)
                AppUtil.currentActivity?.startActivity(intent)
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return true
        }

        if (url != null && webView.shouldReloadUrl(url)) {
            webView.loadUrl(url)
            return true
        }

        return super.shouldOverrideUrlLoading(view, url)
    }

    private fun onWebScheme(uri: Uri): Boolean {
        if (uri.scheme != WebScheme.Common.SCHEME) {
            return false
        }
        val request = WebSchemeHandler.toUriRequest(uri)
        val handler = schemeUriHandlers[uri.path]
        if (handler == null || !handler.canHandle(request)) {
            return false
        }
        handler.handle(uri)
        return true
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun shouldInterceptRequest(
        view: WebView,
        request: WebResourceRequest
    ): WebResourceResponse? {
        val isRedirect = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            request.isRedirect
        } else {
            false
        }
        val response = webResourceLoader.load(
            ResourceRequest(
                request.url.toString(),
                request.method,
                request.requestHeaders,
                request.isForMainFrame,
                isRedirect
            )
        )
        return response ?: super.shouldInterceptRequest(view, request)
    }

}