# PLAN DE DÉVELOPPEMENT - ApiService Singleton + Services Utilisateur/Amicale v2.0

🎯 **Objectif** : Transformer l'ApiService actuel en singleton et créer des services singleton pour l'utilisateur et l'amicale courante, puis refactoriser toute l'application pour utiliser ces nouveaux patterns.

---

## 📋 Vue d'ensemble du projet

### 🎯 Objectifs principaux

- ✅ Convertir ApiService en singleton thread-safe
- ✅ Créer CurrentUserService singleton pour l'utilisateur connecté
- ✅ Créer CurrentAmicaleService singleton pour l'amicale courante
- ✅ Renommer la Hive Box "users" en "user" (plus logique pour un seul utilisateur)
- ✅ Éliminer l'instanciation multiple des services
- ✅ Centraliser la gestion des sessions et de l'authentification
- ✅ Simplifier l'accès aux données utilisateur/amicale dans toute l'app
- ✅ Optimiser les performances et la mémoire
- ✅ Améliorer la maintenance et la testabilité

### 📊 Estimation

- **Durée totale** : 1.5 journée (12h)
- **Complexité** : Moyenne-Élevée
- **Impact** : Application entière
- **Risque** : Faible (refactoring)

---

## 🚀 SESSION DE TRAVAIL - PLANNING DÉTAILLÉ

### Phase 0: Préparation Git et environnement (15 min) ✅

#### Tâche 0.1: Création de la branche (5 min) ✅

```bash
# Créer et basculer sur la nouvelle branche
git checkout -b singletons

# Vérifier qu'on est sur la bonne branche
git branch
```

#### Tâche 0.2: Backup et documentation (10 min) ✅

```bash
# Créer un backup des fichiers critiques
mkdir -p backups/$(date +%Y%m%d_%H%M%S)
cp app/lib/core/services/api_service.dart backups/$(date +%Y%m%d_%H%M%S)/
cp app/lib/core/repositories/user_repository.dart backups/$(date +%Y%m%d_%H%M%S)/
cp app/lib/core/constants/app_keys.dart backups/$(date +%Y%m%d_%H%M%S)/
cp app/lib/main.dart backups/$(date +%Y%m%d_%H%M%S)/

# Documenter le démarrage de la refactorisation
echo "$(date): Début refactorisation singletons" >> refactoring.log
```

**Actions** :

- [x] Créer la branche `singletons`
- [x] Sauvegarder les fichiers critiques
- [x] Documenter le début de la refactorisation
- [x] Commit initial de la branche

```bash
git add .
git commit -m "feat: création branche singletons - début refactorisation

- Sauvegarde des fichiers critiques
- Préparation transformation ApiService en singleton
- Préparation création CurrentUserService et CurrentAmicaleService
- Objectif: renommer Box users -> user"
```

### Phase 1: Préparation et analyse (45 min) ✅

#### Tâche 1.1: Audit du code existant (20 min) ✅

```bash
# Rechercher toutes les utilisations des services
grep -r "ApiService" app/lib --include="*.dart" > audit_apiservice.txt
grep -r "UserRepository" app/lib --include="*.dart" > audit_userrepository.txt
grep -r "getCurrentUser" app/lib --include="*.dart" > audit_getcurrentuser.txt
grep -r "currentUser" app/lib --include="*.dart" > audit_currentuser.txt
grep -r "usersBoxName" app/lib --include="*.dart" > audit_usersbox.txt
```

**Actions** :

- [x] Lister tous les fichiers utilisant ApiService
- [x] Identifier les patterns d'injection actuels
- [x] Noter les accès aux données utilisateur/amicale
- [x] Documenter les méthodes utilisées
- [x] Analyser les Box Hive users/amicale
- [x] Identifier toutes les occurrences de "usersBoxName"

#### Tâche 1.2: Modification app_keys.dart pour renommage Box (10 min) ✅

**Fichier à modifier** : `app/lib/core/constants/app_keys.dart`
**Actions** :

- [x] Changer `usersBoxName` en `userBoxName`
- [x] Ajouter une constante de migration si nécessaire
- [x] Documenter le changement

**Code modifié** :

```dart
// Avant
static const String usersBoxName = 'users';

// Après
static const String userBoxName = 'user'; // Box pour l'utilisateur unique connecté
static const String usersBoxNameOld = 'users'; // Pour migration si nécessaire
```

#### Tâche 1.3: Planification de la refactorisation (15 min) ✅

**Actions** :

- [x] Créer une liste des repositories à modifier
- [x] Identifier les pages/widgets accédant aux données utilisateur
- [x] Planifier l'ordre de modification (dépendances)
- [x] Préparer la stratégie de tests
- [x] Définir l'architecture des nouveaux services
- [x] Planifier la migration de la Box users -> user

---

### Phase 2: Correction des modèles et préparation (60 min) ✅

#### Tâche 2.1: Correction MembreModel selon les vrais champs (30 min) ✅

**Fichier modifié** : `app/lib/core/data/models/membre_model.dart`
**Actions** :

- [x] Corriger les champs selon les spécifications réelles :

  - `final int id`
  - `int? fkEntite`
  - `final int role`
  - `int? fkTitre`
  - `String? name`
  - `String? firstName`
  - `String? username`
  - `String? sectName`
  - `final String email`
  - `String? phone`
  - `String? mobile`
  - `DateTime? dateNaissance`
  - `DateTime? dateEmbauche`
  - `final DateTime createdAt`
  - `bool isActive`

- [x] Adapter les annotations Hive @HiveField
- [x] Corriger fromJson() et toJson()
- [x] Mettre à jour copyWith()

#### Tâche 2.2: Correction ClientModel avec champs manquants (15 min) ✅

**Fichier modifié** : `app/lib/core/data/models/client_model.dart`
**Actions** :

- [x] Ajouter les champs manqués : `chkStripe`, `createdAt`, `updatedAt`
- [x] Mettre à jour les annotations Hive
- [x] Corriger fromJson() et toJson()
- [x] Mettre à jour copyWith()

