// lib/auth_provider.dart import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'dart:convert'; import 'package:environment_monitoring_app/services/api_service.dart'; /// A comprehensive provider to manage user authentication, session state, /// and cached master data for offline use. class AuthProvider with ChangeNotifier { final ApiService _apiService = ApiService(); final DatabaseHelper _dbHelper = DatabaseHelper(); // --- Session & Profile State --- String? _jwtToken; String? _userEmail; Map? _profileData; bool get isLoggedIn => _jwtToken != null; String? get userEmail => _userEmail; Map? get profileData => _profileData; // --- App State --- bool _isLoading = true; bool _isFirstLogin = true; DateTime? _lastSyncTimestamp; bool get isLoading => _isLoading; bool get isFirstLogin => _isFirstLogin; DateTime? get lastSyncTimestamp => _lastSyncTimestamp; // --- Cached Master Data --- List>? _allUsers; List>? _tarballStations; List>? _manualStations; List>? _tarballClassifications; List>? _riverManualStations; List>? _riverTriennialStations; List>? _departments; List>? _companies; List>? _positions; // --- Getters for UI access --- List>? get allUsers => _allUsers; List>? get tarballStations => _tarballStations; List>? get manualStations => _manualStations; List>? get tarballClassifications => _tarballClassifications; List>? get riverManualStations => _riverManualStations; List>? get riverTriennialStations => _riverTriennialStations; List>? get departments => _departments; List>? get companies => _companies; List>? get positions => _positions; // --- SharedPreferences Keys (made public for BaseApiService) --- static const String tokenKey = 'jwt_token'; static const String userEmailKey = 'user_email'; static const String profileDataKey = 'user_profile_data'; static const String lastSyncTimestampKey = 'last_sync_timestamp'; static const String isFirstLoginKey = 'is_first_login'; AuthProvider() { debugPrint('AuthProvider: Initializing...'); _loadSessionAndSyncData(); } /// Loads the user session from storage and then triggers a data sync. Future _loadSessionAndSyncData() async { _isLoading = true; notifyListeners(); final prefs = await SharedPreferences.getInstance(); _jwtToken = prefs.getString(tokenKey); _userEmail = prefs.getString(userEmailKey); _isFirstLogin = prefs.getBool(isFirstLoginKey) ?? true; final lastSyncMillis = prefs.getInt(lastSyncTimestampKey); if (lastSyncMillis != null) { _lastSyncTimestamp = DateTime.fromMillisecondsSinceEpoch(lastSyncMillis); } // Always load from local DB first for instant startup await _loadDataFromCache(); if (_jwtToken != null) { debugPrint('AuthProvider: Session loaded. Triggering online sync.'); // Don't await here to allow the UI to build instantly with cached data syncAllData(); } else { debugPrint('AuthProvider: No active session. App is in offline mode.'); } _isLoading = false; notifyListeners(); } /// The main function to sync all app data. It checks for an internet connection /// and fetches from the server if available, otherwise it relies on the local cache. Future syncAllData({bool forceRefresh = false}) async { final connectivityResult = await Connectivity().checkConnectivity(); if (connectivityResult != ConnectivityResult.none) { debugPrint("AuthProvider: Device is ONLINE. Fetching fresh data from server."); await _fetchDataFromServer(); } else { debugPrint("AuthProvider: Device is OFFLINE. Data is already loaded from cache."); } notifyListeners(); } /// A dedicated method to refresh only the profile. Future refreshProfile() async { final result = await _apiService.refreshProfile(); if (result['success']) { // Update the profile data in the provider state _profileData = result['data']; // Persist the updated profile data in SharedPreferences final prefs = await SharedPreferences.getInstance(); await prefs.setString(profileDataKey, jsonEncode(_profileData)); notifyListeners(); } } /// Fetches all master data from the server and caches it locally. Future _fetchDataFromServer() async { final result = await _apiService.syncAllData(); if (result['success']) { final data = result['data']; _profileData = data['profile']; _allUsers = data['allUsers'] != null ? List>.from(data['allUsers']) : null; _tarballStations = data['tarballStations'] != null ? List>.from(data['tarballStations']) : null; _manualStations = data['manualStations'] != null ? List>.from(data['manualStations']) : null; _tarballClassifications = data['tarballClassifications'] != null ? List>.from(data['tarballClassifications']) : null; _riverManualStations = data['riverManualStations'] != null ? List>.from(data['riverManualStations']) : null; _riverTriennialStations = data['riverTriennialStations'] != null ? List>.from(data['riverTriennialStations']) : null; _departments = data['departments'] != null ? List>.from(data['departments']) : null; _companies = data['companies'] != null ? List>.from(data['companies']) : null; _positions = data['positions'] != null ? List>.from(data['positions']) : null; await setLastSyncTimestamp(DateTime.now()); } } /// Loads all master data from the local cache using DatabaseHelper. Future _loadDataFromCache() async { _profileData = await _dbHelper.loadProfile(); _allUsers = await _dbHelper.loadUsers(); _tarballStations = await _dbHelper.loadTarballStations(); _manualStations = await _dbHelper.loadManualStations(); _tarballClassifications = await _dbHelper.loadTarballClassifications(); _riverManualStations = await _dbHelper.loadRiverManualStations(); _riverTriennialStations = await _dbHelper.loadRiverTriennialStations(); _departments = await _dbHelper.loadDepartments(); _companies = await _dbHelper.loadCompanies(); _positions = await _dbHelper.loadPositions(); debugPrint("AuthProvider: All master data loaded from local DB cache."); } // --- Methods for UI interaction --- /// Handles the login process, saving session data and triggering a full data sync. Future login(String token, Map profile) async { _jwtToken = token; _userEmail = profile['email']; _profileData = profile; final prefs = await SharedPreferences.getInstance(); await prefs.setString(tokenKey, token); await prefs.setString(userEmailKey, _userEmail!); await prefs.setString(profileDataKey, jsonEncode(profile)); await _dbHelper.saveProfile(profile); debugPrint('AuthProvider: Login successful. Session and profile persisted.'); await syncAllData(forceRefresh: true); } Future setProfileData(Map data) async { _profileData = data; final prefs = await SharedPreferences.getInstance(); await prefs.setString(profileDataKey, jsonEncode(data)); await _dbHelper.saveProfile(data); // Also save to local DB notifyListeners(); } Future setLastSyncTimestamp(DateTime timestamp) async { _lastSyncTimestamp = timestamp; final prefs = await SharedPreferences.getInstance(); await prefs.setInt(lastSyncTimestampKey, timestamp.millisecondsSinceEpoch); notifyListeners(); } Future setIsFirstLogin(bool value) async { _isFirstLogin = value; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(isFirstLoginKey, value); notifyListeners(); } Future logout() async { debugPrint('AuthProvider: Initiating logout...'); _jwtToken = null; _userEmail = null; _profileData = null; _lastSyncTimestamp = null; _isFirstLogin = true; _allUsers = null; _tarballStations = null; _manualStations = null; _tarballClassifications = null; _riverManualStations = null; _riverTriennialStations = null; _departments = null; _companies = null; _positions = null; final prefs = await SharedPreferences.getInstance(); await prefs.clear(); await prefs.setBool(isFirstLoginKey, true); debugPrint('AuthProvider: All session and cached data cleared.'); notifyListeners(); } Future> resetPassword(String email) { return _apiService.post('auth/forgot-password', {'email': email}); } }