# Module de Chat pour Geosector

## Vue d'ensemble

Module de chat intégré pour l'application Geosector permettant la communication entre membres, administrateurs d'amicale et super-administrateurs, avec synchronisation temps réel et support hors ligne.

## Architecture actuelle

### Gestion du cycle de vie avec ChatManager

Le module chat utilise un service singleton `ChatManager` pour gérer son cycle de vie :

#### Initialisation au login
```dart
// Dans UserRepository.login()
await ChatManager.instance.initializeChat();
```

#### Arrêt au logout
```dart
// Dans UserRepository.logout()
ChatManager.instance.dispose();
```

#### Gestion complète du cycle de vie de l'application
```dart
// Dans GeosectorApp avec WidgetsBindingObserver
void didChangeAppLifecycleState(AppLifecycleState state) {
  switch (state) {
    case AppLifecycleState.resumed:
      // App au premier plan - reprendre les syncs
      ChatManager.instance.resumeSyncs();
      break;
      
    case AppLifecycleState.paused:
      // App en arrière-plan - pause pour économiser batterie
      ChatManager.instance.pauseSyncs();
      break;
      
    case AppLifecycleState.inactive:
      // État transitoire (appel entrant) - ne rien faire
      break;
      
    case AppLifecycleState.detached:
      // App vraiment fermée (rare sur mobile) - nettoyer
      ChatManager.instance.dispose();
      break;
      
    case AppLifecycleState.hidden:
      // App masquée (Flutter 3.13+) - pause comme paused
      ChatManager.instance.pauseSyncs();
      break;
  }
}
```

#### Points clés du ChatManager
- **Singleton** : Une seule instance pour toute l'application
- **Initialisation unique** : Méthode idempotente, peut être appelée plusieurs fois sans effet
- **Syncs en arrière-plan** : Timer de 15 secondes actif uniquement quand l'app est au premier plan
- **Gestion intelligente** : Vérifie la connexion utilisateur avant chaque utilisation
- **Économie de batterie** : Pause automatique des syncs quand l'app passe en arrière-plan
- **Sync de rattrapage** : Synchronisation immédiate au retour au premier plan
- **Nettoyage automatique** : Arrêt propre des timers et libération des ressources

#### Comportement par état d'application

| État | Action | Raison |
|------|--------|--------|
| **resumed** | `resumeSyncs()` | App au premier plan, syncs actives + sync de rattrapage |
| **paused** | `pauseSyncs()` | App en arrière-plan, économie de batterie |
| **inactive** | Rien | État transitoire (ex: appel entrant sur iOS) |
| **detached** | `dispose()` | App vraiment fermée (très rare sur mobile) |
| **hidden** | `pauseSyncs()` | App masquée (Flutter 3.13+), traité comme paused |

**Note importante** : Sur mobile, "fermer" l'app (swipe) la met généralement en `paused`, pas en `detached`. Le chat arrête donc ses syncs pour économiser la batterie mais reste initialisé pour un retour rapide.

### 1. Structure des modèles

#### Room (Conversation)
```dart
@HiveType(typeId: 50)
class Room {
  String id;                              // UUID de la conversation
  String title;                           // Nom du destinataire ou titre du groupe
  String type;                            // 'private', 'group', 'broadcast'
  DateTime createdAt;                     // Date de création
  DateTime? updatedAt;                    // Date de dernière modification
  String? lastMessage;                    // Dernier message (preview)
  DateTime? lastMessageAt;                // Date du dernier message
  int unreadCount;                        // Nombre de messages non lus
  List<Map<String, dynamic>>? recentMessages; // 5 derniers messages (cache)
}
```

#### Message
```dart
@HiveType(typeId: 51)
class Message {
  String id;                              // UUID du message
  String roomId;                          // ID de la conversation
  String content;                         // Contenu du message
  int senderId;                           // ID de l'expéditeur
  String senderName;                      // Nom de l'expéditeur
  String? senderFirstName;                // Prénom de l'expéditeur
  DateTime sentAt;                        // Date d'envoi
  bool isMe;                              // Message de l'utilisateur actuel
  bool isRead;                            // Message lu/non lu
  int? readCount;                         // Nombre de personnes ayant lu
}
```

### 2. Endpoints API implémentés