#### Tâche 2.3: Correction des repositories selon les vrais modèles (15 min) ✅

**Fichiers modifiés** :

- `app/lib/core/repositories/membre_repository.dart`
- `app/lib/core/repositories/client_repository.dart`
  **Actions** :

- [x] Adapter MembreRepository pour les vrais champs (`isActive` au lieu de `chkActive`, etc.)
- [x] Corriger les méthodes create/update pour éviter les reconstructions manuelles
- [x] Utiliser copyWith() correctement
- [x] Simplifier la logique de création API
- [x] Corriger ClientRepository de la même manière

---

### Phase 3: Renommage et migration de la Hive Box (30 min)

#### Tâche 3.1: Mise à jour main.dart pour la nouvelle Box (15 min)

**Fichier à modifier** : `app/lib/main.dart`
**Actions** :

- [ ] Remplacer `AppKeys.usersBoxName` par `AppKeys.userBoxName`
- [ ] Modifier l'ouverture de la Box dans `_openEssentialHiveBoxes()`
- [ ] Ajouter logique de migration depuis l'ancienne Box si nécessaire

**Code à modifier** :

```dart
Future<void> _openEssentialHiveBoxes() async {
  final boxesToOpen = [
    {'name': AppKeys.userBoxName, 'type': 'UserModel'}, // Changé
    {'name': AppKeys.amicaleBoxName, 'type': 'AmicaleModel'},
    // ... autres boxes
  ];

  // Logique de migration si l'ancienne box existe
  try {
    if (Hive.isBoxOpen(AppKeys.usersBoxNameOld)) {
      final oldBox = Hive.box<UserModel>(AppKeys.usersBoxNameOld);
      final newBox = await Hive.openBox<UserModel>(AppKeys.userBoxName);

      // Migrer les données
      if (oldBox.isNotEmpty && newBox.isEmpty) {
        final userData = oldBox.get('current_user');
        if (userData != null) {
          await newBox.put('current_user', userData);
          debugPrint('✅ Migration de users -> user réussie');
        }
      }

      // Fermer et supprimer l'ancienne box
      await oldBox.close();
      await Hive.deleteBoxFromDisk(AppKeys.usersBoxNameOld);
      debugPrint('✅ Ancienne box users supprimée');
    }
    } catch (e) {
    debugPrint('⚠️ Erreur migration box users: $e');
  }
}
```

#### Tâche 3.2: Mise à jour UserRepository pour la nouvelle Box (15 min)

**Fichier à modifier** : `app/lib/core/repositories/user_repository.dart`
**Actions** :

- [ ] Remplacer toutes les occurrences de `AppKeys.usersBoxName` par `AppKeys.userBoxName`
- [ ] Modifier les getters de Box
- [ ] Tester que la compilation passe

**Code à modifier** :

```dart
// Avant
Box<UserModel> get _userBox => Hive.box<UserModel>(AppKeys.usersBoxName);

// Après
Box<UserModel> get _userBox => Hive.box<UserModel>(AppKeys.userBoxName);
```

---

### Phase 4: Création du nouveau ApiService Singleton (45 min)

#### Tâche 4.1: Backup du code existant (5 min)

```bash
# Sauvegarder l'ApiService actuel
cp app/lib/core/services/api_service.dart app/lib/core/services/api_service_backup.dart
```

#### Tâche 4.2: Refactorisation ApiService en Singleton (40 min)

**Fichier à modifier** : `app/lib/core/services/api_service.dart`
**Actions** :

- [ ] Implémenter le pattern Singleton thread-safe
- [ ] Ajouter méthode d'initialisation statique
- [ ] Conserver toute la logique d'environnement existante
- [ ] Améliorer la gestion des erreurs
- [ ] Ajouter logging pour debug
- [ ] Maintenir la compatibilité des méthodes existantes

**Code à implémenter** :

```dart
class ApiService {
  static ApiService? _instance;
  static final Object _lock = Object();

  // ... propriétés existantes conservées ...
  final Dio _dio = Dio();
  late final String _baseUrl;
  late final String _appIdentifier;
  String? _sessionId;

  // Singleton thread-safe
  static ApiService get instance {
    if (_instance == null) {
      throw Exception('ApiService non initialisé. Appelez initialize() d\'abord.');
    }
    return _instance!;
  }

  static Future<void> initialize() async {
    if (_instance == null) {
      _instance = ApiService._internal();
      debugPrint('✅ ApiService singleton initialisé');
    }
  }

  // Constructeur privé avec toute la logique existante
  ApiService._internal() {
    _configureEnvironment();

    _dio.options.baseUrl = _baseUrl;
    _dio.options.connectTimeout = AppKeys.connectionTimeout;
    _dio.options.receiveTimeout = AppKeys.receiveTimeout;

    final headers = Map<String, String>.from(AppKeys.defaultHeaders);
    headers['X-App-Identifier'] = _appIdentifier;
    _dio.options.headers.addAll(headers);

    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: (options, handler) {
        if (_sessionId != null) {
          options.headers[AppKeys.sessionHeader] = 'Bearer $_sessionId';
        }
        handler.next(options);
  },
      onError: (DioException error, handler) {
        if (error.response?.statusCode == 401) {
          _sessionId = null;
        }
        handler.next(error);
      },
    ));

    debugPrint('🔗 ApiService configuré pour $_baseUrl');
  }

  // Toutes les méthodes existantes restent identiques
  // (get, post, put, delete, login, logout, etc.)

  // Méthode de nettoyage pour les tests
  static void reset() {
    _instance = null;
  }
}
```

---

### Phase 5: Création des Services Singleton Utilisateur/Amicale (90 min)

#### Tâche 5.1: Création CurrentUserService (45 min)

**Fichier à créer** : `app/lib/core/services/current_user_service.dart`
**Actions** :

