# TODO - Cache Web pour tuiles Mapbox

## Contexte

**Problème** : En PROD, les admins travaillent intensivement sur la plateforme web et la carte. Actuellement, les tuiles Mapbox ne sont pas mises en cache sur le web, ce qui provoque :
- Téléchargements répétés des mêmes tuiles
- Consommation excessive de bande passante
- Navigation peu fluide
- Coûts API Mapbox potentiellement élevés

**Cause** : Mapbox envoie des headers `Cache-Control: no-cache` qui empêchent le cache navigateur par défaut, et `flutter_map_cache` ne supporte pas la plateforme web.

**Solution** : Implémenter un cache manuel des tuiles via IndexedDB en Dart, uniquement pour le web.

## Architecture technique

### Flux de données

```
flutter_map demande une tuile
    ↓
CachedWebTileProvider (nouveau)
    ↓
WebTileCacheService.getTile()
    ↓
Vérifie IndexedDB
    ↓
┌─────────────┴─────────────┐
│                           │
Cache HIT                Cache MISS
│                           │
Retourne image            Télécharge depuis Mapbox
depuis IndexedDB              ↓
                          Stocke dans IndexedDB
                              ↓
                          Retourne image
```

### Composants

1. **WebTileCacheService** (Singleton)
   - Gère IndexedDB pour stocker les tuiles
   - Méthodes : `getTile()`, `storeTile()`, `clearCache()`, `clearExpiredTiles()`
   - Durée de cache : 30 jours (aligné avec mobile/desktop)

2. **CachedWebTileProvider** (Custom TileProvider)
   - Implémente `TileProvider` de `flutter_map`
   - Utilise `WebTileCacheService` pour récupérer/stocker
   - Fallback sur téléchargement direct si erreur

3. **MapboxMap** (Modifié)
   - Utilise `CachedWebTileProvider` sur web
   - Garde `HiveCacheStore` sur mobile/desktop

## Fichiers à créer/modifier

### ✅ Fichiers à créer

#### 1. `lib/core/services/web_tile_cache_service.dart`

Service singleton pour gérer le cache IndexedDB des tuiles Mapbox.

**Responsabilités** :
- Initialiser la base IndexedDB `mapbox_tiles_cache`
- Stocker les tuiles avec clé unique (URL de la tuile)
- Récupérer les tuiles depuis le cache
- Gérer l'expiration (30 jours)
- Nettoyer les tuiles expirées

**Structure IndexedDB** :
```
Database: mapbox_tiles_cache
ObjectStore: tiles
  - key: String (URL de la tuile)
  - value: Object {
      data: Uint8List (bytes de l'image)
      timestamp: int (millisecondsSinceEpoch)
    }
```

**API publique** :
```dart
class WebTileCacheService {
  static final instance = WebTileCacheService._();

  Future<void> initialize();
  Future<Uint8List?> getTile(String url);
  Future<void> storeTile(String url, Uint8List data);
  Future<void> clearCache();
  Future<void> clearExpiredTiles();
}
```

**Dépendances** :
- `dart:indexed_db` (natif web)
- `dart:typed_data`
- `package:flutter/foundation.dart` (kIsWeb)

#### 2. `lib/presentation/widgets/cached_web_tile_provider.dart`

TileProvider custom qui utilise le cache IndexedDB.

**Responsabilités** :
- Implémenter `TileProvider` de `flutter_map`
- Vérifier le cache avant téléchargement
- Télécharger et stocker si absent du cache
- Fallback sur comportement par défaut si erreur

**API publique** :
```dart
class CachedWebTileProvider extends TileProvider {
  final String accessToken;
  final String styleId;

  CachedWebTileProvider({
    required this.accessToken,
    required this.styleId,
  });

  @override
  ImageProvider getImage(TileCoordinates coordinates, TileLayer options);
}
```

**Logique** :
1. Construire l'URL de la tuile
2. Appeler `WebTileCacheService.getTile(url)`
3. Si trouvée → `MemoryImage(cachedData)`
4. Sinon → Télécharger → Stocker → `MemoryImage(downloadedData)`

**Dépendances** :
- `flutter_map`
- `WebTileCacheService`
- `dart:typed_data`
- `package:http/http.dart` (ou `Dio` si préféré)

### ✅ Fichiers à modifier

#### 3. `lib/presentation/widgets/mapbox_map.dart`

Modifier pour utiliser le nouveau provider sur web.

**Modifications** :
- Lignes 96-139 : Refactoriser `_initializeCache()`
- Utiliser `CachedWebTileProvider` quand `kIsWeb == true`
- Garder `HiveCacheStore` pour mobile/desktop
- Initialiser `WebTileCacheService` au démarrage sur web

**Avant** (lignes 98-103) :
```dart
if (kIsWeb) {
  // Pas de cache sur Web (non supporté)
  setState(() {
    _cacheInitialized = true;
  });
  return;
}
```

