import 'dart:io';
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 String get _apiKey {
    if (Platform.isAndroid) {
      return dotenv.env['GOOGLE_MAPS_API_KEY_ANDROID'] ??
          dotenv.env['GOOGLE_MAPS_API_KEY'] ??
          '';
    } else if (Platform.isIOS) {
      return dotenv.env['GOOGLE_MAPS_API_KEY_IOS'] ??
          dotenv.env['GOOGLE_MAPS_API_KEY'] ??
          '';
    }
    return dotenv.env['GOOGLE_MAPS_API_KEY'] ?? '';
  }

  /// Géocode la destination d'un voyage et retourne un Trip mis à jour
  Future<Trip> 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<Map<String, double>?> _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<List<Trip>> geocodeTrips(List<Trip> trips) async {
    LoggerService.info(
      '🔄 [TripGeocoding] Géocodage de ${trips.length} voyages',
    );

    final List<Trip> 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;
  }
}
