import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; import 'package:geosector_app/core/constants/app_keys.dart'; import 'package:geosector_app/core/services/hive_web_fix.dart'; import 'package:geosector_app/core/services/hive_adapters.dart'; // Import de tous les modèles typés import 'package:geosector_app/core/data/models/user_model.dart'; import 'package:geosector_app/core/data/models/amicale_model.dart'; import 'package:geosector_app/core/data/models/client_model.dart'; import 'package:geosector_app/core/data/models/operation_model.dart'; import 'package:geosector_app/core/data/models/sector_model.dart'; import 'package:geosector_app/core/data/models/passage_model.dart'; import 'package:geosector_app/core/data/models/membre_model.dart'; import 'package:geosector_app/core/data/models/user_sector_model.dart'; import 'package:geosector_app/chat/models/conversation_model.dart'; import 'package:geosector_app/chat/models/message_model.dart'; /// Service singleton centralisé pour la gestion complète des Box Hive /// Utilisé par main.dart pour l'initialisation et par logout pour le nettoyage class HiveService { static HiveService? _instance; static HiveService get instance => _instance ??= HiveService._internal(); HiveService._internal(); /// Configuration des Box typées de l'application static const List _boxConfigs = [ HiveBoxConfig(AppKeys.userBoxName, 'UserModel'), HiveBoxConfig(AppKeys.amicaleBoxName, 'AmicaleModel'), HiveBoxConfig(AppKeys.clientsBoxName, 'ClientModel'), HiveBoxConfig(AppKeys.operationsBoxName, 'OperationModel'), HiveBoxConfig(AppKeys.sectorsBoxName, 'SectorModel'), HiveBoxConfig(AppKeys.passagesBoxName, 'PassageModel'), HiveBoxConfig(AppKeys.membresBoxName, 'MembreModel'), HiveBoxConfig(AppKeys.userSectorBoxName, 'UserSectorModel'), HiveBoxConfig(AppKeys.chatConversationsBoxName, 'ConversationModel'), HiveBoxConfig(AppKeys.chatMessagesBoxName, 'MessageModel'), HiveBoxConfig(AppKeys.settingsBoxName, 'Settings'), HiveBoxConfig(AppKeys.regionsBoxName, 'Regions'), ]; bool _isInitialized = false; bool get isInitialized => _isInitialized; // === INITIALISATION COMPLÈTE (appelée par main.dart) === /// Initialisation complète de Hive avec réinitialisation totale Future initializeAndResetHive() async { if (_isInitialized) { debugPrint('ℹ️ HiveService déjà initialisé'); return; } try { debugPrint('🔧 Initialisation complète de Hive avec reset...'); // 1. Initialisation de base de Hive await Hive.initFlutter(); debugPrint('✅ Hive.initFlutter() terminé'); // 2. Enregistrement des adaptateurs _registerAdapters(); // 3. Destruction complète des anciennes données await _destroyAllData(); // 4. Création des Box vides et propres await _createAllBoxes(); _isInitialized = true; debugPrint('✅ HiveService initialisé avec succès'); } catch (e) { debugPrint('❌ Erreur lors de l\'initialisation complète: $e'); _isInitialized = false; rethrow; } } // === INITIALISATION SIMPLE (appelée par splash_page si besoin) === /// Initialisation simple sans reset (utilisée par splash_page si déjà initialisé) Future ensureBoxesAreOpen() async { try { debugPrint('🔍 Vérification et ouverture des Box...'); // Vérifier si toutes les Box sont ouvertes bool allOpen = true; for (final config in _boxConfigs) { if (!Hive.isBoxOpen(config.name)) { allOpen = false; break; } } if (allOpen) { debugPrint('✅ Toutes les Box sont déjà ouvertes'); return; } // Ouvrir les Box manquantes await _createAllBoxes(); debugPrint('✅ Box manquantes ouvertes'); } catch (e) { debugPrint('❌ Erreur lors de la vérification des Box: $e'); rethrow; } } // === NETTOYAGE LOGOUT (appelé lors du logout) === /// Nettoyage sélectif lors du logout (préserve les utilisateurs) Future cleanDataOnLogout() async { try { debugPrint('🧹 Nettoyage des données au logout...'); // Nettoyer toutes les Box sauf les utilisateurs for (final config in _boxConfigs) { if (config.name != AppKeys.userBoxName) { await _clearSingleBox(config.name); } } debugPrint('✅ Nettoyage logout terminé (utilisateurs préservés)'); } catch (e) { debugPrint('❌ Erreur lors du nettoyage logout: $e'); rethrow; } } // === MÉTHODES PRIVÉES D'INITIALISATION === /// Enregistrement de tous les adaptateurs Hive void _registerAdapters() { try { if (!Hive.isAdapterRegistered(0)) { // Utiliser HiveAdapters existant pour enregistrer tous les adaptateurs HiveAdapters.registerAll(); debugPrint('🔌 Adaptateurs Hive enregistrés via HiveAdapters'); } else { debugPrint('ℹ️ Adaptateurs déjà enregistrés'); } } catch (e) { debugPrint('❌ Erreur enregistrement adaptateurs: $e'); // Ne pas faire échouer l'initialisation } } /// Destruction complète de toutes les données selon la plateforme Future _destroyAllData() async { try { debugPrint('💥 Destruction complète des données Hive...'); // 1. Fermer toutes les Box ouvertes await _closeAllOpenBoxes(); // 2. Suppression selon la plateforme if (kIsWeb) { await _destroyDataWeb(); } else if (Platform.isIOS) { await _destroyDataIOS(); } else if (Platform.isAndroid) { await _destroyDataAndroid(); } else { await _destroyDataDesktop(); } // 3. Attendre pour s'assurer que tout est détruit await Future.delayed(const Duration(seconds: 1)); debugPrint('✅ Destruction complète terminée'); } catch (e) { debugPrint('❌ Erreur destruction: $e'); // Continuer malgré l'erreur } } /// Fermeture de toutes les Box ouvertes Future _closeAllOpenBoxes() async { try { debugPrint('🔒 Fermeture de toutes les Box ouvertes...'); // Fermer les Box configurées for (final config in _boxConfigs) { try { if (Hive.isBoxOpen(config.name)) { await Hive.box(config.name).close(); debugPrint('🔒 Box ${config.name} fermée'); } } catch (e) { debugPrint('⚠️ Erreur fermeture ${config.name}: $e'); } } // Fermer aussi les Box potentiellement orphelines final orphanBoxes = ['auth', 'temp', 'cache', 'locations', 'messages']; for (final boxName in orphanBoxes) { try { if (Hive.isBoxOpen(boxName)) { await Hive.box(boxName).close(); debugPrint('🔒 Box orpheline $boxName fermée'); } } catch (e) { debugPrint('⚠️ Erreur fermeture orpheline $boxName: $e'); } } } catch (e) { debugPrint('❌ Erreur fermeture générale: $e'); } } /// Destruction des données sur Web Future _destroyDataWeb() async { try { debugPrint('🌐 Destruction Web...'); // Sur Web, utiliser le HiveWebFix si disponible try { await HiveWebFix.resetHiveCompletely(); debugPrint('✅ Destruction Web via HiveWebFix'); return; } catch (e) { debugPrint('⚠️ HiveWebFix échoué, fallback...'); } // Fallback : supprimer Box par Box for (final config in _boxConfigs) { try { await Hive.deleteBoxFromDisk(config.name); debugPrint('🗑️ Box Web ${config.name} supprimée'); } catch (e) { debugPrint('⚠️ Erreur suppression Web ${config.name}: $e'); } } } catch (e) { debugPrint('❌ Erreur destruction Web: $e'); } } /// Destruction des données sur iOS Future _destroyDataIOS() async { try { debugPrint('🍎 Destruction iOS...'); // Méthode 1: Destruction totale try { await Hive.deleteFromDisk(); debugPrint('✅ Destruction iOS complète'); return; } catch (e) { debugPrint('⚠️ Destruction iOS totale échouée, méthode alternative...'); } // Méthode 2: Suppression des fichiers manuellement try { final appDir = await getApplicationDocumentsDirectory(); final hiveDir = Directory('${appDir.path}/hive'); if (await hiveDir.exists()) { await hiveDir.delete(recursive: true); debugPrint('✅ Dossier Hive iOS supprimé'); } } catch (e) { debugPrint('⚠️ Suppression dossier iOS échouée: $e'); } // Méthode 3: Fallback Box par Box await _fallbackDeleteBoxes(); } catch (e) { debugPrint('❌ Erreur destruction iOS: $e'); } } /// Destruction des données sur Android Future _destroyDataAndroid() async { try { debugPrint('🤖 Destruction Android...'); // Méthode 1: Destruction totale try { await Hive.deleteFromDisk(); debugPrint('✅ Destruction Android complète'); return; } catch (e) { debugPrint('⚠️ Destruction Android totale échouée, méthode alternative...'); } // Méthode 2: Suppression des fichiers .hive et .lock try { final appDir = await getApplicationDocumentsDirectory(); final files = await appDir.list().toList(); int deletedCount = 0; for (final file in files) { final fileName = file.path.split('/').last; if (fileName.endsWith('.hive') || fileName.endsWith('.lock')) { try { await file.delete(); deletedCount++; } catch (e) { debugPrint('⚠️ Erreur suppression fichier $fileName: $e'); } } } debugPrint('✅ $deletedCount fichiers Android supprimés'); } catch (e) { debugPrint('⚠️ Suppression fichiers Android échouée: $e'); } // Méthode 3: Fallback Box par Box await _fallbackDeleteBoxes(); } catch (e) { debugPrint('❌ Erreur destruction Android: $e'); } } /// Destruction des données sur Desktop Future _destroyDataDesktop() async { try { debugPrint('🖥️ Destruction Desktop...'); // Destruction totale await Hive.deleteFromDisk(); debugPrint('✅ Destruction Desktop complète'); } catch (e) { debugPrint('⚠️ Destruction Desktop échouée, fallback...'); await _fallbackDeleteBoxes(); } } /// Fallback : suppression Box par Box Future _fallbackDeleteBoxes() async { debugPrint('🔄 Fallback: suppression Box par Box...'); for (final config in _boxConfigs) { try { await Hive.deleteBoxFromDisk(config.name); debugPrint('🗑️ Box fallback ${config.name} supprimée'); } catch (e) { debugPrint('⚠️ Erreur suppression fallback ${config.name}: $e'); } } } /// Création de toutes les Box vides et propres Future _createAllBoxes() async { try { debugPrint('🆕 Création de toutes les Box...'); for (int i = 0; i < _boxConfigs.length; i++) { final config = _boxConfigs[i]; try { await _createSingleBox(config); debugPrint('✅ Box ${config.name} créée (${i + 1}/${_boxConfigs.length})'); } catch (e) { debugPrint('❌ Erreur création ${config.name}: $e'); // Continuer même en cas d'erreur } // Petite pause entre les créations await Future.delayed(const Duration(milliseconds: 100)); } debugPrint('✅ Toutes les Box ont été créées'); } catch (e) { debugPrint('❌ Erreur création des Box: $e'); rethrow; } } /// Création d'une Box individuelle avec le bon type Future _createSingleBox(HiveBoxConfig config) async { try { if (Hive.isBoxOpen(config.name)) { debugPrint('ℹ️ Box ${config.name} déjà ouverte'); return; } switch (config.type) { case 'UserModel': await Hive.openBox(config.name); break; case 'AmicaleModel': await Hive.openBox(config.name); break; case 'ClientModel': await Hive.openBox(config.name); break; case 'OperationModel': await Hive.openBox(config.name); break; case 'SectorModel': await Hive.openBox(config.name); break; case 'PassageModel': await Hive.openBox(config.name); break; case 'MembreModel': await Hive.openBox(config.name); break; case 'UserSectorModel': await Hive.openBox(config.name); break; case 'ConversationModel': await Hive.openBox(config.name); break; case 'MessageModel': await Hive.openBox(config.name); break; default: // Pour Settings, Regions, etc. await Hive.openBox(config.name); } } catch (e) { debugPrint('❌ Erreur création spécifique ${config.name}: $e'); // Fallback : essayer sans type try { await Hive.openBox(config.name); debugPrint('⚠️ Box ${config.name} créée sans type'); } catch (e2) { debugPrint('❌ Échec total ${config.name}: $e2'); rethrow; } } } // === MÉTHODES UTILITAIRES === /// Vider une Box individuelle Future _clearSingleBox(String boxName) async { try { if (Hive.isBoxOpen(boxName)) { await Hive.box(boxName).clear(); debugPrint('🧹 Box $boxName vidée'); } else { debugPrint('ℹ️ Box $boxName n\'est pas ouverte, impossible de la vider'); } } catch (e) { debugPrint('⚠️ Erreur vidage $boxName: $e'); } } /// Vérification que toutes les Box sont ouvertes bool areAllBoxesOpen() { for (final config in _boxConfigs) { if (!Hive.isBoxOpen(config.name)) { debugPrint('❌ Box ${config.name} n\'est pas ouverte'); return false; } } debugPrint('✅ Toutes les Box sont ouvertes'); return true; } /// Récupération sécurisée d'une Box typée Box getTypedBox(String boxName) { if (!Hive.isBoxOpen(boxName)) { throw Exception('La Box $boxName n\'est pas ouverte'); } return Hive.box(boxName); } /// Récupération sécurisée d'une Box non-typée Box getBox(String boxName) { if (!Hive.isBoxOpen(boxName)) { throw Exception('La Box $boxName n\'est pas ouverte'); } return Hive.box(boxName); } /// Liste des noms de toutes les Box configurées List getAllBoxNames() { return _boxConfigs.map((config) => config.name).toList(); } /// Diagnostic complet de l'état des Box Map getDiagnostic() { final diagnostic = {}; for (final config in _boxConfigs) { diagnostic[config.name] = Hive.isBoxOpen(config.name); } return diagnostic; } /// Reset complet du service (pour tests) static void reset() { _instance = null; } } /// Configuration d'une Box Hive avec type class HiveBoxConfig { final String name; final String type; const HiveBoxConfig(this.name, this.type); }