- [ ] Créer la classe singleton CurrentUserService
- [ ] Implémenter la gestion du cache utilisateur
- [ ] Ajouter méthodes de persistence avec Hive (nouvelle Box user)
- [ ] Implémenter les getters utiles (role, permissions, etc.)
- [ ] Ajouter gestion des sessions
- [ ] Implémenter les méthodes de logout/login

**Code à implémenter** :

```dart
import 'package:flutter/foundation.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/data/models/user_model.dart';

class CurrentUserService extends ChangeNotifier {
  static CurrentUserService? _instance;
  static CurrentUserService get instance => _instance ??= CurrentUserService._internal();

  CurrentUserService._internal();

  UserModel? _currentUser;

  // === GETTERS ===
  UserModel? get currentUser => _currentUser;
  bool get isLoggedIn => _currentUser?.hasValidSession ?? false;
  int get userRole => _currentUser?.role ?? 0;
  int? get userId => _currentUser?.id;
  String? get userEmail => _currentUser?.email;
  String? get userName => _currentUser?.name;
  String? get userFirstName => _currentUser?.firstName;
  String? get sessionId => _currentUser?.sessionId;
  int? get fkEntite => _currentUser?.fkEntite;
  String? get userPhone => _currentUser?.phone;
  String? get userMobile => _currentUser?.mobile;

  // Vérifications de rôles
  bool get isUser => userRole == 1;
  bool get isAdminAmicale => userRole == 2;
  bool get isSuperAdmin => userRole >= 3;
  bool get canAccessAdmin => isAdminAmicale || isSuperAdmin;

  // === SETTERS ===
  Future<void> setUser(UserModel? user) async {
    _currentUser = user;
    await _saveToHive();
    notifyListeners();

    debugPrint('👤 Utilisateur défini: ${user?.email ?? 'null'}');

    // Auto-synchroniser l'amicale si l'utilisateur a une entité
    if (user?.fkEntite != null) {
      await CurrentAmicaleService.instance.loadUserAmicale();
    } else {
      await CurrentAmicaleService.instance.clearAmicale();
    }
  }

  Future<void> updateUser(UserModel updatedUser) async {
    _currentUser = updatedUser;
    await _saveToHive();
    notifyListeners();
    debugPrint('👤 Utilisateur mis à jour: ${updatedUser.email}');
  }

  Future<void> clearUser() async {
    final userEmail = _currentUser?.email;
    _currentUser = null;
    await _clearFromHive();
    notifyListeners();
    debugPrint('👤 Utilisateur effacé: $userEmail');
  }

  // === PERSISTENCE HIVE (nouvelle Box user) ===
  Future<void> _saveToHive() async {
    try {
      if (_currentUser != null) {
        final box = Hive.box<UserModel>(AppKeys.userBoxName); // Nouvelle Box
        await box.clear();
        await box.put('current_user', _currentUser!);
        debugPrint('💾 Utilisateur sauvegardé dans Box user');
      }
    } catch (e) {
      debugPrint('❌ Erreur sauvegarde utilisateur Hive: $e');
    }
  }

  Future<void> _clearFromHive() async {
    try {
      final box = Hive.box<UserModel>(AppKeys.userBoxName); // Nouvelle Box
      await box.clear();
      debugPrint('🗑️ Box user effacée');
    } catch (e) {
      debugPrint('❌ Erreur effacement utilisateur Hive: $e');
    }
  }

  Future<void> loadFromHive() async {
    try {
      final box = Hive.box<UserModel>(AppKeys.userBoxName); // Nouvelle Box
      final user = box.get('current_user');

      if (user?.hasValidSession == true) {
        _currentUser = user;
        debugPrint('📥 Utilisateur chargé depuis Hive: ${user.email}');
      } else {
        _currentUser = null;
        debugPrint('ℹ️ Aucun utilisateur valide trouvé dans Hive');
      }

      notifyListeners();
    } catch (e) {
      debugPrint('❌ Erreur chargement utilisateur depuis Hive: $e');
      _currentUser = null;
    }
  }

  // === MÉTHODES UTILITAIRES ===
  Future<void> updateLastPath(String path) async {
    if (_currentUser != null) {
      await updateUser(_currentUser!.copyWith(lastPath: path));
    }
  }

  String? getLastPath() => _currentUser?.lastPath;

  String getDefaultRoute() {
    if (!isLoggedIn) return '/';
    return canAccessAdmin ? '/admin' : '/user';
  }

  String getRoleLabel() {
    switch (userRole) {
      case 1: return 'Utilisateur';
      case 2: return 'Admin Amicale';
      case 3: return 'Super Admin';
      default: return 'Inconnu';
    }
  }

  bool hasPermission(String permission) {
    switch (permission) {
      case 'admin':
        return canAccessAdmin;
      case 'super_admin':
        return isSuperAdmin;
      case 'manage_amicale':
        return canAccessAdmin;
      case 'manage_users':
        return isSuperAdmin;
      default:
        return isLoggedIn;
    }
  }

  // === RESET POUR TESTS ===
  static void reset() {
    _instance?._currentUser = null;
    _instance = null;
  }
}
```

#### Tâche 5.2: Création CurrentAmicaleService (45 min)

**Fichier à créer** : `app/lib/core/services/current_amicale_service.dart`
**Actions** :

- [ ] Créer la classe singleton CurrentAmicaleService
- [ ] Implémenter la gestion du cache amicale
- [ ] Ajouter méthodes de persistence avec Hive
- [ ] Implémenter les getters utiles
- [ ] Ajouter synchronisation avec CurrentUserService

**Code à implémenter** :

