import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/helpers/services/auth_service.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/services/app_logger.dart'; class OTPController extends GetxController { final formKey = GlobalKey(); final RxString email = ''.obs; final RxBool isOTPSent = false.obs; final RxBool isSending = false.obs; final RxBool isResending = false.obs; final RxInt timer = 0.obs; Timer? _countdownTimer; final TextEditingController emailController = TextEditingController(); final List otpControllers = List.generate(4, (_) => TextEditingController()); final List focusNodes = List.generate(4, (_) => FocusNode()); @override void onInit() { super.onInit(); timer.value = 0; _loadSavedEmail(); logSafe("[OTPController] Initialized"); } @override void onClose() { _countdownTimer?.cancel(); emailController.dispose(); for (final controller in otpControllers) { controller.dispose(); } for (final node in focusNodes) { node.dispose(); } logSafe("[OTPController] Disposed"); super.onClose(); } Future _sendOTP(String email) async { logSafe("[OTPController] Sending OTP"); final result = await AuthService.generateOtp(email); if (result == null) { logSafe("[OTPController] OTP sent successfully"); return true; } else { logSafe( "[OTPController] OTP send failed", level: LogLevel.warning, error: result['error'], ); showAppSnackbar( title: "Error", message: result['error'] ?? "Failed to send OTP", type: SnackbarType.error, ); return false; } } Future sendOTP() async { final userEmail = emailController.text.trim(); logSafe("[OTPController] sendOTP called"); if (!_validateEmail(userEmail)) { logSafe("[OTPController] Invalid email format", level: LogLevel.warning); showAppSnackbar( title: "Error", message: "Please enter a valid email address", type: SnackbarType.error, ); return; } if (isSending.value) return; isSending.value = true; final success = await _sendOTP(userEmail); if (success) { email.value = userEmail; isOTPSent.value = true; await _saveEmailIfRemembered(userEmail); _startTimer(); _clearOTPFields(); } isSending.value = false; } Future onResendOTP() async { if (isResending.value) return; logSafe("[OTPController] Resending OTP"); isResending.value = true; _clearOTPFields(); final success = await _sendOTP(email.value); if (success) { _startTimer(); } isResending.value = false; } void onOTPChanged(String value, int index) { logSafe("[OTPController] OTP field changed: index=$index", level: LogLevel.debug); if (value.isNotEmpty) { if (index < otpControllers.length - 1) { focusNodes[index + 1].requestFocus(); } else { focusNodes[index].unfocus(); } } else { if (index > 0) { focusNodes[index - 1].requestFocus(); } } } Future verifyOTP() async { final enteredOTP = otpControllers.map((c) => c.text).join(); final result = await AuthService.verifyOtp( email: email.value, otp: enteredOTP, ); if (result == null) { // ✅ Handle remember-me like in LoginController final remember = LocalStorage.getBool('remember_me') ?? false; if (remember) await LocalStorage.setToken('otp_email', email.value); // ✅ Enable remote logging enableRemoteLogging(); Get.offAllNamed('/select-tenant'); } else { showAppSnackbar( title: "Error", message: result['error']!, type: SnackbarType.error, ); } } void _clearOTPFields() { logSafe("[OTPController] Clearing OTP input fields", level: LogLevel.debug); for (final controller in otpControllers) { controller.clear(); } focusNodes[0].requestFocus(); } void _startTimer() { logSafe("[OTPController] Starting resend timer"); timer.value = 60; _countdownTimer?.cancel(); _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { if (this.timer.value > 0) { this.timer.value--; } else { timer.cancel(); } }); } void resetForChangeEmail() { logSafe("[OTPController] Resetting OTP form for change email"); isOTPSent.value = false; email.value = ''; emailController.clear(); _clearOTPFields(); timer.value = 0; isSending.value = false; isResending.value = false; for (final node in focusNodes) { node.unfocus(); } // Optionally remove saved email LocalStorage.removeToken('otp_email'); } bool _validateEmail(String email) { final regex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,}$'); return regex.hasMatch(email); } /// Save email to local storage if "remember me" is set Future _saveEmailIfRemembered(String email) async { final remember = LocalStorage.getBool('remember_me') ?? false; if (remember) { await LocalStorage.setToken('otp_email', email); } } /// Load email from local storage if "remember me" is true Future _loadSavedEmail() async { final remember = LocalStorage.getBool('remember_me') ?? false; if (remember) { final savedEmail = LocalStorage.getToken('otp_email') ?? ''; emailController.text = savedEmail; email.value = savedEmail; logSafe( "[OTPController] Loaded saved email from local storage: $savedEmail"); } } }