package com.ponzischeme89.memby.update

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow

/**
 * Where a self-update ends up.
 *
 * The install itself is asynchronous and happens in the system installer, not in Memby, so
 * the only way the app learns that an update failed is this broadcast. Without it the TV
 * says "Opening the installer…" and then never mentions it again — which on a *mandatory*
 * update leaves a blocking screen whose button appears to do nothing, and a viewer whose
 * only remaining idea is to sideload the APK by hand.
 */
object AppInstall {
    private val _messages = MutableSharedFlow<String>(
        replay = 1,
        extraBufferCapacity = 1,
    )

    /** Progress and failure text for whichever screen started the install. */
    val messages: SharedFlow<String> = _messages

    internal fun report(message: String) {
        _messages.tryEmit(message)
    }
}

/**
 * Receives the [PackageInstaller] session result.
 *
 * [PackageInstaller.STATUS_PENDING_USER_ACTION] is the ordinary case and is not a failure:
 * the system hands back an intent for its own confirmation screen and expects the app to
 * launch it. That indirection is the reason this path is used instead of an
 * `ACTION_VIEW` handoff — the confirmation activity arrives as an explicit intent from the
 * system, so it does not depend on a TV build exposing an APK viewer or on package
 * visibility rules.
 */
class InstallResultReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action != ACTION) return

        val status = intent.getIntExtra(
            PackageInstaller.EXTRA_STATUS,
            PackageInstaller.STATUS_FAILURE,
        )
        if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
            val confirm = confirmationIntent(intent)
            if (confirm == null) {
                AppInstall.report(installStatusMessage(PackageInstaller.STATUS_FAILURE, null))
                return
            }
            confirm.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            val launched = runCatching { context.startActivity(confirm) }.isSuccess
            AppInstall.report(
                if (launched) {
                    "Confirm the update when your TV asks."
                } else {
                    "This TV would not open Android's install screen. " +
                        "Reinstall Memby from the install page to get this version."
                },
            )
            return
        }

        AppInstall.report(
            installStatusMessage(
                status,
                intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE),
            ),
        )
    }

    @Suppress("DEPRECATION")
    private fun confirmationIntent(intent: Intent): Intent? =
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
            intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
        } else {
            intent.getParcelableExtra(Intent.EXTRA_INTENT)
        }

    companion object {
        const val ACTION = "com.ponzischeme89.memby.INSTALL_RESULT"
    }
}

/**
 * Turns a [PackageInstaller] status into something worth putting on a television.
 *
 * Pure so it can be tested without an installer session. The system's own
 * `EXTRA_STATUS_MESSAGE` is developer-facing ("INSTALL_FAILED_VERSION_DOWNGRADE"), so it is
 * used only as a last resort, and every case that a viewer can actually act on says what
 * to do rather than what went wrong.
 */
internal fun installStatusMessage(status: Int, systemMessage: String?): String = when (status) {
    PackageInstaller.STATUS_SUCCESS ->
        "Update installed. Memby will restart."
    PackageInstaller.STATUS_FAILURE_ABORTED ->
        "The update was cancelled. Press Update now to try again."
    PackageInstaller.STATUS_FAILURE_STORAGE ->
        "There is not enough free space on this TV to install the update."
    PackageInstaller.STATUS_FAILURE_BLOCKED ->
        "Your TV blocked the install. Allow Memby under Settings → Device Preferences → " +
            "Security & restrictions → Unknown sources, then try again."
    PackageInstaller.STATUS_FAILURE_CONFLICT ->
        "Android refused the update because the installed copy of Memby does not match it. " +
            "Uninstall Memby and install this version from the install page."
    PackageInstaller.STATUS_FAILURE_INCOMPATIBLE ->
        "This update is not compatible with this TV."
    PackageInstaller.STATUS_FAILURE_INVALID ->
        "The downloaded update was rejected as damaged. Try again."
    else -> systemMessage?.takeIf { it.isNotBlank() }?.let { "The update could not be installed ($it)." }
        ?: "The update could not be installed."
}
