package com.goon.mobile

import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageInstaller
import android.net.Uri
import android.os.Build
import android.provider.Settings
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import java.io.File
import java.io.FileOutputStream
import java.net.HttpURLConnection
import java.net.URL

/**
 * In-app APK update flow przez PackageInstaller API:
 *
 *  1. `canRequestInstall()` — sprawdza Android 8+ permission `REQUEST_INSTALL_PACKAGES`.
 *  2. `openInstallPermissionSettings()` — wystawia Settings → "Install unknown apps"
 *     dla naszego pkgu, gdy permission nie nadane (jednorazowy step).
 *  3. `installApk(url)` — pobiera APK do `cacheDir`, otwiera PackageInstaller.Session,
 *     wpisuje bytes, commit z PendingIntent. Android pokazuje TYLKO dialog
 *     "Update X to a newer version? [Cancel] [Install]" — bez "scan" warning.
 *
 * URL musi być HTTPS — Network Security Config (cert SPKI pin dla configured
 * backend domain) jest stosowany do HttpURLConnection automatycznie, więc cert
 * self-signed jest weryfikowany pinem zanim cokolwiek się pobierze. MITM blocked.
 */
class ApkInstallerModule(reactContext: ReactApplicationContext) :
    ReactContextBaseJavaModule(reactContext) {

    override fun getName(): String = "ApkInstaller"

    @ReactMethod
    fun canRequestInstall(promise: Promise) {
        val ctx = reactApplicationContext
        try {
            val ok = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                ctx.packageManager.canRequestPackageInstalls()
            } else {
                true // pre-O: nie ma scoped permission, REQUEST_INSTALL_PACKAGES wystarczy
            }
            promise.resolve(ok)
        } catch (e: Exception) {
            promise.reject("ERR_CHECK", e.message ?: "fail", e)
        }
    }

    @ReactMethod
    fun openInstallPermissionSettings(promise: Promise) {
        try {
            val ctx = reactApplicationContext
            val intent = Intent(
                Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
                Uri.parse("package:${ctx.packageName}"),
            ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            ctx.startActivity(intent)
            promise.resolve(null)
        } catch (e: Exception) {
            promise.reject("ERR_OPEN_SETTINGS", e.message ?: "fail", e)
        }
    }

    @ReactMethod
    fun installApk(url: String, promise: Promise) {
        Thread {
            val ctx = reactApplicationContext
            val cache = File(ctx.cacheDir, "update.apk")
            try {
                if (cache.exists()) cache.delete()

                val conn = URL(url).openConnection() as HttpURLConnection
                conn.connectTimeout = 30_000
                conn.readTimeout = 120_000
                conn.requestMethod = "GET"
                conn.connect()
                if (conn.responseCode !in 200..299) {
                    promise.reject(
                        "ERR_DOWNLOAD",
                        "HTTP ${conn.responseCode} fetching APK",
                    )
                    return@Thread
                }

                conn.inputStream.use { input ->
                    FileOutputStream(cache).use { output ->
                        input.copyTo(output)
                    }
                }

                if (cache.length() == 0L) {
                    promise.reject("ERR_DOWNLOAD", "downloaded APK is empty")
                    return@Thread
                }

                installFromFile(cache, promise)
            } catch (e: Exception) {
                promise.reject("ERR_DOWNLOAD", e.message ?: "download fail", e)
            }
        }.start()
    }

    private fun installFromFile(apk: File, promise: Promise) {
        try {
            val ctx = reactApplicationContext
            val installer = ctx.packageManager.packageInstaller
            val params = PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL
            )
            params.setAppPackageName(ctx.packageName)
            val sessionId = installer.createSession(params)
            val session = installer.openSession(sessionId)

            session.use { s ->
                apk.inputStream().use { input ->
                    s.openWrite("apk", 0, apk.length()).use { output ->
                        input.copyTo(output)
                        s.fsync(output)
                    }
                }

                // BUG-FIX 2026-05-31 ("klikam Install i nic się nie dzieje"):
                // commit() NIE pokazuje od razu dialogu. Android najpierw odsyła
                // STATUS_PENDING_USER_ACTION + EXTRA_INTENT, który MUSIMY ręcznie
                // startActivity() żeby pokazać systemowy dialog "Install update?".
                // Poprzednio target był MainActivity bez obsługi tego statusu →
                // download się udawał, sesja commitowała, ale dialog nigdy nie
                // wyskakiwał. Teraz: getBroadcast → receiver → launch EXTRA_INTENT.
                val action = "com.goon.mobile.APK_INSTALL_STATUS"
                val receiver = object : BroadcastReceiver() {
                    override fun onReceive(c: Context, i: Intent) {
                        val status = i.getIntExtra(
                            PackageInstaller.EXTRA_STATUS,
                            PackageInstaller.STATUS_FAILURE,
                        )
                        if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
                            @Suppress("DEPRECATION")
                            val confirm = i.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
                            confirm?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                            if (confirm != null) c.startActivity(confirm)
                        } else {
                            // terminal (SUCCESS/FAILURE/ABORTED) — sprzątamy receiver.
                            try { c.applicationContext.unregisterReceiver(this) } catch (_: Exception) {}
                        }
                    }
                }
                val filter = IntentFilter(action)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    ctx.applicationContext.registerReceiver(
                        receiver, filter, Context.RECEIVER_NOT_EXPORTED,
                    )
                } else {
                    @Suppress("UnspecifiedRegisterReceiverFlag")
                    ctx.applicationContext.registerReceiver(receiver, filter)
                }

                val intent = Intent(action).setPackage(ctx.packageName)
                val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
                } else {
                    PendingIntent.FLAG_UPDATE_CURRENT
                }
                val pendingIntent = PendingIntent.getBroadcast(ctx, sessionId, intent, flags)
                s.commit(pendingIntent.intentSender)
            }

            // commit() async — receiver odpali systemowy dialog gdy Android poprosi
            // o user-action. JS nie czeka na wynik (to Android UI).
            promise.resolve(null)
        } catch (e: Exception) {
            promise.reject("ERR_INSTALL", e.message ?: "install fail", e)
        }
    }
}
