// lib/services/user_preferences_service.dart import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:environment_monitoring_app/services/api_service.dart'; // Contains DatabaseHelper import 'package:environment_monitoring_app/services/database_helper.dart'; /// A dedicated service to manage the user's local preferences for /// module-specific submission destinations. class UserPreferencesService { final DatabaseHelper _dbHelper = DatabaseHelper(); static const _defaultPrefsSavedKey = 'default_preferences_saved'; // Moved from settings.dart for central access // This list now includes all your modules final List> _configurableModules = [ {'key': 'marine_tarball', 'name': 'Marine Tarball'}, {'key': 'marine_in_situ', 'name': 'Marine In-Situ'}, {'key': 'marine_investigative', 'name': 'Marine Investigative'}, {'key': 'marine_report', 'name': 'Marine Report'}, {'key': 'river_in_situ', 'name': 'River In-Situ'}, {'key': 'river_triennial', 'name': 'River Triennial'}, {'key': 'river_investigative', 'name': 'River Investigative'}, {'key': 'air_installation', 'name': 'Air Installation'}, {'key': 'air_collection', 'name': 'Air Collection'}, ]; /// Checks if default preferences have been set. If not, it applies /// and saves the default submission destinations for all modules. /// This ensures the app is ready for submissions immediately after the first login. Future applyAndSaveDefaultPreferencesIfNeeded() async { final prefs = await SharedPreferences.getInstance(); if (prefs.getBool(_defaultPrefsSavedKey) ?? false) { // Defaults have already been saved for this session, do nothing. return; } debugPrint("Checking availability of configs for default preference application..."); try { // Get all possible configs from the database just once final allApiConfigs = await _dbHelper.loadApiConfigs() ?? []; final allFtpConfigs = await _dbHelper.loadFtpConfigs() ?? []; // --- CRITICAL CHECK --- // If we haven't synced data from the server yet, do NOT attempt to apply defaults. // If we proceed now, we would save "empty" preferences and the flag would be set to true, // preventing the defaults from ever being applied correctly later. if (allApiConfigs.isEmpty && allFtpConfigs.isEmpty) { debugPrint("No API or FTP configs found in local DB. Skipping default application. Will retry later."); return; } debugPrint("Configs found. Applying and auto-saving default submission preferences."); for (var module in _configurableModules) { final moduleKey = module['key']!; // 1. Save master switches to enable API and FTP for the module. // FORCE them to be TRUE by default. await saveModulePreference( moduleName: moduleKey, isApiEnabled: true, isFtpEnabled: true, ); // 2. Determine default API links final defaultApiLinks = allApiConfigs.map((config) { // Robust check for active status (handles int 1, string '1', bool true) final activeVal = config['is_active']; final bool isActive = activeVal == 1 || activeVal == true || activeVal.toString() == '1'; final bool isPstwHq = (config['config_name'] == 'PSTW_HQ'); bool isEnabled; // --- MODIFIED: Special logic for Marine Report --- // For marine_report, ONLY tick PSTW_HQ by default. Ignore other active APIs. if (moduleKey == 'marine_report') { isEnabled = isPstwHq; } else { // For other modules, tick if Active OR PSTW_HQ isEnabled = isActive || isPstwHq; } return {...config, 'is_enabled': isEnabled}; }).toList(); // 3. Determine default FTP links // Handle mapping 'marine_in_situ' -> 'marine_manual' String expectedFtpModuleKey = moduleKey; if (moduleKey == 'marine_in_situ') { expectedFtpModuleKey = 'marine_manual'; } else if (moduleKey == 'river_in_situ') { expectedFtpModuleKey = 'river_manual'; } final defaultFtpLinks = allFtpConfigs.map((config) { final String configModule = config['ftp_module'] ?? ''; // Robust check for active status (Handles your SQL data where is_active is 1) final activeVal = config['is_active']; final bool isActive = activeVal == 1 || activeVal == true || activeVal.toString() == '1'; // Enable if the config's module matches the current moduleKey AND it's active bool isEnabled = (configModule == expectedFtpModuleKey) && isActive; return {...config, 'is_enabled': isEnabled}; }).toList(); // --- END MODIFICATION --- // 4. Save the default links to the database. await saveApiLinksForModule(moduleKey, defaultApiLinks); await saveFtpLinksForModule(moduleKey, defaultFtpLinks); } // 5. Set the flag to prevent this from running again until next login. await prefs.setBool(_defaultPrefsSavedKey, true); debugPrint("Default submission preferences have been auto-saved."); } catch (e) { debugPrint("Error auto-saving default preferences: $e"); } } /// Retrieves a module's master submission preferences. /// This method now returns a default TRUE object if no preference is found. Future?> getModulePreference(String moduleName) async { final preference = await _dbHelper.getModulePreference(moduleName); if (preference != null) { return preference; } // Return default enabled state (TRUE) if not found in DB yet return {'is_api_enabled': true, 'is_ftp_enabled': true}; } /// Saves or updates a module's master on/off switches for API and FTP submissions. Future saveModulePreference({ required String moduleName, required bool isApiEnabled, required bool isFtpEnabled, }) async { await _dbHelper.saveModulePreference( moduleName: moduleName, isApiEnabled: isApiEnabled, isFtpEnabled: isFtpEnabled, ); } /// Retrieves all available API configurations and merges them with the user's /// saved preferences for a specific module. /// /// This is primarily for the Settings UI to display all possible destinations /// with their current enabled/disabled state (e.g., checkboxes). Future>> getAllApiConfigsWithModulePreferences(String moduleName) async { // 1. Get all possible API destinations that have been synced to the device. final allApiConfigs = await _dbHelper.loadApiConfigs() ?? []; if (allApiConfigs.isEmpty) return []; // 2. Get the specific links the user has previously saved for this module. final savedLinks = await _dbHelper.getAllApiLinksForModule(moduleName); // 3. Merge the two lists. return allApiConfigs.map((config) { final configId = config['api_config_id']; bool isEnabled; // --- START MODIFICATION: Corrected Merge Logic --- Map? matchingLink; try { matchingLink = savedLinks.firstWhere( (link) => link['api_config_id'] == configId, ); } catch (e) { matchingLink = null; // No match found } if (matchingLink != null) { // A preference exists. Use the saved value. isEnabled = matchingLink['is_enabled'] as bool? ?? false; } else { // No preference saved for this config. Apply default logic. // Robust check for active status final activeVal = config['is_active']; final bool isActive = activeVal == 1 || activeVal == true || activeVal.toString() == '1'; final bool isPstwHq = (config['config_name'] == 'PSTW_HQ'); // --- MODIFIED: Special logic for Marine Report --- if (moduleName == 'marine_report') { isEnabled = isPstwHq; } else { isEnabled = isActive || isPstwHq; } } // --- END MODIFICATION --- // Return a new map containing the original config details plus the 'is_enabled' flag. return { ...config, 'is_enabled': isEnabled, }; }).toList(); } /// Retrieves all available FTP configurations and merges them with the user's /// saved preferences for a specific module. (For the Settings UI). Future>> getAllFtpConfigsWithModulePreferences(String moduleName) async { final allFtpConfigs = await _dbHelper.loadFtpConfigs() ?? []; if (allFtpConfigs.isEmpty) return []; final savedLinks = await _dbHelper.getAllFtpLinksForModule(moduleName); // Handle mapping 'marine_in_situ' -> 'marine_manual' String expectedFtpModuleKey = moduleName; if (moduleName == 'marine_in_situ') { expectedFtpModuleKey = 'marine_manual'; } else if (moduleName == 'river_in_situ') { expectedFtpModuleKey = 'river_manual'; } return allFtpConfigs.map((config) { final configId = config['ftp_config_id']; bool isEnabled; // --- START MODIFICATION: Corrected Merge Logic --- Map? matchingLink; try { matchingLink = savedLinks.firstWhere( (link) => link['ftp_config_id'] == configId, ); } catch (e) { matchingLink = null; // No match found } if (matchingLink != null) { // A preference exists. Use the saved value. isEnabled = matchingLink['is_enabled'] as bool? ?? false; } else { // No preference saved for this config. Apply default logic. final String configModule = config['ftp_module'] ?? ''; // Robust check for active status final activeVal = config['is_active']; final bool isActive = activeVal == 1 || activeVal == true || activeVal.toString() == '1'; // Use the mapped key for comparison isEnabled = (configModule == expectedFtpModuleKey) && isActive; } // --- END MODIFICATION --- return { ...config, 'is_enabled': isEnabled, }; }).toList(); } /// Saves the complete set of enabled/disabled API links for a specific module. /// This will replace all previous links for that module. Future saveApiLinksForModule(String moduleName, List> links) async { await _dbHelper.saveApiLinksForModule(moduleName, links); } /// Saves the complete set of enabled/disabled FTP links for a specific module. Future saveFtpLinksForModule(String moduleName, List> links) async { await _dbHelper.saveFtpLinksForModule(moduleName, links); } /// Retrieves only the API configurations that are actively enabled for a given module. /// /// This is primarily for the submission services to know exactly which /// destinations to send data to. Future>> getEnabledApiConfigsForModule(String moduleName) async { // 1. Check the master switch for the module. final pref = await getModulePreference(moduleName); // Use method that has default if (pref == null || !(pref['is_api_enabled'] as bool)) { debugPrint("API submissions are disabled for module '$moduleName'."); return []; // Return empty list if API is disabled or not set. } // 2. Get all configs with their preference flags. final allConfigsWithPrefs = await getAllApiConfigsWithModulePreferences(moduleName); // 3. Filter for only those that are enabled. final enabledConfigs = allConfigsWithPrefs.where((config) => config['is_enabled'] == true).toList(); debugPrint("Found ${enabledConfigs.length} enabled API destinations for module '$moduleName'."); return enabledConfigs; } /// Retrieves only the FTP configurations that are actively enabled for a given module. Future>> getEnabledFtpConfigsForModule(String moduleName) async { final pref = await getModulePreference(moduleName); // Use method that has default if (pref == null || !(pref['is_ftp_enabled'] as bool)) { debugPrint("FTP submissions are disabled for module '$moduleName'."); return []; } final allConfigsWithPrefs = await getAllFtpConfigsWithModulePreferences(moduleName); final enabledConfigs = allConfigsWithPrefs.where((config) => config['is_enabled'] == true).toList(); debugPrint("Found ${enabledConfigs.length} enabled FTP destinations for module '$moduleName'."); return enabledConfigs; } }