#### Synchronisation initiale (au login)
```http
GET /api/chat/rooms

Réponse:
{
  "status": "success",
  "sync_timestamp": "2025-08-25T11:45:00Z",
  "rooms": [
    {
      "id": "c6a4eaef-8efe-49c6-b896-11b4ca1a8056",
      "title": "VAISSAIRE",
      "type": "private",
      "created_at": "2025-08-25 11:43:32",
      "updated_at": "2025-08-25 11:44:15",
      "last_message": "Salut, ça va ?",
      "last_message_at": "2025-08-25 11:44:15",
      "unread_count": 10,
      "participant_count": 2,
      "participants": [...],
      "recent_messages": [
        {
          "id": "msg-123",
          "content": "Salut Pierre !",
          "sender_id": 9999985,
          "sender_name": "VAISSAIRE",
          "sender_first_name": "Clément",
          "sent_at": "2025-08-25 11:44:00",
          "is_read": false,
          "is_mine": false
        }
      ]
    }
  ]
}
```

#### Synchronisation incrémentale (toutes les 15 secondes)
```http
GET /api/chat/rooms?updated_after=2025-08-25T11:45:00Z

Réponse si changements:
{
  "status": "success",
  "sync_timestamp": "2025-08-25T11:45:15Z",
  "has_changes": true,
  "rooms": [
    // Seulement les rooms modifiées
  ]
}

Réponse si aucun changement:
{
  "status": "success",
  "sync_timestamp": "2025-08-25T11:45:15Z",
  "has_changes": false,
  "rooms": []
}
```

#### Chargement des messages (marque automatiquement comme lu)
```http
GET /api/chat/rooms/{room_id}/messages?limit=50

Réponse:
{
  "status": "success",
  "messages": [...],
  "has_more": true,
  "marked_as_read": 10,  // Messages marqués comme lus automatiquement
  "unread_count": 0      // Messages non lus restants
}
```

#### Création de conversation
```http
POST /api/chat/rooms
{
  "title": "Nom du destinataire",
  "type": "private",
  "participants": [user_id],
  "initial_message": "Message optionnel"
}
```

#### Envoi de message
```http
POST /api/chat/rooms/{room_id}/messages
{
  "content": "Contenu du message"
}
```

#### Récupération des destinataires possibles
```http
GET /api/chat/recipients?search=nom

Réponse:
{
  "status": "success",
  "recipients": [
    {
      "id": 10016609,
      "name": "ANDREZIEUX",
      "first_name": "Boris",
      "sect_name": "Tournée Boris",
      "role": 1,
      "entite_id": 5,
      "entite_name": "AMICALE TEST DEV PIERRE"
    }
  ]
}
```

### 3. Règles métier par rôle

#### Membre (role = 1)
- Peut contacter les membres de son amicale
- Peut contacter les administrateurs de son amicale
- Ne peut pas contacter les super-admins directement

#### Admin Amicale (role = 2)
- Peut contacter tous les membres de son amicale
- Peut contacter les super-admins (GEOSECTOR)
- Bouton "Toute l'Amicale" pour message groupé
- Bouton "GEOSECTOR" pour contacter les super-admins

#### Super Admin (role = 9)
- Peut contacter tous les administrateurs d'amicale
- Peut créer des groupes broadcast
- Accès à toutes les conversations

### 4. Flux de synchronisation optimisé

```
1. Login/Initialisation
   └─> UserRepository.login()
       └─> ChatManager.instance.initializeChat()
           └─> ChatModule.init() avec infos utilisateur
               └─> GET /api/chat/rooms (sync complète initiale)
               └─> Stockage dans Hive
               └─> Timer démarré (15 secondes)

2. Syncs en arrière-plan (pendant toute la session)
   └─> Timer toutes les 15 secondes
       └─> Vérification connectivité
       └─> GET /api/chat/rooms?updated_after=...
           └─> Si has_changes=false → skip
           └─> Si has_changes=true → merge changements

3. Ouverture page communication
   └─> Vérification ChatManager.isReady
   └─> Affichage immédiat depuis Hive (pas de requête)
   └─> ValueListenableBuilder pour UI réactive

4. Ouverture conversation spécifique
   └─> GET /api/chat/rooms/{id}/messages
       └─> Marque automatiquement comme lu côté API
       └─> Met à jour unread_count localement
       └─> Cache les messages dans Hive

5. Pull to refresh (action utilisateur)
   └─> GET /api/chat/rooms (sync complète forcée)
       └─> Remplacement total des données

6. Logout/Fermeture app
   └─> ChatManager.instance.dispose()
       └─> Arrêt du timer de sync
       └─> Nettoyage des ressources
```