```dart
import 'package:flutter/foundation.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/data/models/amicale_model.dart';
import 'package:geosector_app/core/services/current_user_service.dart';

class CurrentAmicaleService extends ChangeNotifier {
  static CurrentAmicaleService? _instance;
  static CurrentAmicaleService get instance => _instance ??= CurrentAmicaleService._internal();
  CurrentAmicaleService._internal();

  AmicaleModel? _currentAmicale;

  // === GETTERS ===
  AmicaleModel? get currentAmicale => _currentAmicale;
  bool get hasAmicale => _currentAmicale != null;
  int? get amicaleId => _currentAmicale?.id;
  String? get amicaleName => _currentAmicale?.name;
  String? get amicaleEmail => _currentAmicale?.email;
  String? get amicalePhone => _currentAmicale?.phone;
  String? get amicaleMobile => _currentAmicale?.mobile;
  String? get amicaleAddress => _currentAmicale != null
      ? '${_currentAmicale!.adresse1} ${_currentAmicale!.adresse2}'.trim()
      : null;
  String? get amicaleFullAddress => _currentAmicale != null
      ? '${amicaleAddress ?? ''} ${_currentAmicale!.codePostal} ${_currentAmicale!.ville}'.trim()
      : null;
  bool get amicaleIsActive => _currentAmicale?.chkActive ?? false;
  bool get isClient => _currentAmicale?.fkType == 1;

  // Géolocalisation
  bool get hasGpsCoordinates =>
      _currentAmicale?.gpsLat.isNotEmpty == true &&
      _currentAmicale?.gpsLng.isNotEmpty == true;

  double? get latitude => hasGpsCoordinates
      ? double.tryParse(_currentAmicale!.gpsLat)
      : null;

  double? get longitude => hasGpsCoordinates
      ? double.tryParse(_currentAmicale!.gpsLng)
      : null;

  // === SETTERS ===
  Future<void> setAmicale(AmicaleModel? amicale) async {
    _currentAmicale = amicale;
    await _saveToHive();
    notifyListeners();
    debugPrint('🏢 Amicale définie: ${amicale?.name ?? 'null'}');
  }

  Future<void> updateAmicale(AmicaleModel updatedAmicale) async {
    _currentAmicale = updatedAmicale;
    await _saveToHive();
    notifyListeners();
    debugPrint('🏢 Amicale mise à jour: ${updatedAmicale.name}');
  }

  Future<void> clearAmicale() async {
    final amicaleName = _currentAmicale?.name;
    _currentAmicale = null;
    await _clearFromHive();
    notifyListeners();
    debugPrint('🏢 Amicale effacée: $amicaleName');
  }

  // === AUTO-LOAD BASÉ SUR L'UTILISATEUR ===
  Future<void> loadUserAmicale() async {
    final user = CurrentUserService.instance.currentUser;
    if (user?.fkEntite != null) {
      await loadAmicaleById(user!.fkEntite!);
    } else {
      await clearAmicale();
    }
  }

  Future<void> loadAmicaleById(int amicaleId) async {
    try {
      final box = Hive.box<AmicaleModel>(AppKeys.amicaleBoxName);
      final amicale = box.get('current_amicale');

      if (amicale?.id == amicaleId) {
        _currentAmicale = amicale;
        debugPrint('📥 Amicale chargée depuis Hive: ${amicale.name}');
      } else {
        // Si l'amicale n'est pas la bonne, la chercher ou l'effacer
        _currentAmicale = null;
        debugPrint('⚠️ Amicale ${amicaleId} non trouvée dans Hive');
      }
      notifyListeners();
    } catch (e) {
      debugPrint('❌ Erreur chargement amicale depuis Hive: $e');
      _currentAmicale = null;
    }
  }

  // === PERSISTENCE HIVE ===
  Future<void> _saveToHive() async {
    try {
      if (_currentAmicale != null) {
        final box = Hive.box<AmicaleModel>(AppKeys.amicaleBoxName);
        await box.clear();
        await box.put('current_amicale', _currentAmicale!);
        debugPrint('💾 Amicale sauvegardée dans Hive');
      }
    } catch (e) {
      debugPrint('❌ Erreur sauvegarde amicale Hive: $e');
    }
  }

  Future<void> _clearFromHive() async {
    try {
      final box = Hive.box<AmicaleModel>(AppKeys.amicaleBoxName);
      await box.clear();
      debugPrint('🗑️ Box amicale effacée');
    } catch (e) {
      debugPrint('❌ Erreur effacement amicale Hive: $e');
    }
  }

  Future<void> loadFromHive() async {
   try {
      final box = Hive.box<AmicaleModel>(AppKeys.amicaleBoxName);
      _currentAmicale = box.get('current_amicale');

      if (_currentAmicale != null) {
        debugPrint('📥 Amicale chargée depuis Hive: ${_currentAmicale!.name}');
      } else {
        debugPrint('ℹ️ Aucune amicale trouvée dans Hive');
      }

      notifyListeners();
    } catch (e) {
      debugPrint('❌ Erreur chargement amicale depuis Hive: $e');
      _currentAmicale = null;
   }
  }

  // === RESET POUR TESTS ===
  static void reset() {
    _instance?._currentAmicale = null;
    _instance = null;
  }
}
```

---

### Phase 6: Modification du main.dart (20 min)

#### Tâche 6.1: Intégration dans main.dart (20 min)

**Fichier à modifier** : `app/lib/main.dart`
**Actions** :

- [ ] Ajouter l'initialisation des nouveaux services dans `_initializeServices()`
- [ ] Gérer les erreurs d'initialisation
- [ ] Ajouter logging approprié
- [ ] Charger les données au démarrage

**Code à ajouter** :

```dart
Future<void> _initializeServices() async {
  try {
    // Initialiser ApiService en premier
    await ApiService.initialize();
    debugPrint('✅ ApiService singleton initialisé');

    // Les services CurrentUserService et CurrentAmicaleService s'initialisent automatiquement
    // au premier accès via le pattern singleton lazy
    debugPrint('✅ CurrentUserService prêt');
    debugPrint('✅ CurrentAmicaleService prêt');

    // Charger les données depuis Hive au démarrage
    await CurrentUserService.instance.loadFromHive();
    await CurrentAmicaleService.instance.loadFromHive();
    debugPrint('✅ Données utilisateur/amicale chargées depuis Hive');

    await AppInfoService.initialize();
    debugPrint('✅ Tous les services initialisés avec succès');
  } catch (e) {
    debugPrint('❌ Erreur lors de l\'initialisation des services: $e');
    rethrow; // Important pour arrêter l'app si les services critiques échouent
  }
}
```

