marco.pms.mobileapp/lib/controller/auth/mpin_controller.dart

316 lines
8.8 KiB
Dart

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';
// import 'package:marco/helpers/services/firebase/firebase_messaging_service.dart'; // 🔴 Commented out
class MPINController extends GetxController {
final MyFormValidator basicValidator = MyFormValidator();
final isNewUser = false.obs;
final isChangeMpin = false.obs;
final RxBool isLoading = false.obs;
final formKey = GlobalKey<FormState>();
// Updated to 4-digit MPIN
final digitControllers = List.generate(4, (_) => TextEditingController());
final focusNodes = List.generate(4, (_) => FocusNode());
final retypeControllers = List.generate(4, (_) => TextEditingController());
final retypeFocusNodes = List.generate(4, (_) => 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}");
}
/// Enable Change MPIN mode
void setChangeMpinMode() {
isChangeMpin.value = true;
isNewUser.value = false;
clearFields();
clearRetypeFields();
logSafe("setChangeMpinMode activated");
}
/// Handle digit entry and focus movement
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 < 3) {
nodes[index + 1].requestFocus();
} else if (value.isEmpty && index > 0) {
nodes[index - 1].requestFocus();
}
}
/// Submit MPIN for verification or generation
Future<void> 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 < 4) {
_showError("Please enter all 4 digits.");
return;
}
if (isNewUser.value || isChangeMpin.value) {
final retypeMPIN = retypeControllers.map((c) => c.text).join();
logSafe("Retyped MPIN: $retypeMPIN");
if (retypeMPIN.length < 4) {
_showError("Please enter all 4 digits in Retype MPIN.");
return;
}
if (enteredMPIN != retypeMPIN) {
_showError("MPIN and Retype MPIN do not match.");
clearFields();
clearRetypeFields();
return;
}
final bool success = await generateMPIN(mpin: enteredMPIN);
if (success) {
logSafe("MPIN generation/change successful.");
showAppSnackbar(
title: "Success",
message: isChangeMpin.value
? "MPIN changed successfully."
: "MPIN generated successfully. Please login again.",
type: SnackbarType.success,
);
await LocalStorage.logout();
} else {
logSafe("MPIN generation/change failed.", level: LogLevel.warning);
clearFields();
clearRetypeFields();
}
} else {
logSafe("Existing user. Proceeding to verify MPIN.");
await verifyMPIN();
}
}
/// Forgot MPIN
Future<void> onForgotMPIN() async {
logSafe("onForgotMPIN called");
isNewUser.value = true;
isChangeMpin.value = false;
clearFields();
clearRetypeFields();
}
/// Switch to login/enter MPIN screen
void switchToEnterMPIN() {
logSafe("switchToEnterMPIN called");
isNewUser.value = false;
isChangeMpin.value = false;
clearFields();
clearRetypeFields();
}
/// Show error snackbar
void _showError(String message) {
logSafe("ERROR: $message", level: LogLevel.error);
showAppSnackbar(
title: "Error",
message: message,
type: SnackbarType.error,
);
}
/// Navigate to dashboard
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());
}
/// Clear the primary MPIN fields
void clearFields() {
logSafe("clearFields called");
for (final c in digitControllers) {
c.clear();
}
focusNodes.first.requestFocus();
}
/// Clear the retype MPIN fields
void clearRetypeFields() {
logSafe("clearRetypeFields called");
for (final c in retypeControllers) {
c.clear();
}
retypeFocusNodes.first.requestFocus();
}
/// Cleanup
@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();
}
/// Generate MPIN for new user/change MPIN
Future<bool> 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) {
return true;
} else {
logSafe("MPIN generation returned error: $response",
level: LogLevel.warning);
showAppSnackbar(
title: "MPIN Operation 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 process MPIN.");
return false;
}
}
/// Verify MPIN for existing user
Future<void> verifyMPIN() async {
logSafe("verifyMPIN triggered");
final enteredMPIN = digitControllers.map((c) => c.text).join();
logSafe("Entered MPIN: $enteredMPIN");
if (enteredMPIN.length < 4) {
_showError("Please enter all 4 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;
// ✅ Fetch FCM Token here (DISABLED)
// final fcmToken = await FirebaseNotificationService().getFcmToken();
final response = await AuthService.verifyMpin(
mpin: enteredMPIN,
mpinToken: mpinToken,
// fcmToken: fcmToken ?? '', // 🔴 Commented out
fcmToken: '', // ✅ Passing empty string instead
);
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,
);
}
}
/// Increment failed attempts and warn
void onInvalidMPIN() {
failedAttempts.value++;
if (failedAttempts.value >= 3) {
showAppSnackbar(
title: "Error",
message: "Too many failed attempts. Consider logging in again.",
type: SnackbarType.error,
);
}
}
}