package com.smplayer.smplayer

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Base64
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
import java.security.KeyStore
import java.security.cert.X509Certificate
import java.util.concurrent.Executors


class AndroidTlsBridge(
    private val context: Context,
    flutterEngine: FlutterEngine,
) {
    companion object {
        private const val CHANNEL_NAME = "smplayer/android_tls"
        private const val CA_BUNDLE_FILE_NAME = "android-trusted-ca-bundle.pem"
    }

    private val mainHandler = Handler(Looper.getMainLooper())
    private val worker = Executors.newSingleThreadExecutor()
    private val methodChannel = MethodChannel(
        flutterEngine.dartExecutor.binaryMessenger,
        CHANNEL_NAME,
    )

    @Volatile
    private var disposed = false

    init {
        methodChannel.setMethodCallHandler { call, result ->
            when (call.method) {
                "exportTrustedCaBundle" -> worker.execute {
                    try {
                        val bundlePath = exportTrustedCaBundle()
                        postResult { result.success(bundlePath) }
                    } catch (exception: Exception) {
                        postResult {
                            result.error(
                                "CA_EXPORT_FAILED",
                                exception.message ?: "Failed to export Android trusted CAs",
                                null,
                            )
                        }
                    }
                }

                else -> result.notImplemented()
            }
        }
    }

    private fun exportTrustedCaBundle(): String {
        val trustStore = KeyStore.getInstance("AndroidCAStore").apply {
            load(null)
        }
        val encodedCertificates = linkedSetOf<String>()
        val aliases = trustStore.aliases().toList().sorted()

        for (alias in aliases) {
            val certificate = trustStore.getCertificate(alias) as? X509Certificate
                ?: continue
            encodedCertificates += Base64.encodeToString(
                certificate.encoded,
                Base64.NO_WRAP,
            )
        }

        check(encodedCertificates.isNotEmpty()) {
            "AndroidCAStore contained no X.509 trust anchors"
        }

        val outputFile = File(context.cacheDir, CA_BUNDLE_FILE_NAME)
        val temporaryFile = File(context.cacheDir, "$CA_BUNDLE_FILE_NAME.tmp")
        temporaryFile.bufferedWriter(Charsets.US_ASCII).use { writer ->
            for (encodedCertificate in encodedCertificates) {
                writer.appendLine("-----BEGIN CERTIFICATE-----")
                encodedCertificate.chunked(64).forEach(writer::appendLine)
                writer.appendLine("-----END CERTIFICATE-----")
            }
        }

        if (outputFile.exists() && !outputFile.delete()) {
            throw IllegalStateException("Could not replace cached CA bundle")
        }
        if (!temporaryFile.renameTo(outputFile)) {
            temporaryFile.copyTo(outputFile, overwrite = true)
            temporaryFile.delete()
        }
        return outputFile.absolutePath
    }

    private fun postResult(callback: () -> Unit) {
        if (disposed) return
        mainHandler.post {
            if (!disposed) callback()
        }
    }

    fun dispose() {
        disposed = true
        methodChannel.setMethodCallHandler(null)
        worker.shutdownNow()
    }
}
