package ru.forbion.f7cloud.feature.talk

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.net.http.SslError
import android.os.Build
import android.os.Bundle
import android.view.ViewGroup
import android.webkit.CookieManager
import android.webkit.HttpAuthHandler
import android.webkit.PermissionRequest
import android.webkit.SslErrorHandler
import android.webkit.WebChromeClient
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.FrameLayout
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import okhttp3.OkHttpClient
import okhttp3.Request
import ru.forbion.f7cloud.core.auth.AuthSession
import ru.forbion.f7cloud.core.auth.AuthStore
import ru.forbion.f7cloud.core.designsystem.F7Colors
import ru.forbion.f7cloud.core.designsystem.F7Theme
import ru.forbion.f7cloud.core.network.NetworkFactory
import ru.forbion.f7cloud.core.push.F7IncomingCallQueue
import java.io.FilterInputStream
import java.net.URI

class TalkCallActivity : ComponentActivity() {

    private var webViewRef: WebView? = null
    private var pageReady by mutableStateOf(false)
    private var callWakeLock: F7CallWakeLock? = null
    private var pendingAutoJoinUrl: String? = null
    private var pendingPermissionRequest: PermissionRequest? = null

    private val permissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestMultiplePermissions(),
    ) { grants ->
        val request = pendingPermissionRequest ?: return@registerForActivityResult
        pendingPermissionRequest = null
        if (grants.values.all { it }) {
            request.grant(request.resources)
        } else {
            request.deny()
        }
    }

    @OptIn(ExperimentalMaterial3Api::class)
    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val launch = readLaunch() ?: run {
            finish()
            return
        }
        val callUrl = TalkHelper.getCallUrlForWebViewLoad(launch.url)
        val roomToken = TalkHelper.extractRoomToken(callUrl)
        if (launch.autoJoin) {
            pendingAutoJoinUrl = callUrl
            F7IncomingCallQueue.dismissAndShowNext(
                this,
                roomToken ?: TalkHelper.extractRoomToken(launch.url),
            )
        }
        callWakeLock = F7CallWakeLock(this).also { it.updateForUrl(callUrl) }
        ensureMediaPermissions()

        val httpClient = NetworkFactory.newAuthedClient(
            launch.username,
            launch.password,
            launch.trustAllCerts,
        )
        val authHosts = buildAuthHosts(launch, callUrl)

        setContent {
            F7Theme {
                Scaffold(
                    topBar = {
                        TopAppBar(
                            title = { Text(launch.title) },
                            navigationIcon = {
                                IconButton(onClick = { finish() }) {
                                    Text("←", style = MaterialTheme.typography.titleLarge)
                                }
                            },
                            colors = TopAppBarDefaults.topAppBarColors(
                                containerColor = F7Colors.Surface,
                                titleContentColor = F7Colors.TextPrimary,
                            ),
                        )
                    },
                    containerColor = F7Colors.Background,
                ) { padding ->
                    Box(
                        modifier = Modifier
                            .fillMaxSize()
                            .padding(padding)
                            .background(F7Colors.Surface),
                    ) {
                        TalkCallWebView(
                            launch = launch,
                            callUrl = callUrl,
                            roomToken = roomToken,
                            autoJoin = launch.autoJoin,
                            httpClient = httpClient,
                            authHosts = authHosts,
                            onPageReady = { pageReady = true },
                            onUrlChanged = { url ->
                                callWakeLock?.updateForUrl(url)
                            },
                        )
                        if (!pageReady) {
                            Box(
                                modifier = Modifier.fillMaxSize(),
                                contentAlignment = Alignment.Center,
                            ) {
                                CircularProgressIndicator(color = F7Colors.Primary)
                            }
                        }
                    }
                }
            }
        }
    }

    private fun readLaunch(): TalkCallLaunch? {
        val url = intent.getStringExtra(EXTRA_URL) ?: return null
        val username = intent.getStringExtra(EXTRA_USERNAME).orEmpty()
        if (url.isBlank() || username.isBlank()) return null
        return TalkCallLaunch(
            url = url,
            title = intent.getStringExtra(EXTRA_TITLE).orEmpty().ifBlank { "Звонок" },
            username = username,
            password = intent.getStringExtra(EXTRA_PASSWORD).orEmpty(),
            serverUrl = intent.getStringExtra(EXTRA_SERVER_URL).orEmpty(),
            trustAllCerts = intent.getBooleanExtra(EXTRA_TRUST_ALL_CERTS, false),
            autoJoin = intent.getBooleanExtra(EXTRA_AUTO_JOIN, false),
        )
    }

    private fun ensureMediaPermissions() {
        val needed = mutableListOf<String>()
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            needed += Manifest.permission.CAMERA
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            needed += Manifest.permission.RECORD_AUDIO
        }
        if (needed.isNotEmpty()) {
            permissionLauncher.launch(needed.toTypedArray())
        }
    }

    private fun handleWebViewPermissionRequest(request: PermissionRequest) {
        val androidPerms = linkedSetOf<String>()
        for (resource in request.resources) {
            when (resource) {
                PermissionRequest.RESOURCE_VIDEO_CAPTURE -> androidPerms += Manifest.permission.CAMERA
                PermissionRequest.RESOURCE_AUDIO_CAPTURE -> androidPerms += Manifest.permission.RECORD_AUDIO
            }
        }
        if (androidPerms.isEmpty()) {
            request.grant(request.resources)
            return
        }
        val needAsk = androidPerms.filter {
            ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
        }
        if (needAsk.isEmpty()) {
            request.grant(request.resources)
            return
        }
        pendingPermissionRequest = request
        permissionLauncher.launch(needAsk.toTypedArray())
    }

    @SuppressLint("SetJavaScriptEnabled")
    @Composable
    private fun TalkCallWebView(
        launch: TalkCallLaunch,
        callUrl: String,
        roomToken: String?,
        autoJoin: Boolean,
        httpClient: OkHttpClient,
        authHosts: Set<String>,
        onPageReady: () -> Unit,
        onUrlChanged: (String?) -> Unit,
    ) {
        val serverPrefix = launch.serverUrl.trimEnd('/')
        val origin = runCatching { URI(launch.serverUrl).scheme + "://" + URI(launch.serverUrl).host }.getOrNull()
            ?: serverPrefix
        val directJoinJs = if (autoJoin) rememberAssetJs("f7_talk_direct_join.js") else null
        AndroidView(
            modifier = Modifier.fillMaxSize(),
            factory = { context ->
                WebView(context).apply {
                    webViewRef = this
                    layoutParams = FrameLayout.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.MATCH_PARENT,
                    )
                    CookieManager.getInstance().setAcceptCookie(true)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        CookieManager.getInstance().setAcceptThirdPartyCookies(this, true)
                    }
                    settings.apply {
                        javaScriptEnabled = true
                        domStorageEnabled = true
                        @Suppress("DEPRECATION")
                        databaseEnabled = true
                        mediaPlaybackRequiresUserGesture = false
                        loadWithOverviewMode = true
                        useWideViewPort = true
                        mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
                        userAgentString = TALK_COMPAT_USER_AGENT
                    }
                    if (autoJoin) {
                        evaluateJavascript(TalkHelper.buildDirectJoinDocumentStartScript(), null)
                    }
                    webChromeClient = object : WebChromeClient() {
                        override fun onPermissionRequest(request: PermissionRequest) {
                            runOnUiThread { handleWebViewPermissionRequest(request) }
                        }
                    }
                    webViewClient = object : WebViewClient() {
                        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                            pageReady = false
                            onUrlChanged(url)
                            if (redirectPendingCallIfNeeded(view, url)) return
                        }

                        override fun onPageFinished(view: WebView?, url: String?) {
                            onUrlChanged(url)
                            val body = buildString {
                                append(TalkHelper.buildPreconnectScript(origin))
                                append('\n')
                                if (autoJoin || pendingAutoJoinUrl != null) {
                                    append(TalkHelper.buildDirectJoinBootstrapScript(roomToken))
                                } else {
                                    append(TalkHelper.buildNormalCallBootstrapScript(roomToken))
                                }
                            }
                            view?.evaluateJavascript(
                                TalkWebBranding.jsAfterBranding(launch.serverUrl, body),
                                null,
                            )
                            if ((autoJoin || pendingAutoJoinUrl != null) && !directJoinJs.isNullOrBlank()) {
                                view?.evaluateJavascript(directJoinJs, null)
                            }
                            onPageReady()
                        }

                        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 (!url.startsWith(serverPrefix, ignoreCase = true)) return null
                            return runCatching {
                                val builder = Request.Builder()
                                    .url(url)
                                    .header("OCS-APIRequest", "true")
                                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 — только debug (см. UnsafeSsl); в release штатная проверка.
                            if (launch.trustAllCerts && BuildConfig.DEBUG) {
                                handler?.proceed()
                            } else {
                                super.onReceivedSslError(view, handler, error)
                            }
                        }
                    }
                    loadUrl(callUrl)
                }
            },
            onRelease = { webViewRef = null },
        )
    }

    private fun redirectPendingCallIfNeeded(view: WebView?, loadingUrl: String?): Boolean {
        val pending = pendingAutoJoinUrl ?: return false
        if (loadingUrl.isNullOrBlank()) return false
        if (TalkHelper.isCallRoomUrl(loadingUrl)) return false
        if (!TalkHelper.isPortalHomeUrl(loadingUrl)) return false
        view?.stopLoading()
        view?.loadUrl(pending)
        return true
    }

    private fun rememberAssetJs(name: String): String? {
        return runCatching {
            assets.open(name).bufferedReader().use { it.readText() }
        }.getOrNull()
    }

    override fun onDestroy() {
        callWakeLock?.releaseAll()
        callWakeLock = null
        webViewRef?.destroy()
        webViewRef = null
        super.onDestroy()
    }

    companion object {
        private const val EXTRA_URL = "url"
        private const val EXTRA_TITLE = "title"
        private const val EXTRA_USERNAME = "username"
        private const val EXTRA_PASSWORD = "password"
        private const val EXTRA_SERVER_URL = "server_url"
        private const val EXTRA_TRUST_ALL_CERTS = "trust_all_certs"
        private const val EXTRA_AUTO_JOIN = "auto_join"

        private const val TALK_COMPAT_USER_AGENT =
            "Mozilla/5.0 (Linux; Android 13; Mobile) AppleWebKit/537.36 (KHTML, like Gecko) " +
                "Chrome/131.0.0.0 Mobile Safari/537.36"

        fun intent(context: Context, launch: TalkCallLaunch): Intent =
            Intent(context, TalkCallActivity::class.java).apply {
                putExtra(EXTRA_URL, launch.url)
                putExtra(EXTRA_TITLE, launch.title)
                putExtra(EXTRA_USERNAME, launch.username)
                putExtra(EXTRA_PASSWORD, launch.password)
                putExtra(EXTRA_SERVER_URL, launch.serverUrl)
                putExtra(EXTRA_TRUST_ALL_CERTS, launch.trustAllCerts)
                putExtra(EXTRA_AUTO_JOIN, launch.autoJoin)
            }

        fun launch(context: Context, session: AuthSession, roomToken: String) {
            val url = TalkHelper.buildCallUrl(session.serverUrl, roomToken)
            context.startActivity(
                intent(
                    context,
                    TalkCallLaunch(
                        url = url,
                        title = "Звонок",
                        username = session.username,
                        password = session.appPassword,
                        serverUrl = session.serverUrl,
                        trustAllCerts = session.trustAllCerts,
                        autoJoin = false,
                    ),
                ),
            )
        }

        fun launchFromAcceptUrl(context: Context, acceptUrl: String) {
            val session = AuthStore(context).load() ?: return
            val callUrl = TalkHelper.getCallUrlForWebViewLoad(acceptUrl)
            context.startActivity(
                intent(
                    context,
                    TalkCallLaunch(
                        url = callUrl,
                        title = "Входящий звонок",
                        username = session.username,
                        password = session.appPassword,
                        serverUrl = session.serverUrl,
                        trustAllCerts = session.trustAllCerts,
                        autoJoin = true,
                    ),
                ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
            )
        }

        private fun buildAuthHosts(launch: TalkCallLaunch, callUrl: String): Set<String> {
            val hosts = mutableSetOf<String>()
            runCatching { URI(launch.url).host }.getOrNull()?.let { hosts += it }
            runCatching { URI(callUrl).host }.getOrNull()?.let { hosts += it }
            runCatching { URI(launch.serverUrl).host }.getOrNull()?.let { hosts += it }
            return hosts
        }
    }
}

data class TalkCallLaunch(
    val url: String,
    val title: String,
    val username: String,
    val password: String,
    val serverUrl: String,
    val trustAllCerts: Boolean,
    val autoJoin: Boolean = false,
)
