Why is my Register button not working in my Android app using Jetpack Compose and MVVM?

Hi everyone,
I’m building an Android app using Jetpack Compose and MVVM architecture with Hilt for dependency injection. On my RegisterScreen, I have a Register button that’s supposed to trigger the registration process via RegisterViewModel.register(...), but **clicking the button does nothing, no transition to the next screen.

Here’s what I have:

:brick: Components Involved

  • RegisterScreen.kt: Composable UI with state handling, text inputs, and button click launching a coroutine.
  • RegisterActivity.kt: Collects the state from RegisterViewModel and navigates to MainActivity on success.
  • RegisterViewModel.kt: Contains logic to validate input, test backend connectivity, and call UserRepository.register(...).

:white_check_mark: Observations

  • UI renders fine and button becomes enabled after filling all fields.
  • Clicking the button triggers the coroutine in the onClick lambda.
  • Logs like "Launching register inside coroutine" and ">>> register function called OUTSIDE coroutine" appear.
  • But registration doesn’t complete and RegisterState doesn’t seem to transition to Success.

:magnifying_glass_tilted_left: Suspected Issue

It seems like the callback onSuccess in UserRepository.register(...) may not be firing, or the ViewModel isn’t correctly updating _registerState, or perhaps the UI isn’t reacting to state changes.

:test_tube: What I’ve Tried

  • Verified role, name, email, and password validation logic — all return valid.
  • Logs confirm that the register() method in RegisterViewModel is being called.
  • Checked backend connectivity and it logs as reachable.
  • Checked that registerState is collected in both RegisterScreen.kt and RegisterActivity.kt.

:red_question_mark: What I Need Help With

Can someone help me identify why the register button doesn’t complete the registration flow or why the state doesn’t update to Success after clicking the button?

Any help debugging the flow or checking what might be going wrong with the coroutine, ViewModel state, or callback logic would be appreciated.

Here is the code:
RegisterScreen.kt:

package com.example.eventmanagement.ui

