package dev.privacyllc.period.lock

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Whether this session has been unlocked, and nothing else.
 *
 * ## Why this is not saved anywhere
 *
 * `rememberSaveable` looks like the obvious home for a boolean the UI cares
 * about, and a `SavedStateHandle` looks like the tidy one. Both are wrong here
 * in the same way: saved state survives process death, so an app killed in the
 * background would be restored **already unlocked**, and the single most likely
 * way to meet the lock screen — coming back to an app Android reclaimed hours
 * ago — would be the one path that skipped it.
 *
 * Held in a `@Singleton` instead, so it lives exactly as long as the process
 * and dies with it. A cold start is always locked.
 */
@Singleton
class AppLockController @Inject constructor() {

    private val _unlocked = MutableStateFlow(false)
    val unlocked: StateFlow<Boolean> = _unlocked.asStateFlow()

    /**
     * Set immediately before the biometric prompt appears and cleared in every
     * one of its outcomes.
     *
     * The prompt is a system window, and on some devices it stops the activity.
     * Without this flag the re-lock observer fires while the user is looking at
     * the fingerprint dialog, so a successful scan returns to a locked screen —
     * and tapping the fingerprint button again does the same thing, forever.
     *
     * `@Volatile` because it is written from the prompt callback and read by the
     * lifecycle observer, which are not guaranteed to be the same thread.
     */
    @Volatile
    var authInProgress: Boolean = false

    private val _pendingNotificationAction = MutableStateFlow<String?>(null)

    /**
     * Observed rather than read once, so an action arriving while the app is
     * already open — `onNewIntent`, not `onCreate` — is delivered too.
     */
    val pendingNotificationAction: StateFlow<String?> = _pendingNotificationAction.asStateFlow()

    fun unlock() {
        _unlocked.value = true
    }

    fun lock() {
        _unlocked.value = false
    }

    /**
     * Park a notification action until the user has proved who they are.
     *
     * Tapping "Not yet" on a reminder writes a `NotYetObservation` into the
     * health record. That tap is available to anybody holding the phone, from
     * the lock screen of the phone itself — so with an app lock on, the write
     * has to wait for the unlock, or the lock is decorative for the one action
     * that modifies data.
     */
    fun holdNotificationAction(action: String?) {
        if (action != null) _pendingNotificationAction.value = action
    }

    /** Returns the held action once, and forgets it. Null when there is none. */
    fun takeNotificationAction(): String? =
        _pendingNotificationAction.value.also { _pendingNotificationAction.value = null }
}