**Après** :
```dart
if (kIsWeb) {
  // Cache manuel via IndexedDB
  await WebTileCacheService.instance.initialize();
  setState(() {
    _cacheInitialized = true;
  });
  return;
}
```

**TileLayer config** (ligne ~180) :
```dart
TileLayer(
  urlTemplate: 'https://api.mapbox.com/styles/v1/mapbox/$styleId/tiles/256/{z}/{x}/{y}@2x?access_token=$accessToken',
  tileProvider: kIsWeb
      ? CachedWebTileProvider(
          accessToken: accessToken,
          styleId: styleId,
        )
      : (_cacheStore != null
          ? CachedTileProvider(store: _cacheStore!)
          : NetworkTileProvider()),
  // ...
),
```

## Étapes d'implémentation

### Phase 1 : Service de cache IndexedDB

**Tâche 1.1** : Créer `web_tile_cache_service.dart`
- [ ] Créer la classe singleton
- [ ] Implémenter `initialize()` avec création de la DB IndexedDB
- [ ] Implémenter `getTile(url)` avec vérification d'expiration
- [ ] Implémenter `storeTile(url, data)` avec timestamp
- [ ] Implémenter `clearCache()` pour vider toute la DB
- [ ] Implémenter `clearExpiredTiles()` pour le nettoyage automatique
- [ ] Ajouter des logs de debug (`debugPrint`)

**Tâche 1.2** : Gestion des erreurs
- [ ] Try/catch sur toutes les opérations IndexedDB
- [ ] Fallback gracieux si IndexedDB indisponible
- [ ] Logs d'erreur explicites

**Tâche 1.3** : Tests unitaires
- [ ] Tester `initialize()` crée bien la DB
- [ ] Tester `storeTile()` + `getTile()` roundtrip
- [ ] Tester expiration (mock timestamp)
- [ ] Tester `clearCache()`

### Phase 2 : TileProvider custom

**Tâche 2.1** : Créer `cached_web_tile_provider.dart`
- [ ] Créer la classe qui extend `TileProvider`
- [ ] Implémenter `getImage()` avec logique de cache
- [ ] Gérer le téléchargement des tuiles manquantes
- [ ] Stocker les tuiles téléchargées dans le cache
- [ ] Ajouter des logs de performance (cache hit/miss)

**Tâche 2.2** : Gestion des erreurs réseau
- [ ] Try/catch sur téléchargement
- [ ] Retry logic (3 tentatives max)
- [ ] Placeholder si échec total
- [ ] Logs d'erreur explicites

**Tâche 2.3** : Tests
- [ ] Tester avec tuile en cache → Pas de requête réseau
- [ ] Tester avec tuile absente → Téléchargement + stockage
- [ ] Tester avec erreur réseau → Fallback gracieux

### Phase 3 : Intégration dans MapboxMap

**Tâche 3.1** : Modifier `mapbox_map.dart`
- [ ] Importer `CachedWebTileProvider` et `WebTileCacheService`
- [ ] Modifier `_initializeCache()` pour initialiser le service web
- [ ] Modifier `TileLayer` pour utiliser le bon provider selon la plateforme
- [ ] Tester sur web que le nouveau provider est bien utilisé

**Tâche 3.2** : Validation visuelle
- [ ] Lancer l'app en mode web DEV
- [ ] Ouvrir DevTools → Console pour voir les logs
- [ ] Naviguer sur la carte → Vérifier logs "Cache MISS" au premier passage
- [ ] Revenir sur la même zone → Vérifier logs "Cache HIT"
- [ ] Vérifier dans DevTools → Application → IndexedDB que les tuiles sont stockées

**Tâche 3.3** : Tests de performance
- [ ] Mesurer le temps de chargement initial (sans cache)
- [ ] Mesurer le temps de chargement avec cache
- [ ] Vérifier qu'il n'y a plus de requêtes réseau pour les tuiles en cache
- [ ] Tester sur une session longue (plusieurs déplacements sur la carte)

### Phase 4 : Nettoyage et documentation

**Tâche 4.1** : Nettoyage automatique
- [ ] Appeler `clearExpiredTiles()` au démarrage de l'app (dans `initialize()`)
- [ ] Ajouter un bouton admin pour vider manuellement le cache (optionnel)

**Tâche 4.2** : Logs et monitoring
- [ ] Ajouter des statistiques de cache (hit rate, taille totale)
- [ ] Logger les performances (temps de chargement moyen)

**Tâche 4.3** : Documentation
- [ ] Commenter le code (dartdoc)
- [ ] Mettre à jour `FLOW-*.md` si nécessaire
- [ ] Ajouter section dans README technique

## Tests de validation

### Tests fonctionnels

1. **Cache vide → Premier chargement**
   - [ ] Ouvrir l'app web en mode incognito
   - [ ] Naviguer sur la carte
   - [ ] Vérifier que les tuiles se chargent
   - [ ] Vérifier les logs "Cache MISS" + "Storing tile"