import android.widget.Toast
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.derivedStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.eventmanagement.viewmodel.RegisterViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RegisterScreen(
    viewModel: RegisterViewModel,
    onRegisterSuccess: () -> Unit,
    onLoginClick: () -> Unit
) {
    val context = LocalContext.current
    val scope = rememberCoroutineScope()
    val snackbarHostState = remember { SnackbarHostState() }
    
    // Collect state from ViewModel
    val registerState by viewModel.registerState.collectAsState()
    val selectedRole by viewModel.selectedRole.collectAsState()
    
    // Use remember to prevent unnecessary recompositions
    val name = remember { mutableStateOf("") }
    val email = remember { mutableStateOf("") }
    val password = remember { mutableStateOf("") }
    val confirmPassword = remember { mutableStateOf("") }
    
    // Show loading state
    val isLoading = remember(registerState) { 
        registerState is RegisterViewModel.RegisterState.Loading 
    }

    // Derive button enabled state from current values
    val buttonEnabled = derivedStateOf {
        !isLoading && selectedRole != null && name.value.trim().isNotEmpty() && 
        email.value.trim().isNotEmpty() && password.value.isNotEmpty() && 
        confirmPassword.value.isNotEmpty()
    }
    
    // Password visibility states
    val passwordVisibility = remember { mutableStateOf(false) }
    val confirmPasswordVisibility = remember { mutableStateOf(false) }
    
    // Handle register state changes
    LaunchedEffect(registerState) {
        when (registerState) {
            is RegisterViewModel.RegisterState.Success -> {
                withContext(Dispatchers.Main.immediate) {
                    Toast.makeText(context, "Registration Successful", Toast.LENGTH_SHORT).show()
                    onRegisterSuccess()
                }
            }
            is RegisterViewModel.RegisterState.Error -> {
                val errorMessage = (registerState as RegisterViewModel.RegisterState.Error).message
                withContext(Dispatchers.Main.immediate) {
                    Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show()
                }
            }
            else -> {}
        }
    }
    
    // Test backend connectivity on first load
    LaunchedEffect(Unit) {
        android.util.Log.d("RegisterScreen", "Testing backend connectivity...")
        // This will be handled by the UserRepository when registration is attempted
    }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Create Account") }
            )
        },
        snackbarHost = { SnackbarHost(snackbarHostState) }
    ) { padding ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(padding)
                .padding(16.dp)
                .verticalScroll(rememberScrollState()),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            Text(
                text = "Register",
                style = MaterialTheme.typography.headlineMedium,
                modifier = Modifier.padding(bottom = 16.dp)
            )

            // Role Selection Section
            Text(
                text = "Are you an owner or client?",
                style = MaterialTheme.typography.titleMedium,
                modifier = Modifier.padding(bottom = 8.dp)
            )

            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                Button(
                    onClick = { viewModel.setRole("Owner") },
                    modifier = Modifier
                        .weight(1f)
                        .height(48.dp),
                    colors = ButtonDefaults.buttonColors(
                        containerColor = if (selectedRole == "Owner") 
                            MaterialTheme.colorScheme.primary 
                        else 
                            MaterialTheme.colorScheme.secondary
                    )
                ) {
                    Text("Owner")
                }

                Button(
                    onClick = { viewModel.setRole("Client") },
                    modifier = Modifier
                        .weight(1f)
                        .height(48.dp),
                    colors = ButtonDefaults.buttonColors(
                        containerColor = if (selectedRole == "Client") 
                            MaterialTheme.colorScheme.primary 
                        else 
                            MaterialTheme.colorScheme.secondary
                    )
                ) {
                    Text("Client")
                }
            }

            Spacer(modifier = Modifier.height(16.dp))
            
            OutlinedTextField(
                value = name.value,
                onValueChange = { name.value = it },
                label = { Text("Name") },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(bottom = 16.dp),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Text,
                    imeAction = ImeAction.Next
                )
            )
            
            OutlinedTextField(
                value = email.value,
                onValueChange = { email.value = it },
                label = { Text("Email") },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(bottom = 16.dp),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Email,
                    imeAction = ImeAction.Next
                )
            )
            
            OutlinedTextField(
                value = password.value,
                onValueChange = { password.value = it },
                label = { Text("Password") },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(bottom = 16.dp),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Password,
                    imeAction = ImeAction.Next
                ),
                visualTransformation = if (passwordVisibility.value) VisualTransformation.None else PasswordVisualTransformation(),
                trailingIcon = {
                    IconButton(onClick = { passwordVisibility.value = !passwordVisibility.value }) {
                        Icon(
                            imageVector = if (passwordVisibility.value) Icons.Default.Visibility else Icons.Default.VisibilityOff,
                            contentDescription = if (passwordVisibility.value) "Hide password" else "Show password"
                        )
                    }
                }
            )
            
            OutlinedTextField(
                value = confirmPassword.value,
                onValueChange = { confirmPassword.value = it },
                label = { Text("Confirm Password") },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(bottom = 32.dp),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.Password,
                    imeAction = ImeAction.Done
                ),
                visualTransformation = if (confirmPasswordVisibility.value) VisualTransformation.None else PasswordVisualTransformation(),
                trailingIcon = {
                    IconButton(onClick = { confirmPasswordVisibility.value = !confirmPasswordVisibility.value }) {
                        Icon(
                            imageVector = if (confirmPasswordVisibility.value) Icons.Default.Visibility else Icons.Default.VisibilityOff,
                            contentDescription = if (confirmPasswordVisibility.value) "Hide password" else "Show password"
                        )
                    }
                }
            )
            
            // Help text
            if (!buttonEnabled.value && !isLoading) {
                val missingFields = mutableListOf<String>()
                if (selectedRole == null) missingFields.add("role")
                if (name.value.trim().isEmpty()) missingFields.add("name")
                if (email.value.trim().isEmpty()) missingFields.add("email")
                if (password.value.isEmpty()) missingFields.add("password")
                if (confirmPassword.value.isEmpty()) missingFields.add("confirm password")
                
                Text(
                    text = "Please fill in: ${missingFields.joinToString(", ")}",
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.error
                )
            }
            
            Text(
                text = "Debug: Role=$selectedRole, Loading=$isLoading, Name=${name.value.isNotEmpty()}, Email=${email.value.isNotEmpty()}, Password=${password.value.isNotEmpty()}, Confirm=${confirmPassword.value.isNotEmpty()}, ButtonEnabled=${buttonEnabled.value}",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )

            Button(
                onClick = {
                    scope.launch {
                        android.util.Log.d("RegisterScreen", "Launching register inside coroutine")

                        if (selectedRole == null) {
                            Toast.makeText(context, "Please select a role (Owner or Client)", Toast.LENGTH_SHORT).show()
                            return@launch
                        }

                        if (name.value.trim().isEmpty()) {
                            Toast.makeText(context, "Please enter your name", Toast.LENGTH_SHORT).show()
                            return@launch
                        }

                        if (email.value.trim().isEmpty()) {
                            Toast.makeText(context, "Please enter your email", Toast.LENGTH_SHORT).show()
                            return@launch
                        }

                        if (password.value.isEmpty()) {
                            Toast.makeText(context, "Please enter a password", Toast.LENGTH_SHORT).show()
                            return@launch
                        }

                        if (password.value != confirmPassword.value) {
                            Toast.makeText(context, "Passwords do not match", Toast.LENGTH_SHORT).show()
                            return@launch
                        }

                        viewModel.register(
                            name.value.trim(),
                            email.value.trim(),
                            password.value
                        )
                    }
                },
                modifier = Modifier
                    .fillMaxWidth()
                    .height(50.dp),
                enabled = buttonEnabled.value,
                colors = ButtonDefaults.buttonColors(
                    containerColor = if (buttonEnabled.value) MaterialTheme.colorScheme.primary 
                                   else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
                )
            ) {
                if (isLoading) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(24.dp),
                        color = MaterialTheme.colorScheme.onPrimary
                    )
                } else {
                    Text(
                        text = if (buttonEnabled.value) "Register" else "Fill all fields to register",
                        color = if (buttonEnabled.value) MaterialTheme.colorScheme.onPrimary 
                               else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
                    )
                }
            }
            
            TextButton(
                onClick = onLoginClick,
                modifier = Modifier.padding(top = 8.dp)
            ) {
                Text("Already have an account? Login")
            }
        }
    }
}