**Imports à ajouter** :

```dart
import 'package:geosector_app/core/services/current_user_service.dart';
import 'package:geosector_app/core/services/current_amicale_service.dart';
```

---

### Phase 7: Commit intermédiaire de sécurité (10 min)

#### Tâche 7.1: Commit des services créés (10 min)

```bash
# Ajouter tous les nouveaux fichiers
git add app/lib/core/services/current_user_service.dart
git add app/lib/core/services/current_amicale_service.dart
git add app/lib/core/services/api_service.dart
git add app/lib/core/constants/app_keys.dart
git add app/lib/main.dart

# Commit intermédiaire
git commit -m "feat: création services singleton et renommage Box

Services créés:
✅ CurrentUserService singleton pour utilisateur connecté
✅ CurrentAmicaleService singleton pour amicale courante
✅ ApiService transformé en singleton

Box Hive:
✅ Renommage users -> user (plus logique)
✅ Migration automatique des données
✅ Services intégrés dans main.dart

État: Services créés, prêt pour refactorisation repositories"
```

**Actions** :

- [ ] Compiler et vérifier qu'il n'y a pas d'erreurs critiques
- [ ] Faire le commit intermédiaire
- [ ] Documenter l'avancement

---

### Phase 8: Modification de l'App principale (20 min)

#### Tâche 8.1: Refactorisation app.dart (20 min)

**Fichier à modifier** : `app/lib/app.dart`

**Actions** :

- [ ] Supprimer l'injection d'ApiService et UserRepository dans les constructeurs
- [ ] Simplifier l'instanciation des repositories
- [ ] Utiliser les services singleton
- [ ] Simplifier les passages de paramètres

**Avant** :

```dart
// Création avec injection
final apiService = ApiService();
final userRepository = UserRepository(apiService);
```

**Après** :

```dart
// Utilisation des singletons (plus besoin de créer quoi que ce soit)
// Les services sont accessibles globalement via .instance
```

---

### Phase 9: Refactorisation des Repositories (90 min)

#### Tâche 9.1: UserRepository - Refactorisation majeure (40 min)

**Fichier** : `app/lib/core/repositories/user_repository.dart`

**Actions** :

- [ ] Supprimer ApiService du constructeur
- [ ] Utiliser `ApiService.instance` dans les méthodes
- [ ] Remplacer la gestion interne par CurrentUserService
- [ ] Simplifier les méthodes d'accès aux données utilisateur
- [ ] Déléguer la persistence à CurrentUserService
- [ ] Mettre à jour toutes les références à la Box (userBoxName)

**Refactorisation majeure** :

```dart
class UserRepository extends ChangeNotifier {
  // Plus d'injection d'ApiService - constructeur vide
  UserRepository();

  // === DÉLÉGATION AUX SERVICES ===
  UserModel? get currentUser => CurrentUserService.instance.currentUser;
  bool get isLoggedIn => CurrentUserService.instance.isLoggedIn;
  int get userRole => CurrentUserService.instance.userRole;

  // Getters délégués
  bool get isUser => CurrentUserService.instance.isUser;
  bool get isAdminAmicale => CurrentUserService.instance.isAdminAmicale;
  bool get isSuperAdmin => CurrentUserService.instance.isSuperAdmin;
  int? get userId => CurrentUserService.instance.userId;

  // === LOGIN SIMPLIFIÉ ===
  Future<bool> login(String username, String password, {required String type}) async {
    try {
      debugPrint('🔐 Tentative de connexion: $username');

      final apiResult = await ApiService.instance.login(username, password, type: type);

      if (apiResult['status'] == 'success') {
        // Créer l'utilisateur
        final user = _processUserData(
          apiResult['user'],
          apiResult['session_id'],
          apiResult['session_expiry']
        );

        // Sauvegarder via le service (qui gérera automatiquement l'amicale)
        await CurrentUserService.instance.setUser(user);

        // Traiter l'amicale si présente dans la réponse
        if (apiResult['amicale'] != null) {
          final amicale = AmicaleModel.fromJson(apiResult['amicale']);
          await CurrentAmicaleService.instance.setAmicale(amicale);
        }

        // Traiter les autres données (opérations, secteurs, etc.)
        await _processLoginData(apiResult);

        debugPrint('✅ Connexion réussie');
        return true;
      }

      debugPrint('❌ Connexion échouée: ${apiResult['message']}');
      return false;
    } catch (e) {
      debugPrint('❌ Erreur connexion: $e');
      return false;
    }
  }

  // === LOGOUT SIMPLIFIÉ ===
  Future<bool> logout(BuildContext context) async {
    try {
      debugPrint('🚪 Déconnexion en cours...');

      await ApiService.instance.logout();
      await CurrentUserService.instance.clearUser();
      await CurrentAmicaleService.instance.clearAmicale();

      // Nettoyer toutes les autres données
      await _clearAllData();

      if (context.mounted) {
        context.go('/');
      }

      debugPrint('✅ Déconnexion réussie');
      return true;
    } catch (e) {
      debugPrint('❌ Erreur déconnexion: $e');
      if (context.mounted) {
        context.go('/'); // Forcer la redirection même en cas d'erreur
      }
      return false;
    }
  }

  // === NAVIGATION ===
  void navigateAfterLogin(BuildContext context) {
    if (context.mounted) {
      final route = CurrentUserService.instance.getDefaultRoute();
      context.go(route);
    }
  }

  // === MÉTHODES UTILITAIRES ===
  Future<void> updateLastPath(String path) async {
    await CurrentUserService.instance.updateLastPath(path);
  }

  String? getLastPath() => CurrentUserService.instance.getLastPath();

  // Simplifier les getters d'amicale aussi
  AmicaleModel? getCurrentUserAmicale() => CurrentAmicaleService.instance.currentAmicale;

  // Les autres méthodes restent mais sont simplifiées...
}
```

