import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:logger/logger.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'; class MPINController extends GetxController { final Logger logger = Logger(); 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()); @override void onInit() { super.onInit(); final bool hasMpin = LocalStorage.getIsMpin(); isNewUser.value = !hasMpin; logger.i("[MPINController] onInit called. isNewUser: ${isNewUser.value}"); } void onDigitChanged(String value, int index, {bool isRetype = false}) { logger.i( "[MPINController] 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 { logger.i("[MPINController] onSubmitMPIN triggered"); if (!formKey.currentState!.validate()) { logger.w("[MPINController] Form validation failed"); return; } final enteredMPIN = digitControllers.map((c) => c.text).join(); logger.i("[MPINController] 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(); logger.i("[MPINController] 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; } logger.i("[MPINController] MPINs matched. Proceeding to generate MPIN."); final bool success = await generateMPIN(mpin: enteredMPIN); if (success) { logger.i("[MPINController] MPIN generation successful."); _navigateToDashboard(message: "MPIN Set Successfully"); } else { logger.w("[MPINController] MPIN generation failed."); clearFields(); clearRetypeFields(); } } else { logger.i("[MPINController] Existing user. Proceeding to verify MPIN."); await verifyMPIN(); } } Future onForgotMPIN() async { logger.i("[MPINController] onForgotMPIN called"); isNewUser.value = true; clearFields(); clearRetypeFields(); } void switchToEnterMPIN() { logger.i("[MPINController] switchToEnterMPIN called"); isNewUser.value = false; clearFields(); clearRetypeFields(); } void _showError(String message) { logger.e("[MPINController] ERROR: $message"); showAppSnackbar( title: "Error", message: message, type: SnackbarType.error, ); } void _navigateToDashboard({String? message}) { if (message != null) { logger .i("[MPINController] Navigating to Dashboard with message: $message"); showAppSnackbar( title: "Success", message: message, type: SnackbarType.success, ); } Get.offAll(() => const DashboardScreen()); } void clearFields() { logger.i("[MPINController] clearFields called"); for (final c in digitControllers) { c.clear(); } focusNodes.first.requestFocus(); } void clearRetypeFields() { logger.i("[MPINController] clearRetypeFields called"); for (final c in retypeControllers) { c.clear(); } retypeFocusNodes.first.requestFocus(); } @override void onClose() { logger.i("[MPINController] 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; logger.i("[MPINController] generateMPIN started for MPIN: $mpin"); final employeeInfo = LocalStorage.getEmployeeInfo(); final String? employeeId = employeeInfo?.id; if (employeeId == null || employeeId.isEmpty) { isLoading.value = false; _showError("Missing employee ID."); return false; } logger.i( "[MPINController] Calling AuthService.generateMpin for employeeId: $employeeId"); final response = await AuthService.generateMpin( employeeId: employeeId, mpin: mpin, ); isLoading.value = false; if (response == null) { logger.i( "[MPINController] MPIN generated successfully (null response treated as success)"); Get.toNamed('/auth/mpin-auth'); return true; } else { logger.w( "[MPINController] MPIN generation returned error response: $response"); 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; _showError("Failed to generate MPIN: $e"); logger.e("[MPINController] Exception in generateMPIN: $e"); return false; } } Future verifyMPIN() async { logger.i("[MPINController] verifyMPIN triggered"); final enteredMPIN = digitControllers.map((c) => c.text).join(); logger.i("[MPINController] 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) { logger.i("[MPINController] MPIN verified successfully."); // Set mpin_verified to true in local storage here: await LocalStorage.setBool('mpin_verified', true); showAppSnackbar( title: "Success", message: "MPIN Verified Successfully", type: SnackbarType.success, ); _navigateToDashboard(); } else { final errorMessage = response["error"] ?? "Invalid MPIN"; logger.w("[MPINController] MPIN verification failed: $errorMessage"); showAppSnackbar( title: "Error", message: errorMessage, type: SnackbarType.error, ); clearFields(); } } catch (e) { isLoading.value = false; final error = "Failed to verify MPIN: $e"; logger.e("[MPINController] Exception in verifyMPIN: $error"); showAppSnackbar( title: "Error", message: "Something went wrong. Please try again.", type: SnackbarType.error, ); } } }