package de.ladkau.deckinadash.ui.reading

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import de.ladkau.deckinadash.data.ConfigRepository
import de.ladkau.deckinadash.data.ReadingDto
import de.ladkau.deckinadash.data.ReadingRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.time.Instant

sealed interface ReadingUiState {
    data object Loading : ReadingUiState
    data object NotConfigured : ReadingUiState
    data class Content(val reading: ReadingDto) : ReadingUiState
    data object Error : ReadingUiState
}

/**
 * Recomputes today's reading whenever the persisted config changes
 * (including the very first emission) - the Compose analog of
 * watch/src/c/reading_state.c's reading_state_recompute() own doc
 * comment ("safe to call again later... overwrites the previous result
 * in place"), just reactive instead of imperatively re-invoked.
 */
class ReadingViewModel(application: Application) : AndroidViewModel(application) {
    private val configRepository = ConfigRepository(application)
    private val readingRepository = ReadingRepository(application)

    private val _uiState = MutableStateFlow<ReadingUiState>(ReadingUiState.Loading)
    val uiState: StateFlow<ReadingUiState> = _uiState.asStateFlow()

    init {
        viewModelScope.launch {
            configRepository.config.collectLatest { config ->
                if (!config.configured) {
                    _uiState.value = ReadingUiState.NotConfigured
                    return@collectLatest
                }
                _uiState.value = ReadingUiState.Loading
                _uiState.value = try {
                    val reading = withContext(Dispatchers.Default) {
                        readingRepository.compute(config, Instant.now().epochSecond)
                    }
                    ReadingUiState.Content(reading)
                } catch (t: Throwable) {
                    ReadingUiState.Error
                }
            }
        }
    }
}