#### Tâche 9.2: AmicaleRepository (20 min)

**Fichier** : `app/lib/core/repositories/amicale_repository.dart`

**Actions** :

- [ ] Supprimer ApiService du constructeur
- [ ] Utiliser `ApiService.instance` dans les méthodes
- [ ] Simplifier l'accès à l'amicale courante
- [ ] Intégrer avec CurrentAmicaleService

#### Tâche 9.3: MembreRepository (15 min)

**Fichier** : `app/lib/core/repositories/membre_repository.dart`

**Actions** :

- [ ] Supprimer ApiService du constructeur
- [ ] Utiliser `ApiService.instance` dans les méthodes
- [ ] Utiliser CurrentUserService pour les vérifications de permissions

#### Tâche 9.4: Autres repositories (15 min)

**Fichiers à traiter** :

- `client_repository.dart`
- `operation_repository.dart`
- `sector_repository.dart`
- `passage_repository.dart`

**Actions par repository** :

- [ ] Supprimer injection ApiService
- [ ] Remplacer par `ApiService.instance`
- [ ] Utiliser CurrentUserService pour les données utilisateur

---

### Phase 10: Modification des Pages principales (120 min)

#### Tâche 10.1: Pages d'authentification (30 min)

**Fichiers** :

- `app/lib/presentation/auth/login_page.dart`
- `app/lib/presentation/auth/register_page.dart`

**Actions** :

- [ ] Supprimer UserRepository des constructeurs
- [ ] Utiliser CurrentUserService directement
- [ ] Simplifier la logique de navigation post-login

**Exemple pour LoginPage** :

```dart
class LoginPage extends StatefulWidget {
  // Plus besoin d'injection
  const LoginPage({super.key});

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  // Utilisation directe des services
  Future<void> _handleLogin() async {
    final userRepo = UserRepository(); // Sans injection, constructeur vide
    final success = await userRepo.login(username, password, type: type);

    if (success && mounted) {
      // Navigation automatique basée sur le rôle
      final route = CurrentUserService.instance.getDefaultRoute();
      context.go(route);
    }
  }

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: CurrentUserService.instance,
      builder: (context, child) {
        // Redirection automatique si déjà connecté
        if (CurrentUserService.instance.isLoggedIn) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            final route = CurrentUserService.instance.getDefaultRoute();
            context.go(route);
          });
        }

        return Scaffold(
          // ... UI de login
        );
      },
    );
  }
}
```

#### Tâche 10.2: Dashboard pages (30 min)

**Fichiers** :

- `app/lib/presentation/admin/admin_dashboard_page.dart`
- `app/lib/presentation/user/user_dashboard_page.dart`

**Actions** :

- [ ] Supprimer injections de services des constructeurs
- [ ] Utiliser CurrentUserService et CurrentAmicaleService directement
- [ ] Simplifier l'affichage des données utilisateur/amicale

**Exemple pour AdminDashboardPage** :

```dart
class AdminDashboardPage extends StatelessWidget {
  const AdminDashboardPage({super.key});

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: Listenable.merge([
        CurrentUserService.instance,
        CurrentAmicaleService.instance,
      ]),
      builder: (context, child) {
        final userService = CurrentUserService.instance;
        final amicaleService = CurrentAmicaleService.instance;

        // Vérification d'authentification
        if (!userService.isLoggedIn) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            context.go('/');
          });
          return const SizedBox.shrink();
        }

        return Scaffold(
          appBar: AppBar(
            title: Text('Bonjour ${userService.userFirstName ?? userService.userName}'),
            actions: [
              IconButton(
                icon: const Icon(Icons.logout),
                onPressed: () => _handleLogout(context),
              ),
            ],
          ),
          body: SingleChildScrollView(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // Widget d'informations utilisateur
                const UserInfoWidget(),

                const SizedBox(height: 16),

                // Widget d'informations amicale
                if (amicaleService.hasAmicale)
                  const AmicaleInfoWidget(),

                const SizedBox(height: 24),

                // Actions selon le rôle
                if (userService.isSuperAdmin)
                  _buildSuperAdminActions(context),

                if (userService.isAdminAmicale)
                  _buildAmicaleAdminActions(context),
              ],
            ),
          ),
        );
      },
    );
  }

  Future<void> _handleLogout(BuildContext context) async {
    final userRepo = UserRepository();
    await userRepo.logout(context);
  }

  // ... autres méthodes
}
```

#### Tâche 10.3: Pages de gestion (30 min)

**Fichiers** :

- `app/lib/presentation/admin/admin_amicale_page.dart`
- `app/lib/presentation/admin/admin_statistics_page.dart`
- `app/lib/presentation/user/map_page.dart`

**Actions** :

- [ ] Supprimer paramètres UserRepository et ApiService
- [ ] Utiliser les services singleton
- [ ] Simplifier l'accès aux données

#### Tâche 10.4: Pages formulaires (30 min)

**Fichiers contenant des formulaires** :

- Forms de création/édition
- Pages de configuration
- Pages de paramètres

**Actions** :

- [ ] Identifier tous les formulaires utilisant UserRepository
- [ ] Remplacer par CurrentUserService/CurrentAmicaleService
- [ ] Simplifier les validations de permissions

---

### Phase 11: Modification des Widgets (90 min)

#### Tâche 11.1: Création de widgets d'information (30 min)

**Nouveaux widgets à créer** :

- `app/lib/presentation/widgets/user/user_info_widget.dart`
- `app/lib/presentation/widgets/user/amicale_info_widget.dart`

**Code UserInfoWidget** :

