import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:flutter_dotenv/flutter_dotenv.dart'; import '../models/trip.dart'; import 'error_service.dart'; import 'logger_service.dart'; /// Service pour géocoder les destinations des voyages class TripGeocodingService { static final TripGeocodingService _instance = TripGeocodingService._internal(); factory TripGeocodingService() => _instance; TripGeocodingService._internal(); final ErrorService _errorService = ErrorService(); static final String _apiKey = dotenv.env['GOOGLE_MAPS_API_KEY'] ?? ''; /// Géocode la destination d'un voyage et retourne un Trip mis à jour Future geocodeTrip(Trip trip) async { try { LoggerService.info('🌍 [TripGeocoding] Géocodage de "${trip.location}"'); // Vérifier si on a déjà des coordonnées récentes if (trip.hasRecentCoordinates) { LoggerService.info( '✅ [TripGeocoding] Coordonnées récentes trouvées, pas de géocodage nécessaire', ); return trip; } if (_apiKey.isEmpty) { LoggerService.error('❌ [TripGeocoding] Clé API Google Maps manquante'); throw Exception('Clé API Google Maps non configurée'); } final coordinates = await _geocodeDestination(trip.location); if (coordinates != null) { LoggerService.info( '✅ [TripGeocoding] Coordonnées trouvées: ${coordinates['lat']}, ${coordinates['lng']}', ); return trip.copyWith( latitude: coordinates['lat'], longitude: coordinates['lng'], lastGeocodingUpdate: DateTime.now(), ); } else { LoggerService.warning( '⚠️ [TripGeocoding] Impossible de géocoder "${trip.location}"', ); return trip; } } catch (e) { LoggerService.error('❌ [TripGeocoding] Erreur lors du géocodage: $e'); _errorService.logError('trip_geocoding_service', e); return trip; // Retourner le voyage original en cas d'erreur } } /// Géocode une destination et retourne les coordonnées Future?> _geocodeDestination(String destination) async { try { final url = 'https://maps.googleapis.com/maps/api/geocode/json' '?address=${Uri.encodeComponent(destination)}' '&key=$_apiKey'; LoggerService.info('🌐 [TripGeocoding] URL = $url'); final response = await http.get(Uri.parse(url)); LoggerService.info( '📡 [TripGeocoding] Status code = ${response.statusCode}', ); if (response.statusCode == 200) { final data = json.decode(response.body); LoggerService.info( '📋 [TripGeocoding] Réponse géocodage = ${data['status']}', ); if (data['status'] == 'OK' && data['results'].isNotEmpty) { final location = data['results'][0]['geometry']['location']; final coordinates = { 'lat': (location['lat'] as num).toDouble(), 'lng': (location['lng'] as num).toDouble(), }; LoggerService.info( '📍 [TripGeocoding] Coordonnées trouvées = $coordinates', ); return coordinates; } else { LoggerService.warning( '⚠️ [TripGeocoding] Erreur API = ${data['error_message'] ?? data['status']}', ); return null; } } else { LoggerService.error( '❌ [TripGeocoding] Erreur HTTP ${response.statusCode}', ); return null; } } catch (e) { LoggerService.error('❌ [TripGeocoding] Exception lors du géocodage: $e'); _errorService.logError('trip_geocoding_service', e); return null; } } /// Vérifie si un voyage a besoin d'être géocodé bool needsGeocoding(Trip trip) { return !trip.hasRecentCoordinates; } /// Géocode plusieurs voyages en batch Future> geocodeTrips(List trips) async { LoggerService.info( '🔄 [TripGeocoding] Géocodage de ${trips.length} voyages', ); final List geocodedTrips = []; for (final trip in trips) { if (needsGeocoding(trip)) { final geocodedTrip = await geocodeTrip(trip); geocodedTrips.add(geocodedTrip); // Petit délai pour éviter de saturer l'API Google await Future.delayed(const Duration(milliseconds: 200)); } else { geocodedTrips.add(trip); } } LoggerService.info('✅ [TripGeocoding] Géocodage terminé'); return geocodedTrips; } }