import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../data/repositories/auth_repository.dart'; import '../../../data/models/user_model.dart'; import '../../../data/services/biometric_service.dart'; import '../../../core/services/analytics_service.dart'; import '../../../bootstrap/supabase_client.dart'; import '../../../core/utils/unit_conversion_utils.dart'; import 'package:local_auth/local_auth.dart' as local_auth; final authControllerProvider = StateNotifierProvider((ref) { return AuthController(ref.read(authRepositoryProvider)); }); final authRepositoryProvider = Provider((ref) { return AuthRepository(supabaseClient); }); class AuthController extends StateNotifier { final AuthRepository _authRepository; final BiometricService _biometricService = BiometricService(); final AnalyticsService _analytics = AnalyticsService(); AuthController(this._authRepository) : super(null) { _init(); } void _init() { state = _authRepository.currentUser; _authRepository.authStateChanges.listen((user) { state = user; if (user != null) { _analytics.setUserId(user.id); } }); } bool get isAuthenticated => _authRepository.isAuthenticated; String? get currentUserId => _authRepository.currentUserId; Future isSessionValid() async { return await _authRepository.isSessionValid(); } Future refreshSession() async { await _authRepository.refreshSession(); } Future signInWithEmail(String email, String password) async { await _authRepository.signInWithEmail(email, password); _analytics.logSignIn(method: 'email'); } Future signUpWithEmail(String email, String password, String username, {double? heightCm, double? weightKg, int? age, Gender? gender, HeightUnit? heightUnit, WeightUnit? weightUnit}) async { await _authRepository.signUpWithEmail(email, password, username, heightCm: heightCm, weightKg: weightKg, age: age, gender: gender, heightUnit: heightUnit, weightUnit: weightUnit); _analytics.logSignUp(method: 'email'); } Future signInWithGoogle() async { await _authRepository.signInWithGoogle(); _analytics.logSignIn(method: 'google'); } Future signInWithGithub() async { await _authRepository.signInWithGithub(); _analytics.logSignIn(method: 'github'); } Future signOut() async { await _authRepository.signOut(); state = null; _analytics.logSignOut(); _analytics.reset(); } Future resetPassword(String email) async { await _authRepository.resetPassword(email); } Future updateProfile({ String? username, String? bio, String? avatarUrl, bool? isPublicProfile, double? heightCm, double? weightKg, int? age, Gender? gender, HeightUnit? heightUnit, WeightUnit? weightUnit, }) async { final updatedFields = []; if (username != null) updatedFields.add('username'); if (bio != null) updatedFields.add('bio'); if (avatarUrl != null) updatedFields.add('avatar'); if (isPublicProfile != null) { updatedFields.add('visibility'); _analytics.logProfileVisibilityChanged(isPublic: isPublicProfile); } if (heightCm != null) updatedFields.add('height'); if (weightKg != null) updatedFields.add('weight'); if (age != null) updatedFields.add('age'); if (gender != null) updatedFields.add('gender'); if (heightUnit != null) updatedFields.add('height_unit'); if (weightUnit != null) updatedFields.add('weight_unit'); await _authRepository.updateProfile( username: username, bio: bio, avatarUrl: avatarUrl, isPublicProfile: isPublicProfile, heightCm: heightCm, weightKg: weightKg, age: age, gender: gender, heightUnit: heightUnit, weightUnit: weightUnit, ); if (updatedFields.isNotEmpty) { _analytics.logProfileUpdated(fieldsUpdated: updatedFields.join(',')); } } // Biometric Authentication Methods /// Check if biometric authentication is available Future checkBiometricAvailability() async { return await _biometricService.checkAvailability(); } /// Check if biometric login is enabled Future isBiometricEnabled() async { return await _biometricService.isBiometricEnabled(); } /// Enable biometric login for current user Future enableBiometric() async { final userId = currentUserId; if (userId == null) return false; final success = await _biometricService.enableBiometric(userId); if (success) { _analytics.logEvent('biometric_enabled'); } return success; } /// Disable biometric login Future disableBiometric() async { final success = await _biometricService.disableBiometric(); if (success) { _analytics.logEvent('biometric_disabled'); } return success; } /// Authenticate with biometrics and sign in Future signInWithBiometric() async { try { // Check if biometric is enabled final isEnabled = await _biometricService.isBiometricEnabled(); if (!isEnabled) { return false; } // Get the stored user ID final biometricUserId = await _biometricService.getBiometricUserId(); if (biometricUserId == null) { return false; } // Authenticate with biometrics final authenticated = await _biometricService.authenticate( reason: 'Sign in to your 1356 day challenge', localizedReason: 'Use your biometric to quickly access your challenge', ); if (authenticated) { // Try to restore session for the stored user await _authRepository.refreshSession(); // Verify the current user matches the stored biometric user final currentUser = _authRepository.currentUser; final currentUserId = currentUser?.id; if (currentUserId == biometricUserId) { _analytics.logSignIn(method: 'biometric'); return true; } else { // User mismatch, disable biometric await disableBiometric(); return false; } } return false; } catch (e) { return false; } } /// Get biometric status message Future getBiometricStatusMessage() async { return await _biometricService.getBiometricStatusMessage(); } /// Get available biometric types Future> getAvailableBiometrics() async { return await _biometricService.getAvailableBiometrics(); } /// Get primary biometric type Future getPrimaryBiometricType() async { return await _biometricService.getPrimaryBiometricType(); } @override void dispose() { _authRepository.dispose(); super.dispose(); } }