import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/services/auth_service.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/widgets/my_form_validator.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/view/dashboard/dashboard_screen.dart'; import 'package:marco/helpers/services/app_logger.dart'; class MPINController extends GetxController { final MyFormValidator basicValidator = MyFormValidator(); final isNewUser = false.obs; final RxBool isLoading = false.obs; final formKey = GlobalKey(); final digitControllers = List.generate(6, (_) => TextEditingController()); final focusNodes = List.generate(6, (_) => FocusNode()); final retypeControllers = List.generate(6, (_) => TextEditingController()); final retypeFocusNodes = List.generate(6, (_) => FocusNode()); final RxInt failedAttempts = 0.obs; @override void onInit() { super.onInit(); final bool hasMpin = LocalStorage.getIsMpin(); isNewUser.value = !hasMpin; logSafe("onInit called. isNewUser: ${isNewUser.value}"); } void onDigitChanged(String value, int index, {bool isRetype = false}) { logSafe("onDigitChanged -> index: $index, value: $value, isRetype: $isRetype", ); final nodes = isRetype ? retypeFocusNodes : focusNodes; if (value.isNotEmpty && index < 5) { nodes[index + 1].requestFocus(); } else if (value.isEmpty && index > 0) { nodes[index - 1].requestFocus(); } } Future onSubmitMPIN() async { logSafe("onSubmitMPIN triggered"); if (!formKey.currentState!.validate()) { logSafe("Form validation failed", level: LogLevel.warning); return; } final enteredMPIN = digitControllers.map((c) => c.text).join(); logSafe("Entered MPIN: $enteredMPIN", ); if (enteredMPIN.length < 6) { _showError("Please enter all 6 digits."); return; } if (isNewUser.value) { final retypeMPIN = retypeControllers.map((c) => c.text).join(); logSafe("Retyped MPIN: $retypeMPIN", ); if (retypeMPIN.length < 6) { _showError("Please enter all 6 digits in Retype MPIN."); return; } if (enteredMPIN != retypeMPIN) { _showError("MPIN and Retype MPIN do not match."); clearFields(); clearRetypeFields(); return; } logSafe("MPINs matched. Proceeding to generate MPIN."); final bool success = await generateMPIN(mpin: enteredMPIN); if (success) { logSafe("MPIN generation successful."); showAppSnackbar( title: "Success", message: "MPIN generated successfully. Please login again.", type: SnackbarType.success, ); await LocalStorage.logout(); } else { logSafe("MPIN generation failed.", level: LogLevel.warning); clearFields(); clearRetypeFields(); } } else { logSafe("Existing user. Proceeding to verify MPIN."); await verifyMPIN(); } } Future onForgotMPIN() async { logSafe("onForgotMPIN called"); isNewUser.value = true; clearFields(); clearRetypeFields(); } void switchToEnterMPIN() { logSafe("switchToEnterMPIN called"); isNewUser.value = false; clearFields(); clearRetypeFields(); } void _showError(String message) { logSafe("ERROR: $message", level: LogLevel.error); showAppSnackbar( title: "Error", message: message, type: SnackbarType.error, ); } void _navigateToDashboard({String? message}) { if (message != null) { logSafe("Navigating to Dashboard with message: $message"); showAppSnackbar( title: "Success", message: message, type: SnackbarType.success, ); } Get.offAll(() => const DashboardScreen()); } void clearFields() { logSafe("clearFields called"); for (final c in digitControllers) { c.clear(); } focusNodes.first.requestFocus(); } void clearRetypeFields() { logSafe("clearRetypeFields called"); for (final c in retypeControllers) { c.clear(); } retypeFocusNodes.first.requestFocus(); } @override void onClose() { logSafe("onClose called"); for (final controller in digitControllers) { controller.dispose(); } for (final node in focusNodes) { node.dispose(); } for (final controller in retypeControllers) { controller.dispose(); } for (final node in retypeFocusNodes) { node.dispose(); } super.onClose(); } Future generateMPIN({ required String mpin, }) async { try { isLoading.value = true; logSafe("generateMPIN started"); final employeeInfo = LocalStorage.getEmployeeInfo(); final String? employeeId = employeeInfo?.id; if (employeeId == null || employeeId.isEmpty) { isLoading.value = false; _showError("Missing employee ID."); return false; } logSafe("Calling AuthService.generateMpin for employeeId: $employeeId", ); final response = await AuthService.generateMpin( employeeId: employeeId, mpin: mpin, ); isLoading.value = false; if (response == null) { logSafe("MPIN generated successfully"); showAppSnackbar( title: "Success", message: "MPIN generated successfully. Please login again.", type: SnackbarType.success, ); await LocalStorage.logout(); return true; } else { logSafe("MPIN generation returned error: $response", level: LogLevel.warning); showAppSnackbar( title: "MPIN Generation Failed", message: "Please check your inputs.", type: SnackbarType.error, ); basicValidator.addErrors(response); basicValidator.validateForm(); basicValidator.clearErrors(); return false; } } catch (e) { isLoading.value = false; logSafe("Exception in generateMPIN", level: LogLevel.error, error: e); _showError("Failed to generate MPIN."); return false; } } Future verifyMPIN() async { logSafe("verifyMPIN triggered"); final enteredMPIN = digitControllers.map((c) => c.text).join(); logSafe("Entered MPIN: $enteredMPIN", ); if (enteredMPIN.length < 6) { _showError("Please enter all 6 digits."); return; } final mpinToken = await LocalStorage.getMpinToken(); if (mpinToken == null || mpinToken.isEmpty) { _showError("Missing MPIN token. Please log in again."); return; } try { isLoading.value = true; final response = await AuthService.verifyMpin( mpin: enteredMPIN, mpinToken: mpinToken, ); isLoading.value = false; if (response == null) { logSafe("MPIN verified successfully"); await LocalStorage.setBool('mpin_verified', true); showAppSnackbar( title: "Success", message: "MPIN Verified Successfully", type: SnackbarType.success, ); _navigateToDashboard(); } else { final errorMessage = response["error"] ?? "Invalid MPIN"; logSafe("MPIN verification failed: $errorMessage", level: LogLevel.warning); showAppSnackbar( title: "Error", message: errorMessage, type: SnackbarType.error, ); clearFields(); onInvalidMPIN(); } } catch (e) { isLoading.value = false; logSafe("Exception in verifyMPIN", level: LogLevel.error, error: e); showAppSnackbar( title: "Error", message: "Something went wrong. Please try again.", type: SnackbarType.error, ); } } void onInvalidMPIN() { failedAttempts.value++; if (failedAttempts.value >= 3) { showAppSnackbar( title: "Error", message: "Too many failed attempts. Consider logging in again.", type: SnackbarType.error, ); } } }