import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:travel_mate/blocs/trip/trip_bloc.dart'; import 'package:travel_mate/blocs/trip/trip_event.dart'; import 'package:travel_mate/blocs/activity/activity_bloc.dart'; import 'package:travel_mate/blocs/activity/activity_event.dart'; import 'package:travel_mate/components/home/create_trip_content.dart'; import 'package:travel_mate/models/trip.dart'; import 'package:travel_mate/components/map/map_content.dart'; import 'package:travel_mate/services/error_service.dart'; import 'package:travel_mate/services/activity_cache_service.dart'; import 'package:travel_mate/components/activities/activities_page.dart'; import 'package:url_launcher/url_launcher.dart'; class ShowTripDetailsContent extends StatefulWidget { final Trip trip; const ShowTripDetailsContent({super.key, required this.trip}); @override State createState() => _ShowTripDetailsContentState(); } class _ShowTripDetailsContentState extends State { final ErrorService _errorService = ErrorService(); final ActivityCacheService _cacheService = ActivityCacheService(); @override void initState() { super.initState(); // Lancer la recherche d'activités Google en arrière-plan _preloadGoogleActivities(); } /// Précharger les activités Google en arrière-plan void _preloadGoogleActivities() { // Attendre un moment avant de lancer la recherche pour ne pas bloquer l'UI Future.delayed(const Duration(milliseconds: 500), () { if (mounted && widget.trip.id != null) { // Vérifier si on a déjà des activités en cache if (_cacheService.hasCachedActivities(widget.trip.id!)) { return; // Utiliser le cache } // Sinon, lancer la recherche context.read().add( widget.trip.hasCoordinates ? SearchActivitiesWithCoordinates( tripId: widget.trip.id!, latitude: widget.trip.latitude!, longitude: widget.trip.longitude!, category: null, maxResults: 6, reset: true, ) : SearchActivities( tripId: widget.trip.id!, destination: widget.trip.location, category: null, maxResults: 6, reset: true, ), ); } }); } // Calculer les jours restants avant le voyage int get daysUntilTrip { final now = DateTime.now(); final tripStart = widget.trip.startDate; final difference = tripStart.difference(now).inDays; return difference > 0 ? difference : 0; } // Méthode pour ouvrir la carte interne void _openInternalMap() { Navigator.push( context, MaterialPageRoute( builder: (context) => MapContent(initialSearchQuery: widget.trip.location), ), ); } // Méthode pour afficher le dialogue de sélection de carte void _showMapOptions() { final theme = Theme.of(context); showDialog( context: context, builder: (BuildContext context) { return AlertDialog( backgroundColor: theme.dialogBackgroundColor, title: Text( 'Ouvrir la carte', style: theme.textTheme.titleLarge?.copyWith( color: theme.colorScheme.onSurface, ), ), content: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Choisissez comment vous souhaitez ouvrir la carte :', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface, ), ), const SizedBox(height: 20), // Options centrées verticalement Column( children: [ // Carte de l'application SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: () { Navigator.pop(context); _openInternalMap(); }, icon: const Icon(Icons.map), label: const Text('Carte de l\'app'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), ), ), ), const SizedBox(height: 12), // Google Maps SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: () { Navigator.pop(context); _openGoogleMaps(); }, icon: const Icon(Icons.directions), label: const Text('Google Maps'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), ), ), ), const SizedBox(height: 12), // Waze SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: () { Navigator.pop(context); _openWaze(); }, icon: const Icon(Icons.navigation), label: const Text('Waze'), style: ElevatedButton.styleFrom( backgroundColor: Colors.orange, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), ), ), ), ], ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'Annuler', style: TextStyle(color: theme.colorScheme.primary), ), ), ], ); }, ); } // Méthode pour ouvrir Google Maps Future _openGoogleMaps() async { final location = Uri.encodeComponent(widget.trip.location); try { // Essayer d'abord l'URL scheme pour l'app mobile final appUrl = 'comgooglemaps://?q=$location'; final appUri = Uri.parse(appUrl); if (await canLaunchUrl(appUri)) { await launchUrl(appUri); return; } // Fallback vers l'URL web final webUrl = 'https://www.google.com/maps/search/?api=1&query=$location'; final webUri = Uri.parse(webUrl); if (await canLaunchUrl(webUri)) { await launchUrl(webUri, mode: LaunchMode.externalApplication); return; } _errorService.showError( message: 'Impossible d\'ouvrir Google Maps. Vérifiez que l\'application est installée.', ); } catch (e) { _errorService.showError( message: 'Erreur lors de l\'ouverture de Google Maps', ); } } // Méthode pour ouvrir Waze Future _openWaze() async { final location = Uri.encodeComponent(widget.trip.location); try { // Essayer d'abord l'URL scheme pour l'app mobile final appUrl = 'waze://?q=$location'; final appUri = Uri.parse(appUrl); if (await canLaunchUrl(appUri)) { await launchUrl(appUri); return; } // Fallback vers l'URL web final webUrl = 'https://waze.com/ul?q=$location'; final webUri = Uri.parse(webUrl); if (await canLaunchUrl(webUri)) { await launchUrl(webUri, mode: LaunchMode.externalApplication); return; } _errorService.showError( message: 'Impossible d\'ouvrir Waze. Vérifiez que l\'application est installée.', ); } catch (e) { _errorService.showError( message: 'Erreur lors de l\'ouverture de Waze', ); } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDarkMode = theme.brightness == Brightness.dark; return Scaffold( backgroundColor: isDarkMode ? theme.scaffoldBackgroundColor : Colors.grey[50], appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back, color: theme.colorScheme.onSurface), onPressed: () => Navigator.pop(context), ), title: Text( widget.trip.title, style: theme.textTheme.titleLarge?.copyWith( color: theme.colorScheme.onSurface, fontWeight: FontWeight.w600, ), ), actions: [ IconButton( icon: Icon(Icons.more_vert, color: theme.colorScheme.onSurface), onPressed: () => _showOptionsMenu(), ), ], ), body: SingleChildScrollView( child: Column( children: [ // Image du voyage Container( height: 250, width: double.infinity, margin: const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha:0.1), blurRadius: 10, offset: const Offset(0, 5), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: widget.trip.imageUrl != null && widget.trip.imageUrl!.isNotEmpty ? Image.network( widget.trip.imageUrl!, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) => _buildPlaceholderImage(), ) : _buildPlaceholderImage(), ), ), // Contenu principal Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Section "Départ dans X jours" Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( color: theme.cardColor, borderRadius: BorderRadius.circular(12), border: Border.all( color: isDarkMode ? Colors.white.withValues(alpha:0.1) : Colors.black.withValues(alpha:0.1), width: 1, ), boxShadow: [ BoxShadow( color: isDarkMode ? Colors.black.withValues(alpha:0.3) : Colors.black.withValues(alpha:0.1), blurRadius: isDarkMode ? 8 : 5, offset: const Offset(0, 2), ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.teal.withValues(alpha:0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.flight_takeoff, color: Colors.teal, size: 20, ), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Départ dans', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withValues(alpha:0.6), ), ), Text( daysUntilTrip > 0 ? '$daysUntilTrip Jours' : 'Voyage terminé', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onSurface, ), ), Text( widget.trip.formattedDates, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withValues(alpha:0.6), ), ), ], ), ], ), ), const SizedBox(height: 24), // Section Participants Text( 'Participants', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onSurface, ), ), const SizedBox(height: 12), Row( children: [ // Avatars des participants (limité à 4 + bouton add) ...List.generate( widget.trip.totalParticipants > 4 ? 4 : widget.trip.totalParticipants, (index) => Container( margin: const EdgeInsets.only(right: 8), child: CircleAvatar( radius: 25, backgroundColor: Colors.blue[100], child: Icon( Icons.person, color: Colors.blue[600], size: 24, ), ), ), ), // Bouton d'ajout si moins de 4 participants affichés if (widget.trip.totalParticipants <= 4) Container( width: 50, height: 50, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.grey[300]!, width: 2, style: BorderStyle.solid, ), ), child: Icon( Icons.add, color: Colors.grey[400], size: 24, ), ), ], ), const SizedBox(height: 32), // Grille d'actions GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 2, childAspectRatio: 1.5, crossAxisSpacing: 16, mainAxisSpacing: 16, children: [ _buildActionButton( icon: Icons.calendar_today, title: 'Calendrier', color: Colors.blue, onTap: () => _showComingSoon('Calendrier'), ), _buildActionButton( icon: Icons.local_activity, title: 'Activités', color: Colors.green, onTap: () => _navigateToActivities(), ), _buildActionButton( icon: Icons.account_balance_wallet, title: 'Dépenses', color: Colors.orange, onTap: () => _showComingSoon('Dépenses'), ), _buildActionButton( icon: Icons.map, title: 'Ouvrir la carte', color: Colors.purple, onTap: _showMapOptions, ), ], ), ], ), ), ], ), ), ); } Widget _buildPlaceholderImage() { return Container( color: Colors.grey[200], child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.location_city, size: 48, color: Colors.grey, ), SizedBox(height: 8), Text( 'Aucune image', style: TextStyle( color: Colors.grey, fontSize: 14, ), ), ], ), ), ); } Widget _buildActionButton({ required IconData icon, required String title, required Color color, required VoidCallback onTap, }) { final theme = Theme.of(context); final isDarkMode = theme.brightness == Brightness.dark; return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: theme.cardColor, borderRadius: BorderRadius.circular(12), border: Border.all( color: isDarkMode ? Colors.white.withValues(alpha:0.1) : Colors.black.withValues(alpha:0.1), width: 1, ), boxShadow: [ BoxShadow( color: isDarkMode ? Colors.black.withValues(alpha:0.3) : Colors.black.withValues(alpha:0.1), blurRadius: isDarkMode ? 8 : 5, offset: const Offset(0, 2), ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( icon, color: color, size: 24, ), ), const SizedBox(height: 8), Text( title, style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.w600, color: theme.colorScheme.onSurface, ), textAlign: TextAlign.center, ), ], ), ), ); } void _showComingSoon(String feature) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('$feature - Fonctionnalité à venir'), backgroundColor: Colors.blue, ), ); } void _showOptionsMenu() { final theme = Theme.of(context); showModalBottomSheet( context: context, backgroundColor: theme.bottomSheetTheme.backgroundColor, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (context) => Container( padding: const EdgeInsets.all(20), child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: Icon(Icons.edit, color: theme.colorScheme.primary), title: Text( 'Modifier le voyage', style: theme.textTheme.bodyLarge?.copyWith( color: theme.colorScheme.onSurface, ), ), onTap: () async { Navigator.pop(context); final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => CreateTripContent(tripToEdit: widget.trip), ), ); if (result == true && mounted) { Navigator.pop(context, true); } }, ), ListTile( leading: const Icon(Icons.delete, color: Colors.red), title: Text( 'Supprimer le voyage', style: theme.textTheme.bodyLarge?.copyWith( color: theme.colorScheme.onSurface, ), ), onTap: () { Navigator.pop(context); _showDeleteConfirmation(); }, ), ], ), ), ); } void _showDeleteConfirmation() { final theme = Theme.of(context); showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: theme.dialogBackgroundColor, title: Text( 'Confirmer la suppression', style: theme.textTheme.titleLarge?.copyWith( color: theme.colorScheme.onSurface, ), ), content: Text( 'Êtes-vous sûr de vouloir supprimer ce voyage ? Cette action est irréversible.', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurface, ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'Annuler', style: TextStyle(color: theme.colorScheme.primary), ), ), TextButton( onPressed: () { context.read().add( TripDeleteRequested(tripId: widget.trip.id!), ); Navigator.pop(context); Navigator.pop(context, true); }, child: const Text( 'Supprimer', style: TextStyle(color: Colors.red), ), ), ], ), ); } void _navigateToActivities() { Navigator.push( context, MaterialPageRoute( builder: (context) => ActivitiesPage(trip: widget.trip), ), ); } }