package sh.sar.gridflow import android.content.Intent import android.os.Bundle import android.util.Log import android.view.View import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import sh.sar.gridflow.databinding.ActivityLoginBinding import sh.sar.gridflow.network.ApiResult import sh.sar.gridflow.network.FenakaApiService import sh.sar.gridflow.utils.SecureStorage class LoginActivity : AppCompatActivity() { private lateinit var binding: ActivityLoginBinding private lateinit var secureStorage: SecureStorage private lateinit var apiService: FenakaApiService companion object { private const val TAG = "LoginActivity" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.d(TAG, "LoginActivity onCreate") binding = ActivityLoginBinding.inflate(layoutInflater) setContentView(binding.root) try { secureStorage = SecureStorage(this) apiService = FenakaApiService() // Check if already logged in if (secureStorage.isLoggedIn()) { Log.d(TAG, "User already logged in, navigating to main") navigateToMain() return } // Pre-fill saved credentials secureStorage.getMobile()?.let { mobile -> Log.d(TAG, "Pre-filling mobile number: $mobile") binding.etMobileNumber.setText(mobile) } } catch (e: Exception) { Log.e(TAG, "Failed to initialize SecureStorage, continuing without it", e) apiService = FenakaApiService() Toast.makeText(this, "Warning: Secure storage not available", Toast.LENGTH_SHORT).show() } setupClickListeners() } private fun setupClickListeners() { binding.btnSignIn.setOnClickListener { handleSignIn() } binding.btnRegister.setOnClickListener { handleRegister() } binding.btnForgotPassword.setOnClickListener { handleForgotPassword() } binding.btnPayWithoutAccount.setOnClickListener { handlePayWithoutAccount() } } private fun handleSignIn() { val mobileNumber = binding.etMobileNumber.text.toString().trim() val password = binding.etPassword.text.toString().trim() Log.d(TAG, "handleSignIn called with mobile: $mobileNumber") if (validateInput(mobileNumber, password)) { Log.d(TAG, "Input validation passed, calling performLogin") performLogin(mobileNumber, password) } else { Log.d(TAG, "Input validation failed") } } private fun performLogin(mobile: String, password: String) { Log.d(TAG, "performLogin called with mobile: $mobile") setLoading(true) lifecycleScope.launch { Log.d(TAG, "Starting coroutine for API call") try { when (val result = apiService.login(mobile, password)) { is ApiResult.Success -> { Log.d(TAG, "Login successful: ${result.data}") // Save credentials and user info (if SecureStorage is available) if (this@LoginActivity::secureStorage.isInitialized) { try { secureStorage.saveCredentials(mobile, password) secureStorage.saveUserInfo( result.data.name, result.data.email, result.data.id ) // Extract and save cookie result.cookie?.let { cookie -> val sessionId = extractSessionId(cookie) Log.d(TAG, "Extracted session ID: $sessionId") secureStorage.saveCookie(sessionId) } } catch (e: Exception) { Log.e(TAG, "Failed to save credentials securely", e) } } setLoading(false) navigateToMain() } is ApiResult.Error -> { Log.d(TAG, "Login failed: ${result.message} (code: ${result.code})") setLoading(false) Toast.makeText(this@LoginActivity, result.message, Toast.LENGTH_LONG).show() } } } catch (e: Exception) { Log.e(TAG, "Exception in performLogin coroutine", e) setLoading(false) Toast.makeText(this@LoginActivity, "Login failed: ${e.message}", Toast.LENGTH_LONG).show() } } } private fun extractSessionId(setCookieHeader: String): String { // Extract the session ID from Set-Cookie header // Format: connect.sid=s%3A-vUZGRtHZZygj5Xm1Xg9nKdcZqanQCWm.JVdk7%2Bv63292vx5TWsOiws4QiGwKCYKjh%2FUhLWGEYVs; HttpOnly; Path=/; Expires=... return setCookieHeader.substringBefore(";").substringAfter("connect.sid=") } private fun setLoading(isLoading: Boolean) { binding.btnSignIn.isEnabled = !isLoading binding.btnSignIn.text = if (isLoading) "Signing in..." else "Sign In" // Disable other buttons during loading binding.btnRegister.isEnabled = !isLoading binding.btnForgotPassword.isEnabled = !isLoading binding.btnPayWithoutAccount.isEnabled = !isLoading // Show/hide progress indicator binding.etMobileNumber.isEnabled = !isLoading binding.etPassword.isEnabled = !isLoading } private fun navigateToMain() { val intent = Intent(this, MainActivity::class.java) startActivity(intent) finish() } private fun handleRegister() { Toast.makeText(this, "Register functionality coming soon", Toast.LENGTH_SHORT).show() // TODO: Navigate to registration screen } private fun handleForgotPassword() { Toast.makeText(this, "Forgot password functionality coming soon", Toast.LENGTH_SHORT).show() // TODO: Navigate to forgot password screen } private fun handlePayWithoutAccount() { Toast.makeText(this, "Guest payment functionality coming soon", Toast.LENGTH_SHORT).show() // TODO: Navigate to guest payment flow } private fun validateInput(mobileNumber: String, password: String): Boolean { if (mobileNumber.isEmpty()) { binding.etMobileNumber.error = "Mobile number is required" return false } if (password.isEmpty()) { binding.etPassword.error = "Password is required" return false } // Client-side password validation - must be at least 8 characters if (password.length < 8) { binding.etPassword.error = "Password must be at least 8 characters" return false } // Basic Maldives mobile number validation (7xxxxxx format) if (!mobileNumber.matches(Regex("^[79]\\d{6}$"))) { binding.etMobileNumber.error = "Enter a valid Maldives mobile number" return false } return true } }