import 'package:flutter/material.dart'; import '../../widgets/password_reset_form.dart'; import '../../../../core/errors/auth_exceptions.dart'; import '../providers/auth_provider.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; /// Password reset request page where users can initiate password recovery class ResetPasswordPage extends ConsumerStatefulWidget { const ResetPasswordPage({super.key}); @override ConsumerState createState() => _ResetPasswordPageState(); } class _ResetPasswordPageState extends ConsumerState { final _formKey = GlobalKey(); final _emailController = TextEditingController(); bool _isLoading = false; String? _errorMessage; String? _successMessage; bool _emailSent = false; bool _canResendAfter = false; DateTime? _lastAttemptTime; @override void dispose() { _emailController.dispose(); super.dispose(); } /// Handles email input changes and clears errors void _handleEmailChanged(String value) { setState(() { _errorMessage = null; _successMessage = null; }); } Future _handlePasswordReset(String email) async { if (!_formKey.currentState!.validate()) return; // Check for empty email final trimmedEmail = email.trim(); if (trimmedEmail.isEmpty) { setState(() { _errorMessage = 'Please enter your email address'; }); return; } setState(() { _isLoading = true; _errorMessage = null; _successMessage = null; }); try { final authProvider = ref.read(authProvider.notifier); await authProvider.resetPassword(trimmedEmail); if (mounted) { setState(() { _isLoading = false; _emailSent = true; _successMessage = 'Password reset email sent! Check your inbox for further instructions.'; }); // Announce success for accessibility _announceMessageForAccessibility(_successMessage!); } } catch (e) { if (mounted) { setState(() { _isLoading = false; _errorMessage = _mapErrorToUserFriendlyMessage(e); }); // Announce error for accessibility _announceErrorForAccessibility(_errorMessage!); } } } String _mapErrorToUserFriendlyMessage(Object error) { if (error is UserNotFoundException) { return 'No account found with this email address. Please check the email or sign up for a new account.'; } else if (error is TooManyRequestsException) { return 'Too many reset attempts. Please wait 15 minutes before trying again, or contact support.'; } else if (error is NetworkException) { return 'Network connection failed. Please check your internet connection and try again.'; } else if (error is AuthDisabledException) { return 'Password reset is currently disabled. Please try again later or contact support.'; } else if (error is InvalidTokenException) { return 'Reset request failed. Please start the password reset process again.'; } else if (error is AuthException) { return error.message; } else { return 'An unexpected error occurred. Please try again or contact support if the problem persists.'; } } /// Announces success message for screen readers void _announceMessageForAccessibility(String message) { SemanticsService.announce(message, TextDirection.ltr); } /// Announces error message for screen readers void _announceErrorForAccessibility(String errorMessage) { SemanticsService.announce(errorMessage, TextDirection.ltr); } void _navigateToLogin() { Navigator.of(context).pushReplacementNamed('/login'); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Scaffold( appBar: AppBar( title: const Text('Reset Password'), backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back), onPressed: _navigateToLogin, tooltip: 'Back to Login', ), ), body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 32), _buildHeader(), const SizedBox(height: 32), if (!_emailSent) ...[ _buildInstructions(), const SizedBox(height: 32), PasswordResetForm( formKey: _formKey, emailController: _emailController, isLoading: _isLoading, errorMessage: _errorMessage, onEmailChanged: _handleEmailChanged, onSubmit: _handlePasswordReset, ), ] else ...[ _buildSuccessMessage(), const SizedBox(height: 32), _buildBackToLoginButton(), ], ], ), ), ), ); } Widget _buildHeader() { return Column( children: [ Icon( Icons.lock_reset_outlined, size: 64, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 16), Text( 'Forgot Password?', style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, ), ), ], ); } Widget _buildInstructions() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceVariant, borderRadius: BorderRadius.circular(12), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.info_outline, size: 20, color: Theme.of(context).colorScheme.onSurfaceVariant, ), const SizedBox(width: 8), Expanded( child: Text( 'Enter your email address below and we\'ll send you a link to reset your password.', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ), ], ), ], ), ); } Widget _buildSuccessMessage() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).colorScheme.primaryContainer, borderRadius: BorderRadius.circular(12), ), child: Column( children: [ Icon( Icons.mark_email_read_outlined, size: 48, color: Theme.of(context).colorScheme.onPrimaryContainer, ), const SizedBox(height: 16), Text( 'Check Your Email', style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), const SizedBox(height: 8), Text( _successMessage ?? 'We\'ve sent password reset instructions to your email address.', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onPrimaryContainer, ), textAlign: TextAlign.center, ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Next Steps:', style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( '1. Open your email inbox\n' '2. Look for password reset email (check spam folder)\n' '3. Click reset link in the email\n' '4. Create a new password\n' '5. Return to sign in with your new password', style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: 8), Text( 'Didn\'t receive the email? Check your spam folder or try again in a few minutes.', style: Theme.of(context).textTheme.bodySmall?.copyWith( fontStyle: FontStyle.italic, ), ), ], ), ), const SizedBox(height: 16), _buildResendSection(), ], ), ); } Widget _buildResendSection() { return Column( children: [ Text( "Didn't receive the email?", style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), const SizedBox(height: 8), TextButton( onPressed: _canResendAfter ? _handleResendEmail : null, style: TextButton.styleFrom( foregroundColor: _canResendAfter ? Theme.of(context).colorScheme.onPrimaryContainer : Theme.of(context).colorScheme.onPrimaryContainer.withOpacity(0.5), ), child: Text( _canResendAfter ? 'Resend Email' : 'Resend Email (available soon)', ), ), ], ); } void _handleResendEmail() { if (_canResendAfter && _emailController.text.isNotEmpty) { _handlePasswordReset(_emailController.text); } } Widget _buildBackToLoginButton() { return OutlinedButton( onPressed: _navigateToLogin, style: OutlinedButton.styleFrom( minimumSize: const Size(double.infinity, 48), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), child: const Text('Back to Login'), ); } }