import 'package:flutter/material.dart'; import 'package:tm_app/data/services/model/customer/customer.dart'; import 'package:tm_app/data/services/model/forgot_password_response/forgot_password_response_model.dart'; import 'package:tm_app/data/services/model/verify_otp_response/verify_otp_response_model.dart'; import '../core/utils/result.dart'; import '../data/repositories/auth_repository.dart'; import '../data/services/model/login_response/login_response_model.dart'; import '../data/services/model/logout_response/logout_response.dart'; import '../data/services/model/reset_password_response/reset_password_response.dart'; class AuthViewModel with ChangeNotifier { final AuthRepository _authRepository; AuthViewModel({required AuthRepository authRepository}) : _authRepository = authRepository { usernameController.addListener(_onTextChanged); passwordController.addListener(_onTextChanged); emailController.addListener(_onTextChanged); otpController.addListener(_onTextChanged); newPasswordController.addListener(_onTextChanged); } final TextEditingController usernameController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); final TextEditingController emailController = TextEditingController(); final TextEditingController newPasswordController = TextEditingController(); final TextEditingController otpController = TextEditingController(); final FocusNode usernameFocus = FocusNode(); final FocusNode passwordFocus = FocusNode(); final FocusNode emailFocus = FocusNode(); final FocusNode newPasswordFocus = FocusNode(); final FocusNode otpFocus = FocusNode(); bool _obscurePassword = true; bool get obscurePassword => _obscurePassword; bool _obscureForgotPassword = true; bool get obscureForgotPassword => _obscureForgotPassword; bool get isLoginEnabled => usernameController.text.isNotEmpty && passwordController.text.isNotEmpty; // Auth state bool _isLoading = false; bool _isLoggedIn = false; bool _isResetPasswordLoading = false; String? _errorMessage; String? _resetPasswordErrorMessage; bool _resetPasswordSuccess = false; Customer? _loggedInUser; String? successMessage; String? _successMessageOtp; String? _forgotPasswordMessage; bool _isLoadingForgot = false; bool _isLoadingOtp = false; String? _errorMessageForget; String? _errorMessageOtp; bool _verifyOtpSuccess = false; bool get isLoading => _isLoading; bool get isLoggedIn => _isLoggedIn; bool get isLoadingForgot => _isLoadingForgot; bool get isLoadingOtp => _isLoadingOtp; bool get isResetPasswordLoading => _isResetPasswordLoading; String? get errorMessage => _errorMessage; String? get errorMessageForget => _errorMessageForget; String? get resetPasswordErrorMessage => _resetPasswordErrorMessage; String? get errorMessageOtp => _errorMessageOtp; String? get successMessageOtp => _successMessageOtp; bool get verifyOtpSuccess => _verifyOtpSuccess; Customer? get loggedInUser => _loggedInUser; String? get forgotPasswordMessage => _forgotPasswordMessage; String? resetToken; bool get resetPasswordSuccess => _resetPasswordSuccess; void togglePasswordVisibility() { _obscurePassword = !_obscurePassword; notifyListeners(); } void toggleForgotPasswordVisibility() { _obscureForgotPassword = !_obscureForgotPassword; notifyListeners(); } bool get isEmailValid { final email = emailController.text.trim(); final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); return emailRegex.hasMatch(email); } bool get isOtpValid { final otp = otpController.text.trim(); return otp.length == 6; } bool get canSubmitOtpPage { return isOtpValid; } bool get isNewPasswordValid { final password = newPasswordController.text.trim(); final hasMinLength = password.length >= 5; final hasUppercase = RegExp(r'[A-Z]').hasMatch(password); final hasLowercase = RegExp(r'[a-z]').hasMatch(password); final hasSpecialChar = RegExp(r'[!@#\$%&\*\^]').hasMatch(password); return hasMinLength && hasUppercase && hasLowercase && hasSpecialChar; } void _onTextChanged() { notifyListeners(); } Future login() async { _isLoading = true; _errorMessage = null; notifyListeners(); await localLogout(); final result = await _authRepository.login( username: usernameController.text.trim(), password: passwordController.text.trim(), ); switch (result) { case Ok(): _loggedInUser = result.value.data.customer; usernameController.clear(); passwordController.clear(); break; case Error(): _errorMessage = result.error.toString(); break; } _isLoading = false; notifyListeners(); // reset error after notify _errorMessage = null; notifyListeners(); } Future localLogout() async { await _authRepository.localLogout(); _loggedInUser = null; notifyListeners(); } Future logout() async { _errorMessage = null; notifyListeners(); final result = await _authRepository.logout(); switch (result) { case Ok(): localLogout(); break; case Error(): _errorMessage = result.error.toString(); break; } notifyListeners(); _errorMessage = null; notifyListeners(); } Future forgotPassword(BuildContext context) async { _isLoadingForgot = true; _errorMessageForget = null; successMessage = null; notifyListeners(); final result = await _authRepository.forgotPassword( email: emailController.text.trim(), ); switch (result) { case Ok(): _isLoadingForgot = false; successMessage = result.value.message; notifyListeners(); break; case Error(): _isLoadingForgot = false; _errorMessageForget = result.error.toString(); notifyListeners(); Future.microtask(() { _errorMessageForget = null; }); break; } } void clearMessages() { successMessage = null; _errorMessage = null; notifyListeners(); } Future verifyOtp(BuildContext context) async { _isLoadingOtp = true; _errorMessageOtp = null; _verifyOtpSuccess = false; notifyListeners(); final result = await _authRepository.verifyOtp( code: otpController.text.trim(), email: emailController.text.trim(), ); switch (result) { case Ok(): _isLoadingOtp = false; resetToken = result.value.data.token; _successMessageOtp = result.value.message; _verifyOtpSuccess = true; notifyListeners(); break; case Error(): _isLoadingOtp = false; _errorMessageOtp = result.error.toString(); _verifyOtpSuccess = false; notifyListeners(); break; } _errorMessageOtp = null; _successMessageOtp = null; notifyListeners(); } void clearOtpFlow() { _errorMessageOtp = null; _successMessageOtp = null; _isLoadingOtp = false; _verifyOtpSuccess = false; // Intentionally do not clear resetToken otpController.clear(); notifyListeners(); } Future resetPassword() async { _isResetPasswordLoading = true; _resetPasswordErrorMessage = null; _resetPasswordSuccess = false; notifyListeners(); if (resetToken != null) { final result = await _authRepository.resetPassword( newPassword: newPasswordController.text.trim(), token: resetToken!, ); switch (result) { case Ok(): _resetPasswordSuccess = true; break; case Error(): _resetPasswordErrorMessage = result.error.toString(); _resetPasswordSuccess = false; break; } _isResetPasswordLoading = false; notifyListeners(); _resetPasswordErrorMessage = null; notifyListeners(); } else { _resetPasswordErrorMessage = 'Reset token is required'; _isResetPasswordLoading = false; notifyListeners(); } } Future checkIsLoggedIn() async { final result = await _authRepository.checkIsLoggedIn(); if (result is Ok) { if (result.value) { _isLoggedIn = result.value; } else { _isLoggedIn = false; } } else { _isLoggedIn = false; } notifyListeners(); } void clearResetPasswordSuccess() { _resetPasswordSuccess = false; notifyListeners(); } void clearResetFlow() { _resetPasswordErrorMessage = null; _resetPasswordSuccess = false; _isResetPasswordLoading = false; // Intentionally do not clear resetToken newPasswordController.clear(); notifyListeners(); } @override void dispose() { usernameController.dispose(); passwordController.dispose(); emailController.dispose(); newPasswordController.dispose(); otpController.dispose(); usernameFocus.dispose(); passwordFocus.dispose(); emailFocus.dispose(); newPasswordFocus.dispose(); otpFocus.dispose(); super.dispose(); } }