```dart
import 'package:flutter/material.dart';
import 'package:geosector_app/core/services/current_user_service.dart';

class UserInfoWidget extends StatelessWidget {
  const UserInfoWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: CurrentUserService.instance,
      builder: (context, child) {
        final userService = CurrentUserService.instance;
        final user = userService.currentUser;

        if (user == null) {
          return const SizedBox.shrink();
        }

        return Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  children: [
                    const Icon(Icons.person),
                    const SizedBox(width: 8),
                    Text(
                      'Informations utilisateur',
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                  ],
                ),
                const SizedBox(height: 12),
                Text(
                  '${user.firstName} ${user.name}',
                  style: Theme.of(context).textTheme.titleLarge,
                ),
                Text(user.email),
                Text('Rôle: ${userService.getRoleLabel()}'),
                if (user.phone?.isNotEmpty == true)
                  Text('Tél: ${user.phone}'),
                if (user.mobile?.isNotEmpty == true)
                  Text('Mobile: ${user.mobile}'),
              ],
            ),
          ),
        );
      },
    );
  }
}
```

**Code AmicaleInfoWidget** :

```dart
import 'package:flutter/material.dart';
import 'package:geosector_app/core/services/current_amicale_service.dart';

class AmicaleInfoWidget extends StatelessWidget {
  const AmicaleInfoWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: CurrentAmicaleService.instance,
      builder: (context, child) {
        final amicaleService = CurrentAmicaleService.instance;
        final amicale = amicaleService.currentAmicale;

        if (amicale == null) {
          return const SizedBox.shrink();
        }

        return Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  children: [
                    const Icon(Icons.business),
                    const SizedBox(width: 8),
                    Text(
                      'Mon amicale',
                      style: Theme.of(context).textTheme.titleMedium,
                    ),
                  ],
                ),
                const SizedBox(height: 12),
                Text(
                  amicale.name,
                  style: Theme.of(context).textTheme.titleLarge,
                ),
                if (amicaleService.amicaleFullAddress?.isNotEmpty == true)
                  Text(amicaleService.amicaleFullAddress!),
                if (amicale.email.isNotEmpty)
                  Text('Email: ${amicale.email}'),
                if (amicale.phone.isNotEmpty)
                  Text('Tél: ${amicale.phone}'),
                if (amicale.mobile.isNotEmpty)
                  Text('Mobile: ${amicale.mobile}'),
                Row(
                  children: [
                    Icon(
                      amicale.chkActive ? Icons.check_circle : Icons.cancel,
                      color: amicale.chkActive ? Colors.green : Colors.red,
                      size: 16,
                    ),
                    const SizedBox(width: 4),
                    Text(amicale.chkActive ? 'Active' : 'Inactive'),
                  ],
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}
```

#### Tâche 11.2: Widgets de tableaux (30 min)

**Fichiers** :

- `app/lib/presentation/widgets/tables/amicale_table_widget.dart`
- `app/lib/presentation/widgets/tables/membre_table_widget.dart`
- `app/lib/presentation/widgets/tables/user_table_widget.dart`

**Actions** :

- [ ] Supprimer injections de repositories des constructeurs
- [ ] Utiliser CurrentUserService pour les vérifications de permissions
- [ ] Simplifier la logique d'affichage conditionnel

#### Tâche 11.3: Widgets de formulaires (30 min)

**Fichiers** :

- `app/lib/presentation/widgets/forms/amicale_form.dart`
- `app/lib/presentation/widgets/forms/membre_form.dart`

**Actions** :

- [ ] Remplacer injections par services singleton
- [ ] Utiliser CurrentUserService pour les validations
- [ ] Simplifier les callbacks et la gestion d'état

---

### Phase 12: Mise à jour du Router et Navigation (45 min)

#### Tâche 12.1: Configuration GoRouter (30 min)

**Fichier** : `app/lib/core/routing/app_router.dart`

**Actions** :

- [ ] Supprimer injection de services dans les routes
- [ ] Utiliser CurrentUserService pour les guards d'authentification
- [ ] Simplifier la création des pages
- [ ] Adapter le middleware d'authentification

**Exemple de refactorisation** :

**Avant** :

```dart
GoRoute(
  path: '/admin',
  builder: (context, state) => AdminPage(
    userRepository: UserRepository(apiService),
    amicaleRepository: AmicaleRepository(apiService),
  ),
)
```

**Après** :

```dart
GoRoute(
  path: '/admin',
  builder: (context, state) => const AdminPage(),
  redirect: (context, state) => AuthGuard.checkAdminAccess(),
)
```

#### Tâche 12.2: Création AuthGuard (15 min)

**Fichier à créer** : `app/lib/core/routing/auth_guard.dart`

**Code AuthGuard** :

```dart
import 'package:geosector_app/core/services/current_user_service.dart';

class AuthGuard {
  static String? checkAuth() {
    final userService = CurrentUserService.instance;

    if (!userService.isLoggedIn) {
      return '/';
    }

    return null; // Accès autorisé
  }

  static String? checkAdminAccess() {
    final userService = CurrentUserService.instance;

    if (!userService.isLoggedIn) {
      return '/';
    }

    if (!userService.canAccessAdmin) {
      return '/user';
    }

    return null; // Accès autorisé
  }

  static String? checkSuperAdminAccess() {
    final userService = CurrentUserService.instance;

    if (!userService.isLoggedIn) {
      return '/';
    }

    if (!userService.isSuperAdmin) {
      return userService.canAccessAdmin ? '/admin' : '/user';
    }

    return null; // Accès autorisé
  }
}
```

---

### Phase 13: Tests et validation (60 min)

#### Tâche 13.1: Tests de compilation (15 min)

**Actions** :

- [ ] `flutter pub get`
- [ ] `flutter analyze`
- [ ] Corriger erreurs de compilation
- [ ] Vérifier warnings

#### Tâche 13.2: Tests fonctionnels de base (30 min)

**Scénarios à tester** :

