import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../core/api/api_client.dart'; import '../core/api/api_exception.dart'; import '../core/auth/auth_state.dart'; import '../core/storage/secure_storage.dart'; import '../repositories/auth_repository.dart'; // Core Service Providers final secureStorageProvider = Provider((ref) { return SecureStorage(); }); final apiClientProvider = Provider((ref) { final storage = ref.watch(secureStorageProvider); return ApiClient(storage: storage); }); final authRepositoryProvider = Provider((ref) { final apiClient = ref.watch(apiClientProvider); return AuthRepository(apiClient: apiClient); }); // Auth State Notifier class AuthNotifier extends StateNotifier { final AuthRepository _authRepository; final SecureStorage _storage; final ApiClient _apiClient; AuthNotifier({ required AuthRepository authRepository, required SecureStorage storage, required ApiClient apiClient, }) : _authRepository = authRepository, _storage = storage, _apiClient = apiClient, super(const AuthState(status: AuthStatus.initial)) { // Hook into session expiration cleanly without cyclic provider dependency _apiClient.onSessionExpired = handleSessionExpired; } /// Checks if a valid session exists in storage on app startup. Future restoreSession() async { state = AuthState.loading(); try { final sessionCookie = await _storage.getSessionCookie(); if (sessionCookie == null || sessionCookie.isEmpty) { state = AuthState.unauthenticated(); return; } // Validate session with backend final authResponse = await _authRepository.getMe(); state = AuthState.authenticated( user: authResponse.user, student: authResponse.student, ); } catch (e) { // If server returns AUTH_REQUIRED or network fails, unauthenticate await _storage.clearSessionCookie(); state = AuthState.unauthenticated(); } } Future login(String email, String password) async { state = AuthState.loading(); try { final authResponse = await _authRepository.login( email: email, password: password, ); state = AuthState.authenticated( user: authResponse.user, student: authResponse.student, ); } on ApiException catch (e) { state = AuthState.unauthenticated(e.message); rethrow; } catch (e) { state = AuthState.unauthenticated('An unexpected error occurred during login.'); rethrow; } } Future logout() async { try { await _authRepository.logout(); } catch (_) { // Even if network logout fails, clear local session } finally { await _storage.clearSessionCookie(); state = AuthState.unauthenticated(); } } void handleSessionExpired() { state = AuthState.unauthenticated('Your session has expired. Please login again.'); } } final authProvider = StateNotifierProvider((ref) { final authRepo = ref.watch(authRepositoryProvider); final storage = ref.watch(secureStorageProvider); final client = ref.watch(apiClientProvider); return AuthNotifier( authRepository: authRepo, storage: storage, apiClient: client, ); });