### 5. Gestion hors ligne

#### Stockage Hive
- **Box chat_rooms** : Conversations avec recent_messages
- **Box chat_messages** : Messages (max 100 par room)
- **Box chat_metadata** : Timestamp de dernière sync

#### Détection de connectivité
- Utilise `ConnectivityService` de l'app principale
- Pas de sync si déconnecté
- Affichage depuis cache local si hors ligne

#### Persistance
- `last_sync_timestamp` sauvegardé dans Hive
- Survit aux redémarrages d'application
- Reprise automatique des syncs incrémentales

### 6. Widgets principaux

#### ChatModule
```dart
// Initialisation au login via ChatManager
// Ne pas appeler directement - utiliser ChatManager
await ChatManager.instance.initializeChat();

// Vérification avant utilisation dans les pages
if (ChatManager.instance.isReady) {
  return ChatModule.getRoomsPage();
}
```

#### RoomsPageEmbedded
- Liste des conversations
- Pas de requête à l'ouverture (données depuis Hive)
- Pull to refresh pour sync manuelle
- Badge de messages non lus
- Affichage des 5 derniers messages (recent_messages)
- Actualisation automatique via ValueListenableBuilder
- **Affichage amélioré des conversations** :
  - Titre avec prénom + nom complet (ex: "Boris ANDREZIEUX")
  - Avatar avec 2 initiales (ex: "BA" pour Boris ANDREZIEUX)
  - Gestion intelligente des titres spéciaux ("GS" pour GEOSECTOR, "AA" pour Administrateurs Amicale)

#### ChatPage
- Affichage d'une conversation
- Chargement automatique + marquage comme lu
- Pagination des messages anciens
- Envoi de messages avec état temporaire
- Mise à jour temps réel des messages reçus

