import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; import '../config/app_config.dart'; import 'landmark_database.dart'; import 'offline_routing_engine.dart'; enum PackageSyncStatus { notInstalled, downloading, installed, error, } class OfflinePackageInfo { final String packageId; final String name; final String version; final int totalLandmarks; final int totalRoadNodes; final String sizeFormatted; final DateTime? lastSyncTime; final PackageSyncStatus status; final bool canSyncNow; final int daysUntilNextSync; const OfflinePackageInfo({ required this.packageId, required this.name, required this.version, required this.totalLandmarks, required this.totalRoadNodes, required this.sizeFormatted, this.lastSyncTime, this.status = PackageSyncStatus.notInstalled, this.canSyncNow = true, this.daysUntilNextSync = 0, }); } class OfflinePackageManager { static const String _pkgKey = 'offline_jordan_pkg_v2'; static const int syncCooldownDays = 14; // 14-day server protection rule static String get defaultServerUrl => AppConfig.serverUrl; static String get defaultApiKey => AppConfig.apiKey; static ValueNotifier syncProgress = ValueNotifier(0.0); static ValueNotifier syncStatusMessage = ValueNotifier('جاهز للمزامنة'); static ValueNotifier packageStatus = ValueNotifier(PackageSyncStatus.installed); static Future getPackageInfo() async { final prefs = await SharedPreferences.getInstance(); final syncDateStr = prefs.getString('${_pkgKey}_date'); final isSynced = prefs.getBool('${_pkgKey}_synced') ?? true; final lastDate = syncDateStr != null ? DateTime.tryParse(syncDateStr) : null; bool canSync = true; int remainingDays = 0; if (lastDate != null) { final daysSinceLastSync = DateTime.now().difference(lastDate).inDays; if (daysSinceLastSync < syncCooldownDays) { canSync = false; remainingDays = syncCooldownDays - daysSinceLastSync; } } return OfflinePackageInfo( packageId: 'jordan-tactical-offline-v2', name: 'حزمة الأردن التكتيكية الميدانية الكاملة (Jordan Tactical Pack)', version: '2.4.0', totalLandmarks: LandmarkDatabase.allLandmarks.length, totalRoadNodes: 36, sizeFormatted: '3.5 MB (شبكة الطرق) + 25 MB (المعالم)', lastSyncTime: lastDate, status: isSynced ? PackageSyncStatus.installed : PackageSyncStatus.notInstalled, canSyncNow: canSync, daysUntilNextSync: remainingDays, ); } /// Download and sync full Jordan tactical data package from server with 14-day throttle protection static Future downloadFullPackage({ String? serverUrl, String? apiKey, bool force = false, }) async { final activeUrl = serverUrl ?? defaultServerUrl; final activeKey = apiKey ?? defaultApiKey; // Check 14-day cooldown unless forced if (!force) { final info = await getPackageInfo(); if (!info.canSyncNow && info.lastSyncTime != null) { syncStatusMessage.value = 'الحزمة محدثة بالفعل • المزامنة القادمة بعد ${info.daysUntilNextSync} يوم لحماية السيرفر.'; return false; } } try { packageStatus.value = PackageSyncStatus.downloading; syncProgress.value = 0.1; syncStatusMessage.value = 'جاري الاتصال بخادم الخرائط السيادي...'; // 1. Fetch Package Manifest final manifestUrl = Uri.parse('$activeUrl/api/tactical/offline-package/jordan'); try { final mResp = await http.get(manifestUrl, headers: {'x-api-key': activeKey}).timeout(const Duration(seconds: 6)); if (mResp.statusCode == 200) { syncProgress.value = 0.35; syncStatusMessage.value = 'تم استقبال بيانات الحزمة... جاري تحميل معالم PostGIS'; } } catch (_) { // Fallback to local cache } syncProgress.value = 0.50; syncStatusMessage.value = 'جاري مزامنة معالم الأردن وتحديث جداول PostGIS...'; // 2. Fetch and Cache PostGIS Landmarks final count = await LandmarkDatabase.syncFromServer(serverUrl: activeUrl, apiKey: activeKey); debugPrint('Synced $count landmarks to local storage.'); syncProgress.value = 0.75; syncStatusMessage.value = 'جاري بناء وتحديث شبكة التوجيه الطوبولوجية...'; // 3. Initialize & warm up on-device routing engine OfflineRoutingEngine.initialize(); syncProgress.value = 0.95; syncStatusMessage.value = 'جاري تثبيت الحزمة في الذاكرة الدائمة وتفعيل قفل الـ 14 يوماً...'; final prefs = await SharedPreferences.getInstance(); await prefs.setBool('${_pkgKey}_synced', true); await prefs.setString('${_pkgKey}_date', DateTime.now().toIso8601String()); syncProgress.value = 1.0; syncStatusMessage.value = 'اكتملت المزامنة بنجاح! الحزمة جاهزة للعمل 100% بدون إنترنت (تحديث قادم بعد 14 يوماً).'; packageStatus.value = PackageSyncStatus.installed; return true; } catch (e) { debugPrint('Package download error: $e'); packageStatus.value = PackageSyncStatus.error; syncStatusMessage.value = 'تعذر الاتصال بالسيرفر. تم تفعيل الحزمة المدمجة المسبقة.'; return false; } } }