RegisterActivity.kt:

package com.example.eventmanagement.ui

import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.SnackbarHostState
import androidx.lifecycle.lifecycleScope
import com.example.eventmanagement.MainActivity
import com.example.eventmanagement.viewmodel.RegisterViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@AndroidEntryPoint
class RegisterActivity : AppCompatActivity() {
    private val viewModel: RegisterViewModel by viewModels()
    private val snackbarHostState = SnackbarHostState()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Set content first to show UI immediately
        setContent {
            RegisterScreen(
                viewModel = viewModel,
                onRegisterSuccess = { /* Handled by state collection */ },
                onLoginClick = { navigateToLoginActivity() }
            )
        }

        // Start background operations after UI is shown
        lifecycleScope.launch(Dispatchers.IO) {
            try {
                val isLoggedIn = viewModel.isLoggedIn()
                if (isLoggedIn) {
                    withContext(Dispatchers.Main.immediate) {
                        navigateToMainActivity()
                    }
                }
            } catch (e: Exception) {
                withContext(Dispatchers.Main.immediate) {
                    showError("Failed to check login status: ${e.message}")
                }
            }
        }

        // Collect state changes in a separate coroutine
        lifecycleScope.launch(Dispatchers.IO) {
            try {
                android.util.Log.d("RegisterActivity", "Starting to collect register state changes")
                viewModel.registerState.collectLatest { state ->
                    android.util.Log.d("RegisterActivity", "State changed to: $state")
                    when (state) {
                        is RegisterViewModel.RegisterState.Success -> {
                            android.util.Log.d("RegisterActivity", "Registration successful, navigating to MainActivity")
                            withContext(Dispatchers.Main.immediate) {
                                navigateToMainActivity()
                            }
                        }
                        is RegisterViewModel.RegisterState.Error -> {
                            android.util.Log.d("RegisterActivity", "Registration error: ${state.message}")
                            withContext(Dispatchers.Main.immediate) {
                                showError(state.message)
                            }
                        }
                        is RegisterViewModel.RegisterState.Loading -> {
                            android.util.Log.d("RegisterActivity", "Registration loading...")
                        }
                        else -> {
                            android.util.Log.d("RegisterActivity", "Other state: $state")
                        }
                    }
                }
            } catch (e: Exception) {
                android.util.Log.e("RegisterActivity", "Failed to collect state changes: ${e.message}")
                withContext(Dispatchers.Main.immediate) {
                    showError("Failed to collect state changes: ${e.message}")
                }
            }
        }
    }

    private fun navigateToMainActivity() {
        val intent = Intent(this, MainActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        startActivity(intent)
        finish()
    }

    private fun navigateToLoginActivity() {
        val intent = Intent(this, LoginActivity::class.java)
        startActivity(intent)
        finish()
    }

    private fun showError(message: String) {
        lifecycleScope.launch(Dispatchers.Main.immediate) {
            snackbarHostState.showSnackbar(message)
        }
    }
}

