/*
 * 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.auth.DeviceNameProvider
import com.runicgateway.app.core.result.ApiResult
import com.runicgateway.app.data.api.dto.TrustedDeviceDto
import com.runicgateway.app.data.repository.AccountRepository
import com.runicgateway.app.data.repository.AccountRepository.TrustOutcome
import com.runicgateway.app.data.repository.AuthRepository
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 the Trusted Devices screen (TRUSTED_DEVICES_MFA.md): list the devices
 * allowed to skip the TOTP step, trust the current one (persisting the returned
 * token via [AuthRepository]), revoke one, and untrust all. The trust action folds
 * the `409` cap into a first-class [Feedback] telling the user to revoke one first.
 */
@HiltViewModel
class TrustedDevicesViewModel @Inject constructor(
    private val accountRepository: AccountRepository,
    private val authRepository: AuthRepository,
    private val sessionManager: com.runicgateway.app.core.auth.SessionManager,
    private val deviceNameProvider: DeviceNameProvider,
) : ViewModel() {

    /** A one-shot result banner shown above the list. */
    data class Feedback(val ok: Boolean, @param:StringRes val messageRes: Int)

    data class State(
        val devices: UiState<List<TrustedDeviceDto>> = UiState.Loading,
        val busy: Boolean = false,
        val feedback: Feedback? = null,
    )

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

    init {
        load()
    }

    fun load() {
        _state.update { it.copy(devices = UiState.Loading) }
        viewModelScope.launch {
            _state.update { it.copy(devices = accountRepository.trustedDevices().toUiState()) }
        }
    }

    /** Trust the current device; persist the returned token so future logins skip 2FA. */
    fun trustThisDevice() {
        if (_state.value.busy) return
        _state.update { it.copy(busy = true, feedback = null) }
        viewModelScope.launch {
            when (val outcome = accountRepository.trustThisDevice(deviceNameProvider.deviceName())) {
                is TrustOutcome.Trusted -> {
                    // Bind the fresh token to the signed-in username (mirrors the login path).
                    val username = sessionManager.state.value.let {
                        (it as? com.runicgateway.app.core.auth.Session.SignedIn)?.user?.username
                    }
                    if (outcome.trustToken != null && username != null) {
                        authRepository.saveTrustToken(username, outcome.trustToken)
                    }
                    finish(true, R.string.trusted_devices_trusted)
                    reload()
                }
                is TrustOutcome.LimitReached -> finish(false, R.string.trusted_devices_limit)
                TrustOutcome.NetworkError -> finish(false, R.string.error_network)
                TrustOutcome.ServerError -> finish(false, R.string.trusted_devices_error)
            }
        }
    }

    fun revoke(id: Long) {
        if (_state.value.busy) return
        _state.update { it.copy(busy = true, feedback = null) }
        viewModelScope.launch {
            when (accountRepository.revokeTrustedDevice(id)) {
                is ApiResult.Ok -> {
                    finish(true, R.string.trusted_devices_revoked)
                    reload()
                }
                else -> finish(false, R.string.trusted_devices_error)
            }
        }
    }

    fun revokeAll() {
        if (_state.value.busy) return
        _state.update { it.copy(busy = true, feedback = null) }
        viewModelScope.launch {
            when (accountRepository.revokeAllTrustedDevices()) {
                is ApiResult.Ok -> {
                    // Every device is untrusted now, including this one — drop the local token.
                    authRepository.clearTrustToken()
                    finish(true, R.string.trusted_devices_revoked_all)
                    reload()
                }
                else -> finish(false, R.string.trusted_devices_error)
            }
        }
    }

    fun clearFeedback() = _state.update { it.copy(feedback = null) }

    private suspend fun reload() {
        _state.update { it.copy(devices = accountRepository.trustedDevices().toUiState()) }
    }

    private fun finish(ok: Boolean, @StringRes messageRes: Int) =
        _state.update { it.copy(busy = false, feedback = Feedback(ok, messageRes)) }
}