#### RecipientSelector
- Sélection des destinataires pour admin (role = 2)
  - Bouton "GEOSECTOR" pour super-admins
  - Bouton "Toute l'Amicale" (inclut l'expéditeur)
  - Sélection individuelle (exclut super-admins)
- Affichage prénom + nom sur première ligne
- Affichage du sectName uniquement (pas de fallback entité)
- Avatar avec 2 initiales (prénom + nom)

### 7. Optimisations implémentées

#### Performance
- ✅ Sync incrémentale avec `updated_after`
- ✅ Skip si `has_changes=false`
- ✅ Cache des 5 derniers messages par room
- ✅ Max 100 messages en cache par room
- ✅ Pas de requête à l'ouverture des pages

#### Bande passante
- ✅ Sync complète uniquement au login
- ✅ Sync incrémentale toutes les 15 secondes
- ✅ Transmission uniquement des changements
- ✅ Compression des réponses API

#### UX
- ✅ Affichage immédiat depuis cache
- ✅ Marquage automatique comme lu
- ✅ Badge de non-lus en temps réel via `ChatInfoService`
- ✅ Pull to refresh disponible
- ✅ Support hors ligne complet
- ✅ Affichage prénom + nom dans les titres de conversations
- ✅ Avatars avec initiales intelligentes (2 lettres)

### 8. Configuration YAML

Le fichier `assets/chat_config.yaml` définit :
- Permissions par rôle
- Messages UI localisés
- Règles de communication entre rôles
- Couleurs et labels des rôles

### 9. Intégration Hive

#### TypeAdapters
Les adapters sont enregistrés dans `HiveAdapters.registerAll()` au démarrage :
```dart
// Chat adapters - TypeIds 50-51
if (!Hive.isAdapterRegistered(50)) {
  Hive.registerAdapter(RoomAdapter());
}
if (!Hive.isAdapterRegistered(51)) {
  Hive.registerAdapter(MessageAdapter());
}
```

#### Boxes gérées centralement
Les boxes sont ouvertes dans `splash_page.dart` avec toutes les autres :
```dart
// Dans HiveService._createSingleBox()
case 'Room':
  await Hive.openBox<Room>(config.name);
  break;
case 'Message':
  await Hive.openBox<Message>(config.name);
  break;
```

#### Utilisation dans ChatService
```dart
// Le ChatService utilise les boxes déjà ouvertes
_roomsBox = Hive.box<Room>(AppKeys.chatRoomsBoxName);
_messagesBox = Hive.box<Message>(AppKeys.chatMessagesBoxName);
// Pas d'ouverture de box - juste utilisation
```

TypeIds réservés :
- 50 : Room
- 51 : Message

### 10. Système de badges pour messages non lus

#### Architecture du système de badges
- **`ChatInfoService`** : Service singleton avec `ChangeNotifier` qui maintient le compte global des messages non lus
- **`BadgedIcon`** : Widget avec `AnimatedBuilder` qui écoute `ChatInfoService` et affiche automatiquement le badge
- **`createBadgedNavigationDestination`** : Helper pour créer des destinations de navigation avec badge

#### Flux de mise à jour des badges
```
Sync toutes les 15 secondes
    ↓
ChatService calcule le total des unreadCount
    ↓
ChatInfoService.updateStats() appelé
    ↓
notifyListeners() déclenché
    ↓
BadgedIcon se redessine automatiquement
```

#### Intégration dans la navigation
- **Mobile** : Badge affiché dans la `NavigationBar` inférieure
- **Desktop** : Badge affiché dans la sidebar (version normale et minimisée)
- **ResponsiveNavigation** : Préserve les `BadgedIcon` sans les recréer

### 11. Points d'attention

#### Sécurité
- Token Bearer dans tous les appels API
- Vérification des permissions côté serveur
- Pas de données sensibles en cache local
- ChatManager vérifie l'authentification avant chaque utilisation

#### Maintenance
- Logs détaillés pour debug (à retirer en production)
- Sync incrémentale limite la charge réseau
- Gestion des erreurs avec fallback sur cache
- Timer de sync s'arrête automatiquement au logout/fermeture

#### Evolution
- Prêt pour WebSocket (remplacement du polling)
- Support des pièces jointes prévu
- Indicateurs de frappe possibles
- Architecture modulaire facilite les évolutions

## Affichage des noms et avatars

### Format d'affichage
- **Conversations privées** : Prénom + Nom (ex: "Boris ANDREZIEUX")
- **Conversations de groupe** : Liste des participants avec prénoms + noms
- **Avatars** : 2 initiales (prénom + nom) ou codes spéciaux pour groupes

### Initiales intelligentes
| Type de conversation | Affichage | Avatar |
|---------------------|-----------|---------|
| Personne seule | "Boris ANDREZIEUX" | "BA" |
| GEOSECTOR Support | "GEOSECTOR Support" | "GS" |
| Administrateurs Amicale | "Administrateurs Amicale" | "AA" |
| Groupe Amicale | "Amicale - Tous les membres" | "AM" |
| Groupe multiple | "Boris A., Pierre D. et 3 autres" | Premières initiales |

## Prochaines étapes

### Court terme
- [x] Système de badges pour messages non lus
- [x] Affichage prénom + nom dans les conversations
- [ ] Retirer les logs de debug en production
- [ ] Ajouter la pagination infinie des messages
- [ ] Implémenter la recherche dans les conversations

### Moyen terme
- [ ] Support des pièces jointes (images, fichiers)
- [ ] Indicateurs de frappe en temps réel
- [ ] Notifications push pour nouveaux messages

### Long terme
- [ ] Migration vers WebSocket pour temps réel
- [ ] Support des messages vocaux
- [ ] Chiffrement end-to-end des messages

## Migration depuis l'ancien système

Le nouveau système remplace complètement l'ancien avec :
- Meilleure gestion des permissions par rôle
- Synchronisation optimisée avec sync incrémentale
- Support hors ligne natif via Hive
- UI plus réactive avec ValueListenableBuilder
- ChatManager pour gestion centralisée du cycle de vie
- Initialisation automatique au login
- Syncs en arrière-plan pendant toute la session

## Maintenance

### Monitoring
- Vérifier les logs de sync toutes les 15 secondes
- Surveiller la taille des boxes Hive
- Analyser les erreurs de connectivité

### Debug
Les logs actuels affichent :
- 🔄 Synchronisations (complètes/incrémentales)
- ✅ Rooms parsées et sauvegardées
- 📵 Détection perte de connexion
- ⏰ Timestamps de synchronisation
- 💾 Opérations Hive

### Performance
- Sync incrémentale : < 100ms si pas de changements
- Sync complète : < 500ms pour 50 conversations
- Affichage initial : instantané (depuis cache)
- Envoi message : < 200ms en ligne