/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.ui.auth

import androidx.annotation.StringRes
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.runicgateway.app.R
import com.runicgateway.app.core.result.ApiResult
import com.runicgateway.app.data.repository.AccountRepository
import com.runicgateway.app.ui.UiState
import com.runicgateway.app.ui.toUiState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject

/**
 * Drives Account → Recovery Codes (TRUSTED_DEVICES_MFA.md): the remaining-count
 * status and a password-stepped regenerate that surfaces a fresh single-use batch
 * **once** (never persisted). The freshly generated codes live only in memory until
 * the user leaves the screen or dismisses them.
 */
@HiltViewModel
class RecoveryCodesViewModel @Inject constructor(
    private val accountRepository: AccountRepository,
) : ViewModel() {

    data class State(
        /** Remaining unused codes (the status endpoint). */
        val remaining: UiState<Int> = UiState.Loading,
        /** A just-generated batch to show once, or null. Cleared on dismiss/leave. */
        val freshCodes: List<String>? = null,
        val busy: Boolean = false,
        @param:StringRes val error: Int? = null,
    )

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

    init {
        load()
    }

    fun load() {
        _state.update { it.copy(remaining = UiState.Loading) }
        viewModelScope.launch {
            _state.update { it.copy(remaining = accountRepository.recoveryCodesStatus().toUiState().map { s -> s.remaining }) }
        }
    }

    /** Regenerate the codes; [currentPassword] is required for accounts that have one. */
    fun regenerate(currentPassword: String?) {
        if (_state.value.busy) return
        _state.update { it.copy(busy = true, error = null, freshCodes = null) }
        viewModelScope.launch {
            when (val result = accountRepository.generateRecoveryCodes(currentPassword?.takeIf { it.isNotBlank() })) {
                is ApiResult.Ok -> {
                    _state.update { it.copy(busy = false, freshCodes = result.data.recoveryCodes) }
                    // Refresh the remaining count to reflect the new batch.
                    _state.update { it.copy(remaining = accountRepository.recoveryCodesStatus().toUiState().map { s -> s.remaining }) }
                }
                is ApiResult.HttpError ->
                    _state.update { it.copy(busy = false, error = R.string.recovery_codes_error) }
                is ApiResult.NetworkError ->
                    _state.update { it.copy(busy = false, error = R.string.error_network) }
            }
        }
    }

    /** Drop the shown-once batch from memory (user saved them / navigated away). */
    fun dismissFreshCodes() = _state.update { it.copy(freshCodes = null) }
}

/** Map an [UiState] success value (local helper mirroring ApiResult.map). */
private inline fun <T, R> UiState<T>.map(transform: (T) -> R): UiState<R> = when (this) {
    is UiState.Success -> UiState.Success(transform(data))
    is UiState.Loading -> UiState.Loading
    is UiState.Error -> this
}
