package com.fabledsword.minstrel.update.data

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton

private const val APK_FILENAME = "minstrel-update.apk"

/**
 * Downloads the server-bundled APK and installs it over ourselves.
 *
 * The download goes through the shared [OkHttpClient] so it inherits
 * the auth cookie + the BaseUrlInterceptor host rewrite (apkUrl is
 * server-relative, e.g. `/api/client/apk`). The APK lands in the
 * cache dir; [SelfUpdateSession] streams it from there into a
 * [android.content.pm.PackageInstaller] session.
 *
 * On Android O+ the user must have granted "install unknown apps"
 * for Minstrel; [canInstall] reports it and [requestInstallPermission]
 * opens the relevant settings screen. That grant is still required with
 * the session API — silent *updates* don't imply silent *permission*.
 */
@Singleton
class ApkInstaller @Inject constructor(
    @ApplicationContext private val context: Context,
    private val okHttpClient: OkHttpClient,
    private val session: SelfUpdateSession,
) {
    suspend fun downloadApk(apkUrl: String): File = withContext(Dispatchers.IO) {
        val request = Request.Builder()
            .url("http://placeholder.invalid$apkUrl")
            .build()
        okHttpClient.newCall(request).execute().use { response ->
            if (!response.isSuccessful) {
                throw IOException("Download failed: HTTP ${response.code}")
            }
            val body = response.body ?: throw IOException("Empty download body")
            val file = File(context.cacheDir, APK_FILENAME)
            body.byteStream().use { input ->
                file.outputStream().use { output -> input.copyTo(output) }
            }
            file
        }
    }

    /** True when the system will accept an install intent without a permission detour. */
    fun canInstall(): Boolean =
        Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
            context.packageManager.canRequestPackageInstalls()

    /**
     * Install [apk] over ourselves, suspending until the platform decides.
     *
     * Note for callers: on a successful silent install this never returns —
     * the process is replaced. Don't treat the absence of a verdict as failure.
     */
    suspend fun install(apk: File): InstallOutcome = session.run(apk)

    /** Open the "install unknown apps" settings page for Minstrel. */
    fun requestInstallPermission() {
        val intent = Intent(
            Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
            Uri.parse("package:${context.packageName}"),
        ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        context.startActivity(intent)
    }
}
