package com.ponzischeme89.memby.update

import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow

/**
 * The gateway telling *this running build* that it may no longer be used.
 *
 * A destructive update floor deletes the session on the next authenticated request and
 * answers 401; a retired build's sign-in attempt is refused with 426. Both carry
 * [HEADER], and without it the television only learns it has been signed out — so it
 * drew a sign-in form that could never succeed, and the mandatory update screen was up
 * to an hour away on the ordinary check interval. Force-closing the app was the only way
 * through, because a fresh launch checks for updates before it draws anything.
 *
 * Reporting the header instead brings that check forward to the moment of the refusal,
 * so the sequence a viewer sees is signed out, then the update screen.
 *
 * Process-scoped and replayed once: the refusal can arrive during startup, before
 * `AppRoot`'s check loop is waiting on it, and a signal nobody was listening for is
 * exactly the one that must not be lost.
 */
object RequiredUpdateSignal {

    /** Set by the gateway on both the retiring 401 and the refused sign-in. */
    const val HEADER = "X-Memby-Update-Required"

    private val _required = MutableSharedFlow<String>(
        replay = 1,
        extraBufferCapacity = 1,
        onBufferOverflow = BufferOverflow.DROP_OLDEST,
    )

    /** Emits the version the gateway says is required, newest last. */
    val required: SharedFlow<String> = _required.asSharedFlow()

    fun report(version: String) {
        val trimmed = version.trim()
        if (trimmed.isEmpty()) return
        _required.tryEmit(trimmed)
    }

    /**
     * Forgets the replayed refusal, once the gateway has been asked and did not in fact
     * demand an update.
     *
     * Without this the replay outlives the thing it describes. The value is process-scoped
     * and `AppRoot`'s check loop starts with no memory of what it has already handled, so
     * an activity Android recreates — returning from the TV's home screen, a
     * configuration change — replays a refusal that has since been answered, and holds the
     * opening screen through the retry budget before letting the launcher through. A
     * verdict that says this build is fine is exactly the evidence that the refusal is
     * spent.
     */
    fun clear() {
        _required.resetReplayCache()
    }
}
