package de.patani.kettlebelltracker.viewmodels import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.patani.kettlebelltracker.data.local.TrainingSessionDao import de.patani.kettlebelltracker.data.datastore.SettingsDataStore import de.patani.kettlebelltracker.repositories.ApiRepository import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import java.util.Date import kotlin.math.min data class TrainingState( val isTrainingRunning: Boolean = false, val remainingSeconds: Int = 0, val initialDurationSeconds: Int = 0, val setsDone: Int = 0, val goalSets: Int = 5, val repsPerSet: Int = 5, val progress: Float = 0.0f, val currentProgram: String = "clean_1.0", val currentBlockDay: Int = 1, val currentReps: Int = 5, val totalTrainingDays: Int = 0 ) class TrainingViewModel( private val dao: TrainingSessionDao, private val settingsDataStore: SettingsDataStore, private val apiRepository: ApiRepository, private val appUUID: String ) : ViewModel() { private val _trainingState = MutableStateFlow(TrainingState()) val trainingState = _trainingState.asStateFlow() private var timerJob: Job? = null init { // Load initial state based on past trainings viewModelScope.launch { val trainingCount = dao.getTrainingCount().first() val initialState = calculateStateByDayCount(trainingCount) _trainingState.update { it.copy( totalTrainingDays = trainingCount, currentProgram = initialState.program, currentBlockDay = initialState.blockDay, currentReps = initialState.reps, repsPerSet = initialState.reps ) } } } fun startTraining() { if (_trainingState.value.isTrainingRunning) return viewModelScope.launch { val settings = settingsDataStore.settingsFlow.first() val durationSeconds = settings.trainingTimeMinutes * 60 _trainingState.update { it.copy( isTrainingRunning = true, initialDurationSeconds = durationSeconds, remainingSeconds = durationSeconds, goalSets = settings.goalSets, setsDone = 0, progress = 0.0f ) } startTimer() } } private fun startTimer() { timerJob?.cancel() timerJob = viewModelScope.launch { while (_trainingState.value.remainingSeconds > 0 && _trainingState.value.isTrainingRunning) { delay(1000) _trainingState.update { it.copy(remainingSeconds = it.remainingSeconds - 1) } } if (_trainingState.value.isTrainingRunning) { finishTraining() } } } fun completeSet() { if (!_trainingState.value.isTrainingRunning) return _trainingState.update { val newSetsDone = it.setsDone + 1 val newProgress = if (it.goalSets > 0) { min(newSetsDone.toFloat() / it.goalSets.toFloat(), 1.0f) } else 0.0f it.copy(setsDone = newSetsDone, progress = newProgress) } } fun finishTraining() { timerJob?.cancel() if (!_trainingState.value.isTrainingRunning) return viewModelScope.launch { val state = _trainingState.value val settings = settingsDataStore.settingsFlow.first() val session = de.patani.kettlebelltracker.data.local.TrainingSession( date = Date(), sets = state.setsDone, weightLeft = settings.weightLeft, weightRight = settings.weightRight, repsPerSet = state.repsPerSet, duration = (state.initialDurationSeconds - state.remainingSeconds).toLong(), program = state.currentProgram, blockDay = state.currentBlockDay ) dao.insert(session) apiRepository.sendTrainingData(session, appUUID) resetTraining() } } private suspend fun resetTraining() { val trainingCount = dao.getTrainingCount().first() val nextState = calculateStateByDayCount(trainingCount) _trainingState.value = TrainingState( totalTrainingDays = trainingCount, currentProgram = nextState.program, currentBlockDay = nextState.blockDay, currentReps = nextState.reps, repsPerSet = nextState.reps ) } private fun calculateStateByDayCount(totalDays: Int): ProgramState { if (totalDays <= 0) { return ProgramState("clean_1.0", 1, 5) } val cycleIndex = (totalDays / 12) % 6 val programs = listOf("clean_1.0", "snatch_1.0", "clean_1.1", "snatch_1.1", "clean_1.2", "snatch_1.2") val program = programs[cycleIndex] val blockDay = (totalDays % 3) + 1 val repsMap = mapOf( "clean_1.0" to listOf(5, 6, 4), "clean_1.1" to listOf(6, 8, 7), "clean_1.2" to listOf(7, 9, 8), "snatch_1.0" to listOf(5, 6, 4), "snatch_1.1" to listOf(6, 8, 7), "snatch_1.2" to listOf(7, 9, 8) ) val reps = repsMap[program]?.getOrNull(blockDay - 1) ?: 5 return ProgramState(program, blockDay, reps) } data class ProgramState(val program: String, val blockDay: Int, val reps: Int) }