import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../../core/network/dio_client.dart'; import '../../../../core/storage/secure_storage.dart'; import '../../data/datasources/auth_remote_datasource.dart'; import '../../data/repositories/auth_repository_impl.dart'; import '../../domain/entities/user.dart'; import '../../domain/repositories/auth_repository.dart'; part 'auth_provider.g.dart'; /// Provider for DioClient (singleton) @Riverpod(keepAlive: true) DioClient dioClient(Ref ref) { return DioClient(); } /// Provider for SecureStorage (singleton) @Riverpod(keepAlive: true) SecureStorage secureStorage(Ref ref) { return SecureStorage(); } /// Provider for AuthRemoteDataSource @Riverpod(keepAlive: true) AuthRemoteDataSource authRemoteDataSource(Ref ref) { final dioClient = ref.watch(dioClientProvider); return AuthRemoteDataSourceImpl(dioClient: dioClient); } /// Provider for AuthRepository @Riverpod(keepAlive: true) AuthRepository authRepository(Ref ref) { final remoteDataSource = ref.watch(authRemoteDataSourceProvider); final secureStorage = ref.watch(secureStorageProvider); final dioClient = ref.watch(dioClientProvider); return AuthRepositoryImpl( remoteDataSource: remoteDataSource, secureStorage: secureStorage, dioClient: dioClient, ); } /// Auth state class class AuthState { final User? user; final bool isAuthenticated; final bool isLoading; final String? errorMessage; const AuthState({ this.user, this.isAuthenticated = false, this.isLoading = false, this.errorMessage, }); AuthState copyWith({ User? user, bool? isAuthenticated, bool? isLoading, String? errorMessage, bool clearUser = false, bool clearError = false, }) { return AuthState( user: clearUser ? null : (user ?? this.user), isAuthenticated: isAuthenticated ?? this.isAuthenticated, isLoading: isLoading ?? this.isLoading, errorMessage: clearError ? null : (errorMessage ?? this.errorMessage), ); } } /// Auth state notifier provider @Riverpod(keepAlive: true) class Auth extends _$Auth { @override AuthState build() { // Don't call async operations in build // Use a separate method to initialize auth state return const AuthState(); } AuthRepository get _repository => ref.read(authRepositoryProvider); /// Initialize auth state - call this on app start Future initialize() async { state = state.copyWith(isLoading: true); final isAuthenticated = await _repository.isAuthenticated(); if (isAuthenticated) { // Get user profile final result = await _repository.getProfile(); result.fold( (failure) { state = const AuthState( isAuthenticated: false, isLoading: false, ); }, (user) { state = AuthState( user: user, isAuthenticated: true, isLoading: false, ); }, ); } else { state = const AuthState( isAuthenticated: false, isLoading: false, ); } } /// Login user Future login({ required String email, required String password, }) async { state = state.copyWith(isLoading: true, clearError: true); final result = await _repository.login(email: email, password: password); return result.fold( (failure) { state = state.copyWith( isAuthenticated: false, isLoading: false, errorMessage: failure.message, ); return false; }, (authResponse) { state = AuthState( user: authResponse.user, isAuthenticated: true, isLoading: false, errorMessage: null, ); return true; }, ); } /// Register new user Future register({ required String name, required String email, required String password, List roles = const ['user'], }) async { state = state.copyWith(isLoading: true, clearError: true); final result = await _repository.register( name: name, email: email, password: password, roles: roles, ); return result.fold( (failure) { state = state.copyWith( isAuthenticated: false, isLoading: false, errorMessage: failure.message, ); return false; }, (authResponse) { state = AuthState( user: authResponse.user, isAuthenticated: true, isLoading: false, errorMessage: null, ); return true; }, ); } /// Get user profile (refresh user data) Future getProfile() async { state = state.copyWith(isLoading: true, clearError: true); final result = await _repository.getProfile(); result.fold( (failure) { state = state.copyWith( isLoading: false, errorMessage: failure.message, ); }, (user) { state = state.copyWith( user: user, isAuthenticated: true, isLoading: false, ); }, ); } /// Refresh access token Future refreshToken() async { final result = await _repository.refreshToken(); return result.fold( (failure) { // If token refresh fails, logout user logout(); return false; }, (authResponse) { state = state.copyWith(user: authResponse.user); return true; }, ); } /// Logout user Future logout() async { state = state.copyWith(isLoading: true); await _repository.logout(); state = const AuthState( isAuthenticated: false, isLoading: false, ); } } /// Current authenticated user provider @riverpod User? currentUser(Ref ref) { final authState = ref.watch(authProvider); return authState.user; } /// Is authenticated provider @riverpod bool isAuthenticated(Ref ref) { final authState = ref.watch(authProvider); return authState.isAuthenticated; }