RegisterViewModel.kt:

package com.example.eventmanagement.viewmodel

import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

@HiltViewModel
class RegisterViewModel @Inject constructor(
    application: Application,
    private val userRepository: UserRepository
) : AndroidViewModel(application) {

    private val _registerState = MutableStateFlow<RegisterState>(RegisterState.Idle)
    val registerState: StateFlow<RegisterState> = _registerState

    private val _selectedRole = MutableStateFlow<String?>(null)
    val selectedRole: StateFlow<String?> = _selectedRole

    suspend fun isLoggedIn(): Boolean {
        return try {
            userRepository.isLoggedIn()
        } catch (e: Exception) {
            withContext(Dispatchers.Main.immediate) {
                _registerState.value = RegisterState.Error(e.message ?: "Failed to check login status")
            }
            false
        }
    }

    fun setRole(role: String) {
        _selectedRole.value = role
    }

    fun register(name: String, email: String, password: String) {
        android.util.Log.d("RegisterViewModel", ">>> register function called OUTSIDE coroutine")

        viewModelScope.launch {
            android.util.Log.d("RegisterViewModel", ">>> INSIDE coroutine launch block")

            try {
                android.util.Log.d("RegisterViewModel", "Setting state to Loading")
                _registerState.value = RegisterState.Loading

                val isBackendReachable = userRepository.testBackendConnectivity()
                android.util.Log.d("RegisterViewModel", "Backend connectivity test result: $isBackendReachable")

                if (!isBackendReachable) {
                    _registerState.value = RegisterState.Error("Cannot connect to server. Please check your internet connection or try again later.")
                    return@launch
                }

                if (!validateInput(name, email, password)) {
                    android.util.Log.d("RegisterViewModel", "Input validation failed")
                    _registerState.value = RegisterState.Error("Invalid input")
                    return@launch
                }

                if (_selectedRole.value == null) {
                    android.util.Log.d("RegisterViewModel", "No role selected")
                    _registerState.value = RegisterState.Error("Please select a role")
                    return@launch
                }

                android.util.Log.d("RegisterViewModel", "Calling userRepository.register")

                userRepository.register(
                    name = name,
                    email = email,
                    password = password,
                    role = _selectedRole.value!!,
                    onSuccess = {
                        android.util.Log.d("RegisterViewModel", "Registration successful")
                        _registerState.value = RegisterState.Success
                    },
                    onFailure = { error ->
                        android.util.Log.d("RegisterViewModel", "Registration failed: $error")
                        _registerState.value = RegisterState.Error((error ?: "Registration failed").toString())
                    }
                )
            } catch (e: Exception) {
                android.util.Log.e("RegisterViewModel", "Exception in register: ${e.message}")
                _registerState.value = RegisterState.Error(e.message ?: "Registration failed")
            }
        }
    }

    private fun isNameValid(name: String): Boolean {
        return name.trim().length >= 2
    }

    private fun isEmailValid(email: String): Boolean {
        val emailRegex = Regex("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\$")
        return emailRegex.matches(email.trim())
    }

    private fun isPasswordValid(password: String): Boolean {
        return password.length >= 6
    }

    private fun validateInput(name: String, email: String, password: String): Boolean {
        android.util.Log.d("RegisterViewModel", "Validating input - Name: '$name' (${name.trim().length}), Email: '$email', Password: '${"*".repeat(password.length)}' (${password.length})")
        
        if (!isNameValid(name)) {
            android.util.Log.d("RegisterViewModel", "Name validation failed: ${name.trim().length} < 2")
            _registerState.value = RegisterState.Error("Name must be at least 2 characters long")
            return false
        }
        if (!isEmailValid(email)) {
            android.util.Log.d("RegisterViewModel", "Email validation failed: $email")
            _registerState.value = RegisterState.Error("Please enter a valid email address")
            return false
        }
        if (!isPasswordValid(password)) {
            android.util.Log.d("RegisterViewModel", "Password validation failed: length ${password.length} < 6")
            _registerState.value = RegisterState.Error("Password must be at least 6 characters long")
            return false
        }
        
        android.util.Log.d("RegisterViewModel", "Input validation passed")
        return true
    }

    sealed class RegisterState {
        object Idle : RegisterState()
        object Loading : RegisterState()
        object Success : RegisterState()
        data class Error(val message: String) : RegisterState()
    }
}

