import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; import 'package:environment_monitoring_app/auth_provider.dart'; import 'package:environment_monitoring_app/services/settings_service.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @override State createState() => _SettingsScreenState(); } class _SettingsScreenState extends State { // SettingsService is now a utility, it doesn't hold state. final SettingsService _settingsService = SettingsService(); bool _isSyncingData = false; // REMOVED: Redundant state variable for settings sync // bool _isSyncingSettings = false; // REMOVED: Chat ID state variables are no longer needed, // we will read directly from the provider in the build method. final TextEditingController _tarballSearchController = TextEditingController(); String _tarballSearchQuery = ''; final TextEditingController _manualSearchController = TextEditingController(); String _manualSearchQuery = ''; final TextEditingController _riverManualSearchController = TextEditingController(); String _riverManualSearchQuery = ''; final TextEditingController _riverTriennialSearchController = TextEditingController(); String _riverTriennialSearchQuery = ''; @override void initState() { super.initState(); // REMOVED: _loadCurrentSettings() is no longer needed as we read from the provider. _tarballSearchController.addListener(_onTarballSearchChanged); _manualSearchController.addListener(_onManualSearchChanged); _riverManualSearchController.addListener(_onRiverManualSearchChanged); _riverTriennialSearchController.addListener(_onRiverTriennialSearchChanged); } @override void dispose() { _tarballSearchController.dispose(); _manualSearchController.dispose(); _riverManualSearchController.dispose(); _riverTriennialSearchController.dispose(); super.dispose(); } // REMOVED: _loadCurrentSettings is obsolete. The build method will now // get the latest settings directly from AuthProvider. void _onTarballSearchChanged() { setState(() { _tarballSearchQuery = _tarballSearchController.text; }); } void _onManualSearchChanged() { setState(() { _manualSearchQuery = _manualSearchController.text; }); } void _onRiverManualSearchChanged() { setState(() { _riverManualSearchQuery = _riverManualSearchController.text; }); } void _onRiverTriennialSearchChanged() { setState(() { _riverTriennialSearchQuery = _riverTriennialSearchController.text; }); } Future _manualDataSync() async { if (_isSyncingData) return; setState(() => _isSyncingData = true); final auth = Provider.of(context, listen: false); try { // This now syncs ALL data, including settings. await auth.syncAllData(forceRefresh: true); if (mounted) { _showSnackBar('Data synced successfully.', isError: false); } } catch (e) { if (mounted) { _showSnackBar('Data sync failed. Please check your connection.', isError: true); } } finally { if (mounted) { setState(() => _isSyncingData = false); } } } // REMOVED: _manualSettingsSync is obsolete because the main data sync // now handles settings as well. void _showSnackBar(String message, {bool isError = false}) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: isError ? Theme.of(context).colorScheme.error : Colors.green, ), ); } } @override Widget build(BuildContext context) { final auth = Provider.of(context); final lastSync = auth.lastSyncTimestamp; // Get the synced app settings from the provider. final appSettings = auth.appSettings; final filteredTarballStations = auth.tarballStations?.where((station) { final stationName = station['tbl_station_name']?.toLowerCase() ?? ''; final stationCode = station['tbl_station_code']?.toLowerCase() ?? ''; final query = _tarballSearchQuery.toLowerCase(); return stationName.contains(query) || stationCode.contains(query); }).toList(); final filteredManualStations = auth.manualStations?.where((station) { final stationName = station['man_station_name']?.toLowerCase() ?? ''; final stationCode = station['man_station_code']?.toLowerCase() ?? ''; final query = _manualSearchQuery.toLowerCase(); return stationName.contains(query) || stationCode.contains(query); }).toList(); final filteredRiverManualStations = auth.riverManualStations?.where((station) { final riverName = station['sampling_river']?.toLowerCase() ?? ''; final stationCode = station['sampling_station_code']?.toLowerCase() ?? ''; final basinName = station['sampling_basin']?.toLowerCase() ?? ''; final query = _riverManualSearchQuery.toLowerCase(); return riverName.contains(query) || stationCode.contains(query) || basinName.contains(query); }).toList(); final filteredRiverTriennialStations = auth.riverTriennialStations?.where((station) { final riverName = station['triennial_river']?.toLowerCase() ?? ''; final stationCode = station['triennial_station_code']?.toLowerCase() ?? ''; final basinName = station['triennial_basin']?.toLowerCase() ?? ''; final query = _riverTriennialSearchQuery.toLowerCase(); return riverName.contains(query) || stationCode.contains(query) || basinName.contains(query); }).toList(); return Scaffold( appBar: AppBar( title: const Text("Settings"), ), body: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Synchronization", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text("Last Data Sync:", style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 4), Text(lastSync != null ? DateFormat('yyyy-MM-dd HH:mm:ss').format(lastSync.toLocal()) : 'Never', style: Theme.of(context).textTheme.bodyLarge), const SizedBox(height: 16), ElevatedButton.icon( onPressed: _isSyncingData ? null : _manualDataSync, icon: _isSyncingData ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) : const Icon(Icons.cloud_sync), label: Text(_isSyncingData ? 'Syncing Data...' : 'Sync App Data'), style: ElevatedButton.styleFrom(minimumSize: const Size(double.infinity, 50)), ), ], ), ), ), const SizedBox(height: 32), Text("Telegram Alert Settings", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ ExpansionTile( title: const Text('Marine Alerts', style: TextStyle(fontWeight: FontWeight.bold)), initiallyExpanded: false, children: [ _buildChatIdEntry('In-Situ', _settingsService.getInSituChatId(appSettings)), _buildChatIdEntry('Tarball', _settingsService.getTarballChatId(appSettings)), _buildChatIdEntry('Investigative', _settingsService.getMarineInvestigativeChatId(appSettings)), ], ), ExpansionTile( title: const Text('River Alerts', style: TextStyle(fontWeight: FontWeight.bold)), initiallyExpanded: false, children: [ _buildChatIdEntry('In-Situ', _settingsService.getRiverInSituChatId(appSettings)), _buildChatIdEntry('Triennial', _settingsService.getRiverTriennialChatId(appSettings)), _buildChatIdEntry('Investigative', _settingsService.getRiverInvestigativeChatId(appSettings)), ], ), ExpansionTile( title: const Text('Air Alerts', style: TextStyle(fontWeight: FontWeight.bold)), initiallyExpanded: false, children: [ _buildChatIdEntry('Manual', _settingsService.getAirManualChatId(appSettings)), _buildChatIdEntry('Investigative', _settingsService.getAirInvestigativeChatId(appSettings)), ], ), // REMOVED: The separate sync button for settings is no longer needed. ], ), ), ), const SizedBox(height: 32), Text("Marine Tarball Stations (${filteredTarballStations?.length ?? 0})", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _tarballSearchController, decoration: InputDecoration( labelText: 'Search Tarball Stations', hintText: 'Search by name or code', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)), suffixIcon: _tarballSearchController.text.isNotEmpty ? IconButton(icon: const Icon(Icons.clear), onPressed: () => _tarballSearchController.clear()) : null, ), ), const SizedBox(height: 16), _buildStationList( filteredTarballStations, 'No matching tarball stations found.', 'No tarball stations available. Sync to download.', (station) => ListTile( title: Text(station['tbl_station_name'] ?? 'N/A'), subtitle: Text('Code: ${station['tbl_station_code'] ?? 'N/A'}'), dense: true, ), ), ], ), ), ), const SizedBox(height: 32), Text("Marine Manual Stations (${filteredManualStations?.length ?? 0})", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _manualSearchController, decoration: InputDecoration( labelText: 'Search Manual Stations', hintText: 'Search by name or code', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)), suffixIcon: _manualSearchController.text.isNotEmpty ? IconButton(icon: const Icon(Icons.clear), onPressed: () => _manualSearchController.clear()) : null, ), ), const SizedBox(height: 16), _buildStationList( filteredManualStations, 'No matching manual stations found.', 'No manual stations available. Sync to download.', (station) => ListTile( title: Text(station['man_station_name'] ?? 'N/A'), subtitle: Text('Code: ${station['man_station_code'] ?? 'N/A'}'), dense: true, ), ), ], ), ), ), const SizedBox(height: 32), Text("River Manual Stations (${filteredRiverManualStations?.length ?? 0})", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _riverManualSearchController, decoration: InputDecoration( labelText: 'Search River Manual Stations', hintText: 'Search by river, basin, or code', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)), suffixIcon: _riverManualSearchController.text.isNotEmpty ? IconButton(icon: const Icon(Icons.clear), onPressed: () => _riverManualSearchController.clear()) : null, ), ), const SizedBox(height: 16), _buildStationList( filteredRiverManualStations, 'No matching river manual stations found.', 'No river manual stations available. Sync to download.', (station) => ListTile( title: Text(station['sampling_river'] ?? 'N/A'), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Code: ${station['sampling_station_code'] ?? 'N/A'}'), Text('Basin: ${station['sampling_basin'] ?? 'N/A'}'), Text('State: ${station['state_name'] ?? 'N/A'}'), ], ), dense: true, ), ), ], ), ), ), const SizedBox(height: 32), Text("River Triennial Stations (${filteredRiverTriennialStations?.length ?? 0})", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _riverTriennialSearchController, decoration: InputDecoration( labelText: 'Search River Triennial Stations', hintText: 'Search by river, basin, or code', prefixIcon: const Icon(Icons.search), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8.0)), suffixIcon: _riverTriennialSearchController.text.isNotEmpty ? IconButton(icon: const Icon(Icons.clear), onPressed: () => _riverTriennialSearchController.clear()) : null, ), ), const SizedBox(height: 16), _buildStationList( filteredRiverTriennialStations, 'No matching river triennial stations found.', 'No river triennial stations available. Sync to download.', (station) => ListTile( title: Text(station['triennial_river'] ?? 'N/A'), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Code: ${station['triennial_station_code'] ?? 'N/A'}'), Text('Basin: ${station['triennial_basin'] ?? 'N/A'}'), Text('State: ${station['state_name'] ?? 'N/A'}'), ], ), dense: true, ), ), ], ), ), ), const SizedBox(height: 32), Text("General Settings", style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), Card( margin: EdgeInsets.zero, child: Column( children: [ ListTile( leading: const Icon(Icons.info_outline), title: const Text('App Version'), subtitle: const Text('1.2.03'), dense: true, ), ListTile( leading: const Icon(Icons.privacy_tip_outlined), title: const Text('Privacy Policy'), onTap: () {}, dense: true, ), ], ), ), ], ), ), ); } Widget _buildStationList( List>? stations, String noMatchText, String noDataText, Widget Function(Map) itemBuilder, ) { if (stations == null || stations.isEmpty) { return Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Text( _isSyncingData ? 'Loading...' : (stations == null ? noDataText : noMatchText), ), ), ); } return SizedBox( height: 250, child: ListView.builder( itemCount: stations.length, itemBuilder: (context, index) { final station = stations[index]; return itemBuilder(station); }, ), ); } Widget _buildChatIdEntry(String label, String value) { return ListTile( contentPadding: EdgeInsets.zero, leading: const Icon(Icons.telegram, size: 20), title: Text('$label Chat ID'), subtitle: Text(value.isNotEmpty ? value : 'Not Set'), dense: true, ); } }