import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import '../../services/trip_image_service.dart'; /// Page d'administration pour gérer les images des voyages class ImageManagementPage extends StatefulWidget { const ImageManagementPage({super.key}); @override State createState() => _ImageManagementPageState(); } class _ImageManagementPageState extends State { final TripImageService _tripImageService = TripImageService(); Map? _statistics; bool _isLoading = false; bool _isCleaningUp = false; bool _isCleaningDuplicates = false; String? _cleanupResult; String? _duplicateCleanupResult; @override void initState() { super.initState(); _loadStatistics(); } Future _loadStatistics() async { setState(() { _isLoading = true; }); try { final userId = FirebaseAuth.instance.currentUser?.uid; if (userId != null) { final stats = await _tripImageService.getImageStatistics(userId); setState(() { _statistics = stats; }); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Erreur lors du chargement des statistiques: $e')), ); } } finally { setState(() { _isLoading = false; }); } } Future _cleanupUnusedImages() async { // Demander confirmation final shouldCleanup = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Nettoyer les images inutilisées'), content: const Text( 'Cette action supprimera définitivement toutes les images qui ne sont plus utilisées par vos voyages. ' 'Voulez-vous continuer ?' ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('Annuler'), ), ElevatedButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('Nettoyer'), ), ], ), ); if (shouldCleanup != true) return; setState(() { _isCleaningUp = true; _cleanupResult = null; }); try { final userId = FirebaseAuth.instance.currentUser?.uid; if (userId != null) { await _tripImageService.cleanupUnusedImages(userId); setState(() { _cleanupResult = 'Nettoyage terminé avec succès !'; }); // Recharger les statistiques await _loadStatistics(); } } catch (e) { setState(() { _cleanupResult = 'Erreur lors du nettoyage: $e'; }); } finally { setState(() { _isCleaningUp = false; }); } } Future _cleanupDuplicateImages() async { // Demander confirmation final shouldCleanup = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Nettoyer les doublons d\'images'), content: const Text( 'Cette action analysera vos images et supprimera automatiquement les doublons pour la même destination. ' 'Seule l\'image la plus récente sera conservée. Voulez-vous continuer ?' ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: const Text('Annuler'), ), ElevatedButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('Nettoyer les doublons'), ), ], ), ); if (shouldCleanup != true) return; setState(() { _isCleaningDuplicates = true; _duplicateCleanupResult = null; }); try { await _tripImageService.cleanupDuplicateImages(); setState(() { _duplicateCleanupResult = 'Doublons supprimés avec succès !'; }); // Recharger les statistiques await _loadStatistics(); } catch (e) { setState(() { _duplicateCleanupResult = 'Erreur lors du nettoyage des doublons: $e'; }); } finally { setState(() { _isCleaningDuplicates = false; }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Gestion des Images'), backgroundColor: Colors.blue, foregroundColor: Colors.white, ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Statistiques Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Statistiques', style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 16), if (_isLoading) const Center(child: CircularProgressIndicator()) else if (_statistics != null) ...[ _buildStatItem( 'Voyages totaux', _statistics!['totalTrips']?.toString() ?? '0' ), _buildStatItem( 'Voyages avec image', _statistics!['tripsWithImages']?.toString() ?? '0' ), _buildStatItem( 'Voyages sans image', _statistics!['tripsWithoutImages']?.toString() ?? '0' ), const SizedBox(height: 8), Text( 'Dernière mise à jour: ${_formatTimestamp(_statistics!['timestamp'])}', style: Theme.of(context).textTheme.bodySmall, ), ] else const Text('Impossible de charger les statistiques'), ], ), ), ), const SizedBox(height: 24), // Actions Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Actions', style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 16), // Bouton recharger statistiques SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _isLoading ? null : _loadStatistics, icon: const Icon(Icons.refresh), label: const Text('Recharger les statistiques'), ), ), const SizedBox(height: 12), // Bouton nettoyer les images inutilisées SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _isCleaningUp ? null : _cleanupUnusedImages, icon: _isCleaningUp ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.cleaning_services), label: Text(_isCleaningUp ? 'Nettoyage en cours...' : 'Nettoyer les images inutilisées'), style: ElevatedButton.styleFrom( backgroundColor: Colors.orange, foregroundColor: Colors.white, ), ), ), const SizedBox(height: 12), // Bouton nettoyer les doublons SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _isCleaningDuplicates ? null : _cleanupDuplicateImages, icon: _isCleaningDuplicates ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.content_copy), label: Text(_isCleaningDuplicates ? 'Suppression doublons...' : 'Supprimer les doublons d\'images'), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), ), ), if (_cleanupResult != null) ...[ const SizedBox(height: 12), Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: _cleanupResult!.contains('Erreur') ? Colors.red.withValues(alpha: 0.1) : Colors.green.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all( color: _cleanupResult!.contains('Erreur') ? Colors.red : Colors.green, width: 1, ), ), child: Text( _cleanupResult!, style: TextStyle( color: _cleanupResult!.contains('Erreur') ? Colors.red : Colors.green, fontWeight: FontWeight.bold, ), ), ), ], if (_duplicateCleanupResult != null) ...[ const SizedBox(height: 12), Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: _duplicateCleanupResult!.contains('Erreur') ? Colors.red.withValues(alpha: 0.1) : Colors.blue.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), border: Border.all( color: _duplicateCleanupResult!.contains('Erreur') ? Colors.red : Colors.blue, width: 1, ), ), child: Text( _duplicateCleanupResult!, style: TextStyle( color: _duplicateCleanupResult!.contains('Erreur') ? Colors.red : Colors.blue, fontWeight: FontWeight.bold, ), ), ), ], ], ), ), ), const SizedBox(height: 24), // Informations Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Explications', style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 12), const Text( '• Chaque voyage peut avoir une image automatiquement téléchargée depuis Google Places\n' '• Les images sont stockées dans Firebase Storage\n' '• Il peut y avoir des images inutilisées si des voyages ont été supprimés ou modifiés\n' '• Le nettoyage supprime uniquement les images qui ne sont plus référencées par vos voyages', style: TextStyle(height: 1.5), ), ], ), ), ), ], ), ), ); } Widget _buildStatItem(String label, String value) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(label), Text( value, style: const TextStyle(fontWeight: FontWeight.bold), ), ], ), ); } String _formatTimestamp(String? timestamp) { if (timestamp == null) return 'Inconnue'; try { final dateTime = DateTime.parse(timestamp); return '${dateTime.day}/${dateTime.month}/${dateTime.year} ' '${dateTime.hour.toString().padLeft(2, '0')}:' '${dateTime.minute.toString().padLeft(2, '0')}'; } catch (e) { return 'Format invalide'; } } }