package dev.privacyllc.period.feature.lock

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.privacyllc.period.core.data.CycleRepository
import dev.privacyllc.period.core.datastore.UserPreferencesRepository
import dev.privacyllc.period.core.security.AppLockRepository
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

enum class EraseState { IDLE, ERASING, DONE, FAILED }

/**
 * The only way past a forgotten PIN, and it is destruction rather than recovery.
 *
 * The owner decided there is no recovery path: a backdoor into this app's lock
 * would be used by exactly the person the lock exists to stop. That decision
 * makes something else mandatory — a forgotten PIN must not leave an installed
 * app that can never be opened again. So this exists, and it grants access to
 * nothing.
 *
 * It also grants an attacker nothing they did not already have. Anyone holding
 * the phone can clear this app's storage from Android's own settings, or
 * uninstall it, and with `allowBackup="false"` and every extraction domain
 * excluded there is no restore either way. What this adds is honesty: the user
 * finds out from us, in plain words, rather than by discovering their history is
 * gone.
 *
 * ## The order matters, and the second step is the one that gets forgotten
 *
 * 1. the health records
 * 2. **the Keystore key and the stored verifier**
 * 3. the biometric preference
 *
 * Skip step 2 and the user has erased everything and is *still locked out* — a
 * working lock over an empty database, with no PIN that opens it and nothing
 * left to protect. That is the worst outcome this feature can produce, so a test
 * mutates that call out and requires exactly one red.
 *
 * ## What it deliberately does not touch
 *
 * `resetToDefaults()` is not called. `PrivacyViewModel`'s KDoc argues that
 * somebody exercising a privacy control has not asked to have their notification
 * privacy reset to a default they never chose, and that applies here with more
 * force, not less. `ReminderScheduler.cancel()` is not called either, for the
 * reason recorded there: with no history the worker already decides to say
 * nothing, and cancelling would leave reminders silently off until the user next
 * touched a setting. Onboarding is not reset — `PeriodRoot`'s KDoc settles that
 * somebody who deletes their data has not asked to be onboarded again.
 */
@HiltViewModel
class LockEraseViewModel @Inject constructor(
    private val cycles: CycleRepository,
    private val lock: AppLockRepository,
    private val preferences: UserPreferencesRepository,
) : ViewModel() {

    private val _state = MutableStateFlow(EraseState.IDLE)
    val state: StateFlow<EraseState> = _state.asStateFlow()

    private val handler = CoroutineExceptionHandler { _, _ ->
        _state.value = EraseState.FAILED
    }

    fun eraseEverything() {
        if (_state.value == EraseState.ERASING) return
        _state.value = EraseState.ERASING
        viewModelScope.launch(handler) {
            cycles.deleteAllHealthData()
            // Destroys the Keystore key as well as the stored verifier. Without
            // this line the erase leaves a lock nobody can open.
            lock.clearLock()
            preferences.setBiometricLockEnabled(false)
            _state.value = EraseState.DONE
        }
    }

    fun acknowledge() {
        _state.value = EraseState.IDLE
    }
}
