package com.tt.honeyDue.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tt.honeyDue.data.DataManager import com.tt.honeyDue.models.AuthResponse import com.tt.honeyDue.models.LoginRequest import com.tt.honeyDue.models.RegisterRequest import com.tt.honeyDue.models.ResidenceCreateRequest import com.tt.honeyDue.models.TaskCreateRequest import com.tt.honeyDue.models.TaskTemplate import com.tt.honeyDue.models.VerifyEmailRequest import com.tt.honeyDue.network.ApiResult import com.tt.honeyDue.network.APILayer import com.tt.honeyDue.repository.LookupsRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch /** * User's intent during onboarding */ enum class OnboardingIntent { UNKNOWN, START_FRESH, // Creating a new residence JOIN_EXISTING // Joining with a share code } /** * Steps in the onboarding flow */ enum class OnboardingStep { WELCOME, VALUE_PROPS, NAME_RESIDENCE, CREATE_ACCOUNT, VERIFY_EMAIL, JOIN_RESIDENCE, RESIDENCE_LOCATION, FIRST_TASK, SUBSCRIPTION_UPSELL } /** * ViewModel for managing the onboarding flow state */ class OnboardingViewModel : ViewModel() { private val _currentStep = MutableStateFlow(OnboardingStep.WELCOME) val currentStep: StateFlow = _currentStep private val _userIntent = MutableStateFlow(OnboardingIntent.UNKNOWN) val userIntent: StateFlow = _userIntent private val _residenceName = MutableStateFlow("") val residenceName: StateFlow = _residenceName private val _shareCode = MutableStateFlow("") val shareCode: StateFlow = _shareCode // Registration state private val _registerState = MutableStateFlow>(ApiResult.Idle) val registerState: StateFlow> = _registerState // Login state (for returning users) private val _loginState = MutableStateFlow>(ApiResult.Idle) val loginState: StateFlow> = _loginState // Email verification state private val _verifyEmailState = MutableStateFlow>(ApiResult.Idle) val verifyEmailState: StateFlow> = _verifyEmailState // Residence creation state private val _createResidenceState = MutableStateFlow>(ApiResult.Idle) val createResidenceState: StateFlow> = _createResidenceState // Join residence state private val _joinResidenceState = MutableStateFlow>(ApiResult.Idle) val joinResidenceState: StateFlow> = _joinResidenceState // Task creation state private val _createTasksState = MutableStateFlow>(ApiResult.Idle) val createTasksState: StateFlow> = _createTasksState // Regional templates state private val _regionalTemplates = MutableStateFlow>>(ApiResult.Idle) val regionalTemplates: StateFlow>> = _regionalTemplates // ZIP code entered during location step (persisted on residence) private val _postalCode = MutableStateFlow("") val postalCode: StateFlow = _postalCode // Whether onboarding is complete private val _isComplete = MutableStateFlow(false) val isComplete: StateFlow = _isComplete fun setUserIntent(intent: OnboardingIntent) { _userIntent.value = intent } fun setResidenceName(name: String) { _residenceName.value = name } fun setShareCode(code: String) { _shareCode.value = code } /** * Move to the next step in the flow * Flow: Welcome → Features → Name Residence → Create Account → Verify → Tasks → Upsell */ fun nextStep() { _currentStep.value = when (_currentStep.value) { OnboardingStep.WELCOME -> { if (_userIntent.value == OnboardingIntent.JOIN_EXISTING) { OnboardingStep.CREATE_ACCOUNT } else { OnboardingStep.VALUE_PROPS } } OnboardingStep.VALUE_PROPS -> OnboardingStep.NAME_RESIDENCE OnboardingStep.NAME_RESIDENCE -> OnboardingStep.CREATE_ACCOUNT OnboardingStep.CREATE_ACCOUNT -> OnboardingStep.VERIFY_EMAIL OnboardingStep.VERIFY_EMAIL -> { if (_userIntent.value == OnboardingIntent.JOIN_EXISTING) { OnboardingStep.JOIN_RESIDENCE } else { OnboardingStep.RESIDENCE_LOCATION } } OnboardingStep.JOIN_RESIDENCE -> OnboardingStep.SUBSCRIPTION_UPSELL OnboardingStep.RESIDENCE_LOCATION -> OnboardingStep.FIRST_TASK OnboardingStep.FIRST_TASK -> OnboardingStep.SUBSCRIPTION_UPSELL OnboardingStep.SUBSCRIPTION_UPSELL -> { completeOnboarding() OnboardingStep.SUBSCRIPTION_UPSELL } } } /** * Go to a specific step */ fun goToStep(step: OnboardingStep) { _currentStep.value = step } /** * Go back to the previous step */ fun previousStep() { _currentStep.value = when (_currentStep.value) { OnboardingStep.VALUE_PROPS -> OnboardingStep.WELCOME OnboardingStep.NAME_RESIDENCE -> OnboardingStep.VALUE_PROPS OnboardingStep.CREATE_ACCOUNT -> { if (_userIntent.value == OnboardingIntent.JOIN_EXISTING) { OnboardingStep.WELCOME } else { OnboardingStep.NAME_RESIDENCE } } OnboardingStep.VERIFY_EMAIL -> OnboardingStep.CREATE_ACCOUNT else -> _currentStep.value } } /** * Skip the current step (for skippable screens) */ fun skipStep() { when (_currentStep.value) { OnboardingStep.VALUE_PROPS, OnboardingStep.JOIN_RESIDENCE, OnboardingStep.RESIDENCE_LOCATION, OnboardingStep.FIRST_TASK -> nextStep() OnboardingStep.SUBSCRIPTION_UPSELL -> completeOnboarding() else -> {} } } /** * Register a new user */ fun register(username: String, email: String, password: String) { viewModelScope.launch { _registerState.value = ApiResult.Loading val result = APILayer.register( RegisterRequest( username = username, email = email, password = password ) ) _registerState.value = when (result) { is ApiResult.Success -> { DataManager.setAuthToken(result.data.token) DataManager.setCurrentUser(result.data.user) LookupsRepository.initialize() ApiResult.Success(result.data) } is ApiResult.Error -> result else -> ApiResult.Error("Unknown error") } } } /** * Login an existing user */ fun login(username: String, password: String) { viewModelScope.launch { _loginState.value = ApiResult.Loading val result = APILayer.login(LoginRequest(username, password)) _loginState.value = when (result) { is ApiResult.Success -> { LookupsRepository.initialize() ApiResult.Success(result.data) } is ApiResult.Error -> result else -> ApiResult.Error("Unknown error") } } } /** * Verify email with 6-digit code */ fun verifyEmail(code: String) { viewModelScope.launch { _verifyEmailState.value = ApiResult.Loading val token = DataManager.authToken.value ?: run { _verifyEmailState.value = ApiResult.Error("Not authenticated") return@launch } val result = APILayer.verifyEmail(token, VerifyEmailRequest(code = code)) _verifyEmailState.value = when (result) { is ApiResult.Success -> ApiResult.Success(Unit) is ApiResult.Error -> result else -> ApiResult.Error("Unknown error") } } } /** * Create the residence with the name from onboarding */ fun createResidence() { viewModelScope.launch { if (_residenceName.value.isBlank()) { _createResidenceState.value = ApiResult.Success(Unit) return@launch } _createResidenceState.value = ApiResult.Loading val result = APILayer.createResidence( ResidenceCreateRequest( name = _residenceName.value, propertyTypeId = null, streetAddress = null, apartmentUnit = null, city = null, stateProvince = null, postalCode = _postalCode.value.takeIf { it.isNotBlank() }, country = null, bedrooms = null, bathrooms = null, squareFootage = null, lotSize = null, yearBuilt = null, description = null, purchaseDate = null, purchasePrice = null, isPrimary = true ) ) _createResidenceState.value = when (result) { is ApiResult.Success -> ApiResult.Success(Unit) is ApiResult.Error -> result else -> ApiResult.Error("Failed to create residence") } } } /** * Join an existing residence with a share code */ fun joinResidence(code: String) { viewModelScope.launch { _joinResidenceState.value = ApiResult.Loading val result = APILayer.joinWithCode(code) _joinResidenceState.value = when (result) { is ApiResult.Success -> ApiResult.Success(Unit) is ApiResult.Error -> result else -> ApiResult.Error("Failed to join residence") } } } /** * Create selected tasks during onboarding */ fun createTasks(taskRequests: List) { viewModelScope.launch { if (taskRequests.isEmpty()) { _createTasksState.value = ApiResult.Success(Unit) return@launch } _createTasksState.value = ApiResult.Loading var successCount = 0 for (request in taskRequests) { val result = APILayer.createTask(request) if (result is ApiResult.Success) { successCount++ } } _createTasksState.value = if (successCount > 0) { ApiResult.Success(Unit) } else { ApiResult.Error("Failed to create tasks") } } } /** * Load regional templates by ZIP code (backend resolves ZIP → state → climate zone). * Also stores the ZIP code for later use when creating the residence. */ fun loadRegionalTemplates(zip: String) { _postalCode.value = zip viewModelScope.launch { _regionalTemplates.value = ApiResult.Loading _regionalTemplates.value = APILayer.getRegionalTemplates(zip = zip) } } /** * Mark onboarding as complete */ fun completeOnboarding() { _isComplete.value = true } /** * Reset all state (useful for testing) */ fun reset() { _currentStep.value = OnboardingStep.WELCOME _userIntent.value = OnboardingIntent.UNKNOWN _residenceName.value = "" _shareCode.value = "" _registerState.value = ApiResult.Idle _loginState.value = ApiResult.Idle _verifyEmailState.value = ApiResult.Idle _createResidenceState.value = ApiResult.Idle _joinResidenceState.value = ApiResult.Idle _createTasksState.value = ApiResult.Idle _regionalTemplates.value = ApiResult.Idle _postalCode.value = "" _isComplete.value = false } fun resetRegisterState() { _registerState.value = ApiResult.Idle } fun resetLoginState() { _loginState.value = ApiResult.Idle } fun resetVerifyEmailState() { _verifyEmailState.value = ApiResult.Idle } }