import 'package:firebase_auth/firebase_auth.dart' as firebase_auth; import 'package:cloud_firestore/cloud_firestore.dart'; import '../models/user.dart'; import '../services/auth_service.dart'; import '../services/error_service.dart'; import '../services/notification_service.dart'; /// Repository for authentication operations and user data management. /// /// This repository handles authentication logic and user data persistence /// by coordinating between Firebase Auth and Firestore. It provides a /// clean interface for authentication operations while managing the /// relationship between Firebase users and application user data. class AuthRepository { /// Authentication service for Firebase Auth operations. final AuthService _authService; /// Firestore instance for user data operations. final FirebaseFirestore _firestore; /// Error service for logging authentication errors. final _errorService = ErrorService(); /// Creates a new [AuthRepository] with optional service dependencies. /// /// If [authService] or [firestore] are not provided, default instances will be used. AuthRepository({AuthService? authService, FirebaseFirestore? firestore}) : _authService = authService ?? AuthService(), _firestore = firestore ?? FirebaseFirestore.instance; /// Stream of authentication state changes. /// /// Emits Firebase user objects when authentication state changes. Stream get authStateChanges => _authService.authStateChanges; /// Gets the currently authenticated Firebase user. /// /// Returns null if no user is currently authenticated. firebase_auth.User? get currentUser => _authService.currentUser; /// Signs in a user with email and password. /// /// Authenticates with Firebase Auth and retrieves the corresponding /// user data from Firestore. /// /// [email] - User's email address /// [password] - User's password /// /// Returns the [User] model if successful, null if user data not found. /// Throws an exception if authentication fails. Future signInWithEmailAndPassword({ required String email, required String password, }) async { try { final firebaseUser = await _authService.signInWithEmailAndPassword( email: email, password: password, ); await _saveFCMToken(firebaseUser.user!.uid); return await getUserFromFirestore(firebaseUser.user!.uid); } catch (e) { _errorService.showError(message: 'Utilisateur ou mot de passe incorrect'); } return null; } /// Creates a new user account with email and password. /// /// Creates a Firebase Auth account and stores additional user information /// in Firestore. /// /// [email] - User's email address /// [password] - User's password /// [nom] - User's last name /// [prenom] - User's first name /// [phoneNumber] - User's phone number /// /// Returns the created [User] model if successful. /// Throws an exception if account creation fails. Future signUpWithEmailAndPassword({ required String email, required String password, required String nom, required String prenom, required String phoneNumber, }) async { try { final firebaseUser = await _authService.signUpWithEmailAndPassword( email: email, password: password, ); // Create user document in Firestore with additional information final user = User( id: firebaseUser.user!.uid, email: email, nom: nom, prenom: prenom, phoneNumber: phoneNumber, platform: 'email', profilePictureUrl: '', ); await _firestore.collection('users').doc(user.id).set(user.toMap()); if (user.id != null) { await _saveFCMToken(user.id!); } return user; } catch (e) { _errorService.showError(message: 'Erreur lors de la création du compte'); } return null; } /// Signs in a user using Google authentication. /// /// Handles Google sign-in flow and creates/retrieves user data from Firestore. /// If the user doesn't exist, creates a new user document. /// /// Returns the [User] model if successful, null if Google sign-in was cancelled. /// Throws an exception if authentication fails. Future signUpWithGoogle( String phoneNumber, String name, String firstname, ) async { try { firebase_auth.User? firebaseUser = _authService.currentUser; if (firebaseUser == null) { final userCredential = await _authService.signInWithGoogle(); firebaseUser = userCredential.user; } if (firebaseUser != null) { // Check if user already exists in Firestore final existingUser = await getUserFromFirestore(firebaseUser.uid); if (existingUser != null) { return existingUser; } // Create new user document for first-time Google sign-in final user = User( id: firebaseUser.uid, email: firebaseUser.email ?? '', nom: name, prenom: firstname, phoneNumber: phoneNumber, profilePictureUrl: firebaseUser.photoURL ?? 'Unknown', platform: 'google', ); await _firestore.collection('users').doc(user.id).set(user.toMap()); if (user.id != null) { await _saveFCMToken(user.id!); } return user; } return null; } catch (e) { _errorService.showError(message: 'Erreur lors de la connexion Google'); } return null; } Future signInWithGoogle() async { try { final firebaseUser = await _authService.signInWithGoogle(); final user = await getUserFromFirestore(firebaseUser.user!.uid); if (user != null) { if (user.id != null) { await _saveFCMToken(user.id!); } return user; } else { throw Exception('Utilisateur non trouvé'); } } catch (e) { _errorService.showError(message: 'Erreur lors de la connexion Google'); } return null; } /// Signs in a user using Apple authentication. /// /// Handles Apple sign-in flow and creates/retrieves user data from Firestore. /// If the user doesn't exist, creates a new user document. /// /// Returns the [User] model if successful, null if Apple sign-in was cancelled. /// Throws an exception if authentication fails. Future signUpWithApple( String phoneNumber, String name, String firstname, ) async { try { firebase_auth.User? firebaseUser = _authService.currentUser; if (firebaseUser == null) { final userCredential = await _authService.signInWithApple(); firebaseUser = userCredential.user; } if (firebaseUser != null) { final existingUser = await getUserFromFirestore(firebaseUser.uid); if (existingUser != null) { return existingUser; } final user = User( id: firebaseUser.uid, email: firebaseUser.email ?? '', nom: name, prenom: firstname, phoneNumber: phoneNumber, profilePictureUrl: firebaseUser.photoURL ?? 'Unknown', platform: 'apple', ); await _firestore.collection('users').doc(user.id).set(user.toMap()); if (user.id != null) { await _saveFCMToken(user.id!); } return user; } return null; } catch (e) { _errorService.showError(message: 'Erreur lors de la connexion Apple'); } return null; } Future signInWithApple() async { try { final firebaseUser = await _authService.signInWithApple(); final user = await getUserFromFirestore(firebaseUser.user!.uid); if (user != null) { if (user.id != null) { await _saveFCMToken(user.id!); } return user; } else { throw Exception('Utilisateur non trouvé'); } } catch (e) { _errorService.showError(message: 'Erreur lors de la connexion Apple'); } return null; } /// Signs out the current user. /// /// Clears the authentication state and signs out from Firebase Auth. Future signOut() async { await _authService.signOut(); } /// Sends a password reset email to the specified email address. /// /// [email] - The email address to send the reset link to /// /// Throws an exception if the operation fails. Future resetPassword(String email) async { await _authService.resetPassword(email); } /// Retrieves user data from Firestore by user ID. /// /// Fetches the user document from the 'users' collection and converts /// it to a [User] model. /// /// [uid] - The Firebase user ID to look up /// /// Returns the [User] model if successful, null otherwise. Future getUserFromFirestore(String uid) async { try { final doc = await _firestore.collection('users').doc(uid).get(); if (doc.exists) { final data = doc.data() as Map; return User.fromMap({...data, 'id': uid}); } return null; } catch (e) { return null; } } /// Helper method to save the FCM token for the authenticated user. Future _saveFCMToken(String userId) async { try { final token = await NotificationService().getFCMToken(); if (token != null) { await _firestore.collection('users').doc(userId).set({ 'fcmToken': token, }, SetOptions(merge: true)); } } catch (e) { // Non-blocking error print('Error saving FCM token: $e'); } } }