import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../api/api_client.dart'; import '../config/app_config.dart'; import '../config/secure_storage_config.dart'; import '../../features/auth/domain/auth_user.dart'; import 'auth_repository.dart'; /// État d'authentification de l'application. class AuthState { const AuthState({ this.user, this.accessToken, this.refreshToken, this.isLoading = false, this.error, }); final AuthUser? user; final String? accessToken; final String? refreshToken; final bool isLoading; final String? error; bool get isAuthenticated => accessToken != null && accessToken!.isNotEmpty; AuthState copyWith({ AuthUser? user, String? accessToken, String? refreshToken, bool? isLoading, String? error, bool clearError = false, bool clearUser = false, }) { return AuthState( user: clearUser ? null : (user ?? this.user), accessToken: accessToken ?? this.accessToken, refreshToken: refreshToken ?? this.refreshToken, isLoading: isLoading ?? this.isLoading, error: clearError ? null : (error ?? this.error), ); } } /// Gestionnaire d'état Riverpod pour l'authentification. class AuthNotifier extends StateNotifier { AuthNotifier(this._repository) : super(const AuthState(isLoading: true)) { _restoreSession(); } final AuthRepository _repository; /// Tente de renouveler le token d'accès (appelé par le client API sur 401). Future refreshAccessToken() async { final refreshed = await _repository.refresh(); if (refreshed == null) { return null; } state = state.copyWith( accessToken: refreshed.accessToken, refreshToken: refreshed.refreshToken, user: refreshed.user ?? state.user, ); return refreshed.accessToken; } /// Session expirée après échec du refresh. Future sessionExpired() async { await logout(); } Future _restoreSession() async { state = state.copyWith(isLoading: true, clearError: true); try { final stored = await _repository.loadStoredTokens(); if (stored == null) { state = state.copyWith(isLoading: false); return; } state = state.copyWith( accessToken: stored.accessToken, refreshToken: stored.refreshToken, ); final user = await _repository.fetchCurrentUser(); state = state.copyWith(user: user, isLoading: false); } catch (_) { final refreshed = await _repository.refresh(); if (refreshed != null) { final user = await _repository.fetchCurrentUser(); state = state.copyWith( accessToken: refreshed.accessToken, refreshToken: refreshed.refreshToken, user: user, isLoading: false, ); } else { await _repository.clearStoredTokens(); state = const AuthState(isLoading: false); } } } Future login(String email, String password) async { state = state.copyWith(isLoading: true, clearError: true); try { final tokens = await _repository.login(email: email, password: password); final user = tokens.user ?? await _repository.fetchCurrentUser(); state = state.copyWith( accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, user: user, isLoading: false, ); return true; } catch (e) { state = state.copyWith( isLoading: false, error: 'Connexion impossible. Vérifiez vos identifiants.', ); return false; } } Future register({ required String firstName, required String lastName, required String email, required String password, String? phone, }) async { state = state.copyWith(isLoading: true, clearError: true); try { final tokens = await _repository.register( firstName: firstName, lastName: lastName, email: email, password: password, phone: phone, ); final user = tokens.user ?? await _repository.fetchCurrentUser(); state = state.copyWith( accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, user: user, isLoading: false, ); return true; } catch (e) { state = state.copyWith( isLoading: false, error: 'Inscription impossible. L\'email est peut-être déjà utilisé.', ); return false; } } Future logout() async { await _repository.logout(); state = const AuthState(); } } final authRepositoryProvider = Provider((ref) { final storage = laverieSecureStorage; // Token lu depuis le stockage sécurisé (évite la dépendance circulaire avec authProvider). return AuthRepository( apiClient: ApiClient( baseUrl: kApiBaseUrl, getAccessToken: () => storage.read(key: AuthStorageKeys.accessToken), ), secureStorage: storage, ); }); final authProvider = StateNotifierProvider((ref) { return AuthNotifier(ref.watch(authRepositoryProvider)); });