// global_data.dart import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; const String BASE_URL = 'http://192.168.100.9/kopikaocare_api'; List> historyGlobal = []; class HistoryManager { static const String _keyHistory = 'sensor_history'; static const String _keyIntervalValue = 'interval_value'; static const String _keyIntervalUnit = 'interval_unit'; static const String _keyLastSync = 'last_sync_time'; static const String _keyCurrentRange = 'current_range'; static int intervalValue = 5; static String intervalUnit = 'detik'; static DateTime? lastSyncTime; static String currentRange = 'semua'; // Load history from LOCAL cache FIRST, then sync with server static Future loadHistory( {String range = 'semua', int limit = 1000, bool forceRefresh = false}) async { currentRange = range; // First, load from local cache await _loadHistoryLocal(); // Then, try to sync with server (if forceRefresh or cache is old) if (forceRefresh || _shouldSyncFromServer()) { await _syncFromServer(range, limit); } } // Check if should sync from server (every 5 minutes) static bool _shouldSyncFromServer() { if (lastSyncTime == null) return true; final difference = DateTime.now().difference(lastSyncTime!); return difference.inMinutes >= 5; // Sync every 5 minutes } // Load from local SharedPreferences cache static Future _loadHistoryLocal() async { final prefs = await SharedPreferences.getInstance(); // Load interval settings intervalValue = prefs.getInt(_keyIntervalValue) ?? 5; intervalUnit = prefs.getString(_keyIntervalUnit) ?? 'detik'; // Load last sync time String? lastSyncStr = prefs.getString(_keyLastSync); if (lastSyncStr != null) { lastSyncTime = DateTime.tryParse(lastSyncStr); } // Load current range currentRange = prefs.getString(_keyCurrentRange) ?? 'semua'; // Load history from local cache String? historyString = prefs.getString(_keyHistory); if (historyString != null && historyString.isNotEmpty) { try { List decoded = jsonDecode(historyString); historyGlobal = decoded.map((item) { Map map = Map.from(item); if (map['waktu'] is String) { map['waktu'] = DateTime.parse(map['waktu']); } return map; }).toList(); print('Local cache loaded: ${historyGlobal.length} records'); } catch (e) { print('Error loading local cache: $e'); historyGlobal = []; } } else { historyGlobal = []; print('No local cache found'); } } // Sync data from server with range filter static Future _syncFromServer(String range, int limit) async { try { print('Syncing from server with range: $range'); final response = await http.get( Uri.parse('$BASE_URL/get_history.php?range=$range&limit=$limit'), ); if (response.statusCode == 200) { final data = jsonDecode(response.body); if (data['status'] == 'success') { final serverData = List>.from(data['data']).map((item) { return { 'suhu': double.parse(item['suhu'].toString()), 'kelembapan': double.parse(item['kelembapan'].toString()), 'berat': double.parse(item['berat'].toString()), 'waktu': DateTime.parse(item['waktu']), }; }).toList(); // Replace with server data (server is source of truth for range) historyGlobal = serverData; await _saveHistoryLocal(); // Save current range final prefs = await SharedPreferences.getInstance(); await prefs.setString(_keyCurrentRange, range); lastSyncTime = DateTime.now(); await _saveLastSyncTime(); print( 'Synced from server: ${historyGlobal.length} records (range: $range)'); } } } catch (e) { print('Error syncing from server: $e, using cached data'); } } // Save to local SharedPreferences cache (PRIVATE) static Future _saveHistoryLocal() async { final prefs = await SharedPreferences.getInstance(); List> toSave = historyGlobal.map((item) { Map copy = Map.from(item); if (copy['waktu'] is DateTime) { copy['waktu'] = (copy['waktu'] as DateTime).toIso8601String(); } return copy; }).toList(); String historyString = jsonEncode(toSave); await prefs.setString(_keyHistory, historyString); print('Local cache saved: ${historyGlobal.length} records'); } // PUBLIC: Save to local SharedPreferences cache (for external use) static Future saveHistoryLocal() async { await _saveHistoryLocal(); } // Save last sync time static Future _saveLastSyncTime() async { final prefs = await SharedPreferences.getInstance(); if (lastSyncTime != null) { await prefs.setString(_keyLastSync, lastSyncTime!.toIso8601String()); } } // Save single data to MySQL and local cache static Future addData(Map newData) async { try { final response = await http.post( Uri.parse('$BASE_URL/save_history.php'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'suhu': newData['suhu'], 'kelembapan': newData['kelembapan'], 'berat': newData['berat'], }), ); if (response.statusCode == 200) { final result = jsonDecode(response.body); if (result['status'] == 'success') { // Add to local cache Map dataWithTime = { ...newData, 'waktu': DateTime.now(), }; historyGlobal.insert(0, dataWithTime); // Limit to 1000 data if (historyGlobal.length > 1000) { historyGlobal.removeLast(); } await _saveHistoryLocal(); print('Data saved to MySQL and local cache'); } } } catch (e) { print('Error saving to MySQL: $e'); } } // Delete selected data by index from display list static Future deleteSelectedData(List indices) async { if (indices.isEmpty) return; // Get display data (reversed from historyGlobal) List> displayData = List.from(historyGlobal.reversed); List> toDelete = []; for (var index in indices) { if (index < displayData.length) { toDelete.add(displayData[index]); } } if (toDelete.isEmpty) return; // Remove from historyGlobal historyGlobal.removeWhere((item) { return toDelete.any((deleteItem) => deleteItem['waktu'].toString() == item['waktu'].toString()); }); // Save to local cache await _saveHistoryLocal(); // Also sync deletion to server (optional - you may need to implement server-side deletion) // For now, we'll just update local and server will sync on next refresh print('${toDelete.length} data deleted locally'); } // Delete single data by its id or index static Future deleteSingleData( Map dataToDelete) async { historyGlobal.removeWhere((item) { return item['waktu'].toString() == dataToDelete['waktu'].toString(); }); await _saveHistoryLocal(); print('Single data deleted'); } // Clear all history from MySQL and local cache static Future clearHistory() async { try { final response = await http.post( Uri.parse('$BASE_URL/clear_history.php'), ); if (response.statusCode == 200) { historyGlobal.clear(); await _saveHistoryLocal(); print('History cleared from MySQL and local cache'); } } catch (e) { print('Error clearing history: $e'); } } // Update interval settings static Future updateInterval(int value, String unit) async { intervalValue = value; intervalUnit = unit; final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_keyIntervalValue, value); await prefs.setString(_keyIntervalUnit, unit); print('Interval updated: every $value $unit'); } // Force refresh from server static Future forceRefresh( {String range = 'semua', int limit = 1000}) async { await _syncFromServer(range, limit); } }