- [ ] Démarrage de l'application
- [ ] Authentification utilisateur avec différents rôles
- [ ] Navigation entre pages
- [ ] Affichage des données utilisateur/amicale
- [ ] Déconnexion et reconnexion
- [ ] Persistence des données au redémarrage
- [ ] Migration de la Box users -> user

#### Tâche 13.3: Tests des services singleton (15 min)

**Actions** :

- [ ] Tester CurrentUserService.instance
- [ ] Tester CurrentAmicaleService.instance
- [ ] Vérifier la persistence Hive avec nouvelle Box
- [ ] Tester les listeners et notifications

---

### Phase 14: Tests unitaires (45 min)

#### Tâche 14.1: Tests ApiService (15 min)

**Fichier** : `test/core/services/api_service_test.dart`

**Actions** :

- [ ] Adapter tests existants pour le singleton
- [ ] Tester initialisation
- [ ] Tester thread-safety
- [ ] Mocker les appels réseau

#### Tâche 14.2: Tests CurrentUserService (15 min)

**Fichier** : `test/core/services/current_user_service_test.dart`

**Actions** :

- [ ] Créer tests pour le singleton utilisateur
- [ ] Tester setUser/clearUser
- [ ] Tester persistence Hive avec nouvelle Box
- [ ] Tester les getters de rôles

#### Tâche 14.3: Tests CurrentAmicaleService (15 min)

**Fichier** : `test/core/services/current_amicale_service_test.dart`

**Actions** :

- [ ] Créer tests pour le singleton amicale
- [ ] Tester setAmicale/clearAmicale
- [ ] Tester loadUserAmicale
- [ ] Tester les getters utiles

---

### Phase 15: Optimisations et nettoyage (45 min)

#### Tâche 15.1: Optimisation des performances (20 min)

**Actions** :

- [ ] Vérifier la réactivité des ListenableBuilder
- [ ] Optimiser les notifications des services
- [ ] Éliminer les rebuilds inutiles
- [ ] Vérifier les memory leaks

#### Tâche 15.2: Nettoyage du code (25 min)

**Actions** :

- [ ] Supprimer les imports inutiles
- [ ] Nettoyer les constructeurs simplifiés
- [ ] Uniformiser le code style
- [ ] Supprimer les fichiers backup
- [ ] Mettre à jour la documentation

---

### Phase 16: Commit final et documentation (30 min)

#### Tâche 16.1: Commit final (15 min)

```bash
# Ajouter tous les fichiers modifiés
git add .

# Commit final avec description complète
git commit -m "feat: refactorisation complète vers architecture singleton

🚀 TRANSFORMATION MAJEURE:

API & Services:
✅ ApiService transformé en singleton thread-safe
✅ CurrentUserService singleton pour utilisateur connecté
✅ CurrentAmicaleService singleton pour amicale courante

Hive Box:
✅ Renommage users -> user (logique pour utilisateur unique)
✅ Migration automatique des données existantes
✅ Persistence optimisée dans les nouveaux services

Repositories:
✅ UserRepository simplifié (plus d'injection ApiService)
✅ AmicaleRepository simplifié
✅ MembreRepository corrigé selon vrais champs modèle
✅ ClientRepository corrigé selon vrais champs modèle
✅ Tous les repositories utilisent ApiService.instance

Models:
✅ MembreModel corrigé avec les vrais champs
✅ ClientModel complété avec champs manquants
✅ Méthodes create/update simplifiées dans repositories

UI/UX:
✅ Pages sans injections de dépendances
✅ Widgets UserInfoWidget et AmicaleInfoWidget réactifs
✅ Navigation automatique basée sur les rôles
✅ ListenableBuilder pour réactivité en temps réel

Architecture:
✅ Constructeurs ultra-simplifiés (plus de prop drilling)
✅ AuthGuard centralisé pour sécurité
✅ Code maintenable et performance optimisée

Tests:
✅ Tests unitaires pour tous les nouveaux services
✅ Validation fonctionnelle complète

BREAKING CHANGES:
- Box Hive users renommée en user
- Constructeurs de pages/widgets simplifiés
- Pattern d'accès aux données utilisateur/amicale changé
- Champs de modèles corrigés selon spécifications

MIGRATION: Automatique au démarrage de l'app"

# Push de la branche
git push origin singletons
```

#### Tâche 16.2: Documentation finale (15 min)

**Actions** :

- [ ] Mettre à jour le README principal
- [ ] Documenter les nouveaux services
- [ ] Créer guide de migration pour l'équipe
- [ ] Mettre à jour les commentaires dans le code

**Documentation à créer** : `app/SINGLETONS_GUIDE.md`

````markdown
# Guide des Services Singleton

## Vue d'ensemble

Cette refactorisation introduit 3 services singleton pour simplifier l'architecture:

### CurrentUserService

```dart
// Accès global à l'utilisateur connecté
final user = CurrentUserService.instance.currentUser;
final isAdmin = CurrentUserService.instance.canAccessAdmin;

// Réactivité
ListenableBuilder(
  listenable: CurrentUserService.instance,
  builder: (context, child) => Text(user?.name ?? 'Anonyme'),
)
```
````

### CurrentAmicaleService

```dart
// Accès global à l'amicale courante
final amicale = CurrentAmicaleService.instance.currentAmicale;
final hasGps = CurrentAmicaleService.instance.hasGpsCoordinates;
```

### ApiService

```dart
// Singleton API
final response = await ApiService.instance.get('/endpoint');
```

## Corrections modèles

### MembreModel

- Corrigé selon les vrais champs : `isActive`, `role`, etc.
- Plus de champs inventés

### ClientModel

- Ajout champs manquants : `chkStripe`, `createdAt`, `updatedAt`

## Migration

- Box `users` renommée en `user` (migration automatique)
- Plus d'injection de dépendances dans les constructeurs
- Widgets réactifs avec ListenableBuilder
- Repositories simplifiés

```

---

## 📁 Structure finale des fichiers modifiés/créés

### ✅ Fichiers déjà traités
```
