# AppTimeoutViewModel.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2023 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */
    package dk.nsp.mingraviditet.ui.main

    import androidx.lifecycle.DefaultLifecycleObserver
    import androidx.lifecycle.LifecycleOwner
    import androidx.lifecycle.ViewModel
    import androidx.lifecycle.viewModelScope
    import dagger.hilt.android.lifecycle.HiltViewModel
    import dk.nsp.mingraviditet.core.data.repository.UserPreferencesRepository
    import dk.nsp.mingraviditet.core.data.util.userAcceptedDataAgreement
    import dk.nsp.mingraviditet.core.shared.authentication.IAuthController
    import kotlinx.coroutines.Job
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.flow.MutableSharedFlow
    import kotlinx.coroutines.flow.MutableStateFlow
    import kotlinx.coroutines.flow.collectLatest
    import kotlinx.coroutines.flow.map
    import kotlinx.coroutines.launch
    import java.time.LocalDateTime
    import java.util.concurrent.TimeUnit
    import javax.inject.Inject

    @HiltViewModel
    class AppTimeoutViewModel @Inject constructor(
        private val preferencesRepository: UserPreferencesRepository,
        private val authController: IAuthController,
    ) : ViewModel(), DefaultLifecycleObserver {

        private var onStopTime = MutableStateFlow<LocalDateTime?>(null)
        private var authenticationTimer: Job? = null
        val logoutEvent = MutableSharedFlow<Unit?>(replay = 0)
        val requestAuthenticationEvent = MutableSharedFlow<Unit?>(replay = 0)

        private val useBiometricsPreference = preferencesRepository.preferences.map {
            it.useBiometricsForAnonymousApp || it.userAcceptedDataAgreement()
        }

        private var userIsAuthenticated = false

        override fun onCreate(owner: LifecycleOwner) {
            super.onCreate(owner)
            viewModelScope.launch {
                preferencesRepository.preferences.collect {
                    if (it.userAcceptedDataAgreement()) {
                        startForegroundTimer()
                    }
                    userIsAuthenticated = it.userAcceptedDataAgreement()
                }
            }
        }

        override fun onResume(owner: LifecycleOwner) {
            super.onResume(owner)

            viewModelScope.launch {
                useBiometricsPreference.collectLatest { useBiometrics ->
                    if (useBiometrics && onStopTime.value?.isBefore(
                            LocalDateTime.now().minusSeconds(TIMEOUT_SECONDS_BACKGROUND),
                        ) == true
                    ) {
                        logoutEvent.emit(Unit)
                        authController.clearSession()
                        startAuthenticationTimerIfAuthenticated()
                    }

                    onStopTime.value = null
                }
            }
        }

        override fun onStop(owner: LifecycleOwner) {
            super.onStop(owner)
            onStopTime.value = LocalDateTime.now()
        }

        private fun startAuthenticationTimerIfAuthenticated() {
            viewModelScope.launch {
                if (userIsAuthenticated) {
                    startForegroundTimer()
                } else {
                    stopForegroundTimer()
                }
            }
        }

        private fun startForegroundTimer() {
            stopForegroundTimer()

            authenticationTimer = viewModelScope.launch {
                delay(TimeUnit.MILLISECONDS.convert(TIMEOUT_SECONDS_AUTHENTICATION, TimeUnit.SECONDS))
                requestAuthenticationEvent.emit(Unit)
            }
        }

        private fun stopForegroundTimer() {
            authenticationTimer?.cancel()
            authenticationTimer = null
        }

        fun resetAuthenticationTimer() {
            startAuthenticationTimerIfAuthenticated()
        }

        companion object {
            private const val TIMEOUT_SECONDS_BACKGROUND = 5 * 60L // 5 minutes
            private const val TIMEOUT_SECONDS_AUTHENTICATION = 20 * 60L // 20 minutes
        }
    }