2. **Cache rempli → Rechargement**
   - [ ] Recharger la page (F5)
   - [ ] Revenir sur la même zone de carte
   - [ ] Vérifier que les tuiles se chargent instantanément
   - [ ] Vérifier les logs "Cache HIT"
   - [ ] Vérifier dans Network tab : pas de requêtes Mapbox pour les tuiles en cache

3. **Navigation étendue**
   - [ ] Se déplacer sur plusieurs zones de la carte
   - [ ] Revenir sur les zones précédentes
   - [ ] Vérifier mix de "Cache HIT" et "Cache MISS"

4. **Gestion de l'expiration**
   - [ ] Modifier temporairement l'expiration à 10 secondes (pour test)
   - [ ] Charger des tuiles
   - [ ] Attendre 15 secondes
   - [ ] Recharger → Vérifier que les tuiles sont re-téléchargées

5. **Gestion d'erreur réseau**
   - [ ] Désactiver le réseau en plein chargement
   - [ ] Vérifier que les tuiles en cache s'affichent quand même
   - [ ] Vérifier que les tuiles manquantes ne bloquent pas l'app

### Tests de performance

6. **Mesure de bande passante**
   - [ ] Vider le cache
   - [ ] Naviguer sur une zone avec ~100 tuiles visibles
   - [ ] Noter la bande passante consommée (DevTools Network)
   - [ ] Recharger la page et revenir sur la même zone
   - [ ] Vérifier que la bande passante est ~0 KB

7. **Mesure de vitesse**
   - [ ] Mesurer le temps de chargement initial : ____ ms
   - [ ] Mesurer le temps de chargement avec cache : ____ ms
   - [ ] Objectif : >50% de réduction

### Tests multi-plateformes

8. **Mobile/Desktop non régressé**
   - [ ] Lancer sur Android → Vérifier que HiveCacheStore fonctionne toujours
   - [ ] Lancer sur iOS → Vérifier que HiveCacheStore fonctionne toujours
   - [ ] Vérifier les logs "Cache initialized with HiveCacheStore"

9. **Web multi-navigateurs**
   - [ ] Tester sur Chrome
   - [ ] Tester sur Firefox
   - [ ] Tester sur Safari (si IndexedDB supporté)
   - [ ] Tester sur Edge

## Checklist de validation finale

- [ ] Aucune nouvelle dépendance ajoutée au `pubspec.yaml`
- [ ] Code compatible web uniquement (guards `kIsWeb`)
- [ ] Mobile/Desktop non impactés (toujours HiveCacheStore)
- [ ] Logs de debug présents et clairs
- [ ] Gestion d'erreur complète (try/catch)
- [ ] Expiration automatique (30 jours)
- [ ] Performance mesurée et améliorée
- [ ] Tests manuels passés
- [ ] Code documenté (dartdoc)
- [ ] Pas de régression sur les autres plateformes

## Notes techniques

### Pourquoi IndexedDB et pas localStorage ?

- **localStorage** : Limite de 5-10 MB → Insuffisant pour des tuiles (1 tuile = ~60 KB)
- **IndexedDB** : Limite de ~50% de l'espace disque disponible → Largement suffisant

### Structure de la clé de cache

Format recommandé : `{styleId}_{z}_{x}_{y}@{scale}x`

Exemple : `streets-v11_15_16234_11378@2x`

Cela permet :
- Identification unique de chaque tuile
- Support multi-styles (streets, satellite, etc.)
- Support multi-résolutions (@1x, @2x)

### Taille estimée du cache

- 1 tuile Mapbox @2x : ~60 KB
- Carte complète niveau zoom 15 : ~500 tuiles
- Total pour une zone : ~30 MB
- IndexedDB peut stocker plusieurs GB → Pas de souci

### Maintenance du cache

Le nettoyage automatique (`clearExpiredTiles()`) s'exécute :
- Au démarrage de l'app
- Évite l'accumulation de vieilles tuiles
- Garde le cache sous contrôle

## Dépendances

### Packages Flutter (déjà présents)
- `flutter_map: ^7.0.2` → Fournit `TileProvider`
- `flutter_map_cache: ^1.5.1` → Pour mobile/desktop uniquement

### Bibliothèques Dart natives (aucun ajout)
- `dart:indexed_db` → Pour IndexedDB (web seulement)
- `dart:typed_data` → Pour manipuler les bytes
- `package:flutter/foundation.dart` → Pour `kIsWeb`

### Pas de nouvelle dépendance externe ✅

## Estimation

- **Temps de développement** : 2-3 heures
- **Complexité** : Moyenne
- **Risque** : Faible (fallback sur comportement actuel)
- **Gain** : Important pour les admins PROD

## Prochaine étape

**Attente de validation utilisateur** pour démarrer l'implémentation.
