package ru.forbion.f7cloud.mobile

import android.graphics.Bitmap
import android.net.http.SslError
import android.webkit.HttpAuthHandler
import android.webkit.SslErrorHandler
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import okhttp3.OkHttpClient
import okhttp3.Request
import ru.forbion.f7cloud.feature.files.OfficeEditorLaunch
import java.io.FilterInputStream
import java.net.URI

internal class OfficeWebViewClient(
    private val launch: OfficeEditorLaunch,
    private val httpClient: OkHttpClient,
    private val authHosts: Set<String>,
    private val interceptPrefixes: List<String>,
    private val onMainFrameError: (String) -> Unit,
    private val onPageReady: (WebView?) -> Unit,
) : WebViewClient() {

    override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
        view?.evaluateJavascript(OfficeEditorActivity.HIDE_F7CLOUD_CHROME_JS, null)
    }

    override fun onPageFinished(view: WebView?, url: String?) {
        view?.evaluateJavascript(OfficeEditorActivity.HIDE_F7CLOUD_CHROME_JS, null)
        onPageReady(view)
    }

    override fun onReceivedHttpAuthRequest(
        view: WebView?,
        handler: HttpAuthHandler?,
        host: String?,
        realm: String?,
    ) {
        if (host != null && authHosts.any { host.equals(it, ignoreCase = true) }) {
            handler?.proceed(launch.username, launch.password)
        } else {
            super.onReceivedHttpAuthRequest(view, handler, host, realm)
        }
    }

    override fun shouldInterceptRequest(
        view: WebView?,
        request: WebResourceRequest,
    ): WebResourceResponse? {
        val url = request.url?.toString() ?: return null
        if (!shouldIntercept(url)) return null
        return runCatching {
            val builder = Request.Builder().url(url)
            val method = request.method.uppercase()
            when (method) {
                "GET", "HEAD" -> builder.method(method, null)
                else -> builder.method(method, null)
            }
            request.requestHeaders.forEach { (k, v) ->
                if (!k.equals("Authorization", ignoreCase = true)) {
                    builder.header(k, v)
                }
            }
            val response = httpClient.newCall(builder.build()).execute()
            if (!response.isSuccessful || response.body == null) {
                response.close()
                return null
            }
            val body = response.body!!
            val stream = object : FilterInputStream(body.byteStream()) {
                override fun close() {
                    super.close()
                    response.close()
                }
            }
            WebResourceResponse(
                body.contentType()?.let { "${it.type}/${it.subtype}" },
                body.contentType()?.charset()?.name() ?: "utf-8",
                stream,
            )
        }.getOrNull()
    }

    override fun onReceivedSslError(
        view: WebView?,
        handler: SslErrorHandler?,
        error: SslError?,
    ) {
        // trust-all TLS в WebView — только debug (см. UnsafeSsl); в release всегда штатная проверка.
        if (launch.trustAllCerts && BuildConfig.DEBUG) {
            handler?.proceed()
        } else {
            super.onReceivedSslError(view, handler, error)
        }
    }

    override fun onReceivedError(
        view: WebView?,
        request: WebResourceRequest,
        error: android.webkit.WebResourceError?,
    ) {
        if (request.isForMainFrame) {
            val msg = error?.description?.toString().orEmpty().ifBlank { "Ошибка сети" }
            onMainFrameError(msg)
        }
        super.onReceivedError(view, request, error)
    }

    private fun shouldIntercept(url: String): Boolean =
        interceptPrefixes.any { prefix -> url.startsWith(prefix, ignoreCase = true) }

    companion object {
        fun buildAuthHosts(launch: OfficeEditorLaunch): Set<String> {
            val hosts = mutableSetOf<String>()
            runCatching { URI(launch.url).host }.getOrNull()?.let { hosts += it }
            runCatching { URI(launch.serverUrl).host }.getOrNull()?.let { hosts += it }
            if (launch.collaboraBaseUrl.isNotBlank()) {
                runCatching { URI(launch.collaboraBaseUrl).host }.getOrNull()?.let { hosts += it }
            }
            return hosts
        }

        fun buildInterceptPrefixes(launch: OfficeEditorLaunch): List<String> {
            val prefixes = mutableListOf(launch.serverUrl.trimEnd('/'))
            if (launch.collaboraBaseUrl.isNotBlank()) {
                prefixes += launch.collaboraBaseUrl.trimEnd('/')
            }
            return prefixes.distinct()
        }
    }
}