UserRepository.kt:

package com.example.eventmanagement.viewmodel

import android.app.Application
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.example.eventmanagement.model.UserResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONArray
import org.json.JSONObject
import java.io.IOException
import javax.inject.Inject
import java.util.Date
import com.example.eventmanagement.viewmodel.Booking

private val Context.dataStore by preferencesDataStore(name = "user_session")

class UserRepository @Inject constructor(private val application: Application) {
    private val client = OkHttpClient.Builder()
        .connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
        .readTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
        .writeTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
        .build()
    private val BASE_URL = "https://proper-physically-condor.ngrok-free.app"
    private val API_URL = "$BASE_URL/api/login"
    
    // Test backend connectivity
    suspend fun testBackendConnectivity(): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                val request = Request.Builder()
                    .url("$BASE_URL/api/register")
                    .head()
                    .build()
                
                val response = client.newCall(request).execute()
                android.util.Log.d("UserRepository", "Backend connectivity test - Response code: ${response.code}")
                response.isSuccessful
            } catch (e: Exception) {
                android.util.Log.e("UserRepository", "Backend connectivity test failed: ${e.message}")
                false
            }
        }
    }
    private val SESSION_TOKEN = stringPreferencesKey("session_token")
    private val USER_ID = intPreferencesKey("userId")
    private val USER_ROLE = stringPreferencesKey("userRole")
    private val REMEMBER_ME = booleanPreferencesKey("remember_me")

    suspend fun login(email: String, password: String, rememberMe: Boolean, onSuccess: () -> Unit, onFailure: (Any?) -> Unit) {
        val jsonObject = JSONObject().apply {
            put("email", email)
            put("password", password)
            put("rememberMe", rememberMe)
        }

        val requestBody = jsonObject.toString().toRequestBody("application/json".toMediaTypeOrNull())

        val request = Request.Builder()
            .url(API_URL)
            .post(requestBody)
            .build()

        withContext(Dispatchers.IO) {
            try {
                android.util.Log.d("UserRepository", "Attempting login with URL: ${request.url}")
                val response = client.newCall(request).execute()
                val responseData = response.body?.string()
                
                android.util.Log.d("UserRepository", "Response code: ${response.code}")
                android.util.Log.d("UserRepository", "Response body: $responseData")

                if (!response.isSuccessful) {
                    val errorMessage = when (response.code) {
                        404 -> "Server not found. Please check your internet connection."
                        503, 504 -> "Server is currently unavailable. Please try again later."
                        else -> try {
                            if (responseData != null) {
                                val errorJson = JSONObject(responseData)
                                errorJson.optString("message", errorJson.optString("error", "Login failed"))
                            } else {
                                "Login failed: Server returned ${response.code}"
                            }
                        } catch (e: Exception) {
                            android.util.Log.e("UserRepository", "Error parsing response: ${e.message}")
                            "Login failed: ${response.message}"
                        }
                    }
                    withContext(Dispatchers.Main) {
                        onFailure(errorMessage)
                    }
                    return@withContext
                }

                if (responseData == null) {
                    withContext(Dispatchers.Main) {
                        onFailure("No response from server")
                    }
                    return@withContext
                }

                try {
                    val jsonObject = JSONObject(responseData)
                    
                    if (jsonObject.optBoolean("success", false)) {
                        val data = jsonObject.getJSONObject("data")
                        val token = data.getString("token")
                        val user = data.getJSONObject("user")
                        
                        val userId = user.getInt("id")
                        val userRole = user.getString("role")

                        // Save the session data
                        application.applicationContext.dataStore.edit { preferences ->
                            preferences[SESSION_TOKEN] = token
                            preferences[USER_ID] = userId
                            preferences[USER_ROLE] = userRole
                            preferences[REMEMBER_ME] = rememberMe
                        }

                        withContext(Dispatchers.Main) {
                            onSuccess()
                        }
                    } else {
                        val message = jsonObject.optString("message", "Login failed")
                        withContext(Dispatchers.Main) {
                            onFailure(message)
                        }
                    }
                } catch (e: Exception) {
                    android.util.Log.e("UserRepository", "Error parsing success response: ${e.message}")
                    withContext(Dispatchers.Main) {
                        onFailure("Error processing server response: ${e.message}")
                    }
                }
            } catch (e: IOException) {
                android.util.Log.e("UserRepository", "Network error: ${e.message}")
                withContext(Dispatchers.Main) {
                    onFailure("Network error: Please check your internet connection")
                }
            } catch (e: Exception) {
                android.util.Log.e("UserRepository", "Unexpected error: ${e.message}")
                withContext(Dispatchers.Main) {
                    onFailure("An unexpected error occurred: ${e.message}")
                }
            }
        }
    }

    suspend fun logout(onLogout: () -> Unit) {
        withContext(Dispatchers.IO) {
            application.applicationContext.dataStore.edit { preferences ->
                preferences.remove(SESSION_TOKEN)
                preferences.remove(USER_ID)
            }

            // Switch to Main thread to call onLogout
            withContext(Dispatchers.Main) {
                onLogout()
            }
        }
    }

    suspend fun isLoggedIn(): Boolean {
        return withContext(Dispatchers.IO) {
            val sessionToken = application.applicationContext.dataStore.data.map { preferences ->
                preferences[SESSION_TOKEN]
            }.first()
            !sessionToken.isNullOrEmpty()
        }
    }

    suspend fun getUserId(): Int {
        return withContext(Dispatchers.IO) {
            application.applicationContext.dataStore.data.map { preferences ->
                preferences[USER_ID] ?: -1
            }.first()
        }
    }

    suspend fun getUserRole(): String {
        return withContext(Dispatchers.IO) {
            try {
                val userId = getUserId()
                val jsonObject = JSONObject().apply {
                    put("userId", userId)
                }

                val requestBody = jsonObject.toString().toRequestBody("application/json".toMediaTypeOrNull())

                val request = Request.Builder()
                    .url("$BASE_URL/api/userrole")
                    .post(requestBody)
                    .build()

                val response = client.newCall(request).execute()
                val responseData = response.body?.string()

                if (response.isSuccessful && responseData != null) {
                    val jsonResponse = JSONObject(responseData)
                    val role = jsonResponse.getString("role")
                    
                    // Update the stored role
                    application.applicationContext.dataStore.edit { preferences ->
                        preferences[USER_ROLE] = role
                    }
                    
                    role
                } else {
                    // Return the stored role if API call fails
                    application.applicationContext.dataStore.data.map { preferences ->
                        preferences[USER_ROLE] ?: ""
                    }.first()
                }
            } catch (e: Exception) {
                android.util.Log.e("UserRepository", "Error fetching user role: ${e.message}")
                // Return the stored role if there's an error
                application.applicationContext.dataStore.data.map { preferences ->
                    preferences[USER_ROLE] ?: ""
                }.first()
            }
        }
    }

    suspend fun fetchUserProfile(userId: Int): UserProfile? {
        return withContext(Dispatchers.IO) {
            val request = Request.Builder()
                .url("https://proper-physically-condor.ngrok-free.app/api/fetch-profile?userId=$userId")
                .get()
                .build()

            val response = client.newCall(request).execute()
            val responseData = response.body?.string()
            if (response.isSuccessful && responseData != null) {
                val jsonObject = JSONObject(responseData)
                UserProfile(
                    id = jsonObject.getInt("id"),
                    name = jsonObject.getString("name"),
                    username = jsonObject.getString("username"),
                    email = jsonObject.getString("email"),
                    phone = jsonObject.getString("phone"),
                    profileImageUrl = jsonObject.getString("profileImageUrl")
                )
            } else {
                null
            }
        }
    }

    suspend fun fetchBookings(userId: Int): List<Booking> {
        return withContext(Dispatchers.IO) {
            val request = Request.Builder()
                .url("https://proper-physically-condor.ngrok-free.app/api/myBookings?userId=$userId")
                .get()
                .build()

            val response = client.newCall(request).execute()
            val responseData = response.body?.string()
            if (response.isSuccessful && responseData != null) {
                val jsonArray = JSONArray(responseData)
                val bookingsList = mutableListOf<Booking>()
                for (i in 0 until jsonArray.length()) {
                    val jsonObject = jsonArray.getJSONObject(i)
                    val booking = Booking(
                        id = jsonObject.getInt("id"),
                        hallName = jsonObject.optJSONObject("hall")?.getString("name"),
                        hallImage = jsonObject.optJSONObject("hall")?.getString("image"),
                        cateringName = jsonObject.optJSONObject("catering")?.getString("name"),
                        cateringImage = jsonObject.optJSONObject("catering")?.getString("image"),
                        decorationName = jsonObject.optJSONObject("decoration")?.getString("name"),
                        decorationImage = jsonObject.optJSONObject("decoration")?.getString("image"),
                        date = jsonObject.getString("date"),
                        startTime = jsonObject.optString("startTime"),
                        endTime = jsonObject.optString("endTime"),
                        status = jsonObject.optString("status", "pending"),
                        paymentStatus = jsonObject.optString("paymentStatus", "pending"),
                        amount = jsonObject.optDouble("amount", 0.0),
                        createdAt = jsonObject.optString("createdAt", ""),
                        updatedAt = jsonObject.optString("updatedAt", "")
                    )
                    bookingsList.add(booking)
                }
                bookingsList
            } else {
                throw Exception("Failed to fetch bookings: ${response.message}")
            }
        }
    }

    suspend fun register(
        name: String,
        email: String,
        password: String,
        role: String,
        onSuccess: () -> Unit,
        onFailure: (Any?) -> Unit
    ) {
        android.util.Log.d("UserRepository", "Starting registration with - Name: $name, Email: $email, Role: $role")
        
        // First, test if the backend is reachable
        try {
            val testRequest = Request.Builder()
                .url("$BASE_URL/api/register")
                .head()
                .build()
            
            val testResponse = client.newCall(testRequest).execute()
            android.util.Log.d("UserRepository", "Backend connectivity test - Response code: ${testResponse.code}")
        } catch (e: Exception) {
            android.util.Log.e("UserRepository", "Backend connectivity test failed: ${e.message}")
        }
        
        val jsonObject = JSONObject().apply {
            put("name", name)
            put("email", email)
            put("password", password)
            put("role", role)
        }

        val requestBody = jsonObject.toString().toRequestBody("application/json".toMediaTypeOrNull())
        android.util.Log.d("UserRepository", "Request body: ${jsonObject.toString()}")

        val request = Request.Builder()
            .url("$BASE_URL/api/register")
            .post(requestBody)
            .build()
        
        android.util.Log.d("UserRepository", "Registration URL: ${request.url}")

        withContext(Dispatchers.IO) {
            try {
                android.util.Log.d("UserRepository", "Attempting registration with URL: ${request.url}")
                val response = client.newCall(request).execute()
                val responseData = response.body?.string()
                
                android.util.Log.d("UserRepository", "Response code: ${response.code}")
                android.util.Log.d("UserRepository", "Response body: $responseData")

                if (!response.isSuccessful) {
                    val errorMessage = when (response.code) {
                        404 -> "Server not found. Please check your internet connection."
                        503, 504 -> "Server is currently unavailable. Please try again later."
                        else -> try {
                            if (responseData != null) {
                                val errorJson = JSONObject(responseData)
                                errorJson.optString("details", errorJson.optString("error", "Registration failed"))
                            } else {
                                "Registration failed: Server returned ${response.code}"
                            }
                        } catch (e: Exception) {
                            android.util.Log.e("UserRepository", "Error parsing response: ${e.message}")
                            "Registration failed: ${response.message}"
                        }
                    }
                    withContext(Dispatchers.Main) {
                        onFailure(errorMessage)
                    }
                    return@withContext
                }

                if (responseData == null) {
                    withContext(Dispatchers.Main) {
                        onFailure("No response from server")
                    }
                    return@withContext
                }

                try {
                    android.util.Log.d("UserRepository", "Response register: $responseData")
                    val jsonObject = JSONObject(responseData)
                    
                    if (jsonObject.optBoolean("success", false)) {
                        val data = jsonObject.getJSONObject("data")
                        val token = data.getString("token")
                        val user = data.getJSONObject("user")
                        
                        val userId = user.getInt("id")
                        val userRole = user.getString("role")

                        android.util.Log.d("UserRepository", "Parsed data - userId: $userId, role: $userRole, token: $token")

                        // Save the session data
                        application.applicationContext.dataStore.edit { preferences ->
                            preferences[SESSION_TOKEN] = token
                            preferences[USER_ID] = userId
                            preferences[USER_ROLE] = userRole
                        }

                        withContext(Dispatchers.Main) {
                            onSuccess()
                        }
                    } else {
                        val message = jsonObject.optString("message", "Registration failed")
                        withContext(Dispatchers.Main) {
                            onFailure(message)
                        }
                    }
                } catch (e: Exception) {
                    android.util.Log.e("UserRepository", "Error parsing success response: ${e.message}")
                    withContext(Dispatchers.Main) {
                        onFailure("Error processing server response: ${e.message}")
                    }
                }
            } catch (e: IOException) {
                android.util.Log.e("UserRepository", "Network error: ${e.message}")
                withContext(Dispatchers.Main) {
                    onFailure("Network error: Please check your internet connection")
                }
            } catch (e: Exception) {
                android.util.Log.e("UserRepository", "Unexpected error: ${e.message}")
                withContext(Dispatchers.Main) {
                    onFailure("An unexpected error occurred: ${e.message}")
                }
            }
        }
    }

    suspend fun getRememberMe(): Boolean {
        return withContext(Dispatchers.IO) {
            application.applicationContext.dataStore.data.map { preferences ->
                preferences[REMEMBER_ME] ?: false
            }.first()
        }
    }

    suspend fun setRememberMe(rememberMe: Boolean) {
        withContext(Dispatchers.IO) {
            application.applicationContext.dataStore.edit { preferences ->
                preferences[REMEMBER_ME] = rememberMe
            }
        }
    }

    suspend fun getUserResponses(): List<UserResponse> {
        return withContext(Dispatchers.IO) {
            val userId = getUserId()
            val request = Request.Builder()
                .url("$BASE_URL/api/user-responses?userId=$userId")
                .get()
                .build()

            val response = client.newCall(request).execute()
            val responseData = response.body?.string()
            
            if (response.isSuccessful && responseData != null) {
                val jsonArray = JSONArray(responseData)
                val responsesList = mutableListOf<UserResponse>()
                
                for (i in 0 until jsonArray.length()) {
                    val jsonObject = jsonArray.getJSONObject(i)
                    val response = UserResponse(
                        id = jsonObject.getInt("id"),
                        userId = jsonObject.getInt("userId"),
                        serviceId = jsonObject.getInt("serviceId"),
                        serviceName = jsonObject.getString("serviceName"),
                        serviceType = jsonObject.getString("serviceType"),
                        status = jsonObject.getString("status"),
                        createdAt = Date(jsonObject.getLong("createdAt"))
                    )
                    responsesList.add(response)
                }
                responsesList
            } else {
                throw Exception("Failed to fetch user responses: ${response.message}")
            }
        }
    }
}