import 'dart:io';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:travel_mate/services/error_service.dart';

class PlaceImageService {
  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'] ?? '';
  }

  final FirebaseStorage _storage = FirebaseStorage.instance;
  final ErrorService _errorService = ErrorService();

  /// Récupère l'URL de l'image d'un lieu depuis Google Places API
  Future<String?> getPlaceImageUrl(String location) async {
    try {
      // ÉTAPE 1: Vérifier d'abord si une image existe déjà dans le Storage
      final existingUrl = await _checkExistingImage(location);
      if (existingUrl != null) {
        return existingUrl;
      }

      if (_apiKey.isEmpty) {
        _errorService.logError(
          'PlaceImageService',
          'Google Maps API key manquante',
        );
        return null;
      }

      // ÉTAPE 2: Recherche via Google Places API seulement si aucune image n'existe
      final searchTerms = _generateSearchTerms(location);

      for (final searchTerm in searchTerms) {
        // 1. Rechercher le lieu
        final placeId = await _getPlaceIdForTerm(searchTerm);
        if (placeId == null) continue;

        // 2. Récupérer les détails du lieu avec les photos
        final photoReference = await _getPhotoReference(placeId);
        if (photoReference == null) continue;

        // 3. Télécharger et sauvegarder l'image (seulement si pas d'image existante)
        final imageUrl = await _downloadAndSaveImage(photoReference, location);
        if (imageUrl != null) {
          return imageUrl;
        }
      }

      return null;
    } catch (e) {
      _errorService.logError(
        'PlaceImageService',
        'Erreur lors de la récupération de l\'image: $e',
      );
      return null;
    }
  }

  /// Génère différents termes de recherche pour optimiser les résultats
  List<String> _generateSearchTerms(String location) {
    final terms = <String>[];

    // Ajouter des termes spécifiques pour les villes connues
    final citySpecificTerms = _getCitySpecificTerms(location.toLowerCase());
    terms.addAll(citySpecificTerms);

    // Termes génériques avec attractions
    terms.addAll([
      '$location attractions touristiques monuments',
      '$location landmarks',
      '$location tourist attractions',
      '$location monuments historiques',
      '$location points d\'intérêt',
      '$location centre ville',
      '$location skyline',
      location, // Terme original en dernier
    ]);

    return terms;
  }

  /// Retourne des termes spécifiques pour des villes connues
  List<String> _getCitySpecificTerms(String location) {
    final specific = <String>[];

    if (location.contains('paris')) {
      specific.addAll([
        'Tour Eiffel Paris',
        'Arc de Triomphe Paris',
        'Notre-Dame Paris',
        'Louvre Paris',
        'Champs-Élysées Paris',
      ]);
    } else if (location.contains('london') || location.contains('londres')) {
      specific.addAll([
        'Big Ben London',
        'Tower Bridge London',
        'London Eye',
        'Buckingham Palace London',
        'Tower of London',
      ]);
    } else if (location.contains('rome') || location.contains('roma')) {
      specific.addAll([
        'Colosseum Rome',
        'Trevi Fountain Rome',
        'Vatican Rome',
        'Pantheon Rome',
      ]);
    } else if (location.contains('new york') || location.contains('nyc')) {
      specific.addAll([
        'Statue of Liberty New York',
        'Empire State Building New York',
        'Times Square New York',
        'Brooklyn Bridge New York',
      ]);
    } else if (location.contains('tokyo') || location.contains('japon')) {
      specific.addAll([
        'Tokyo Tower',
        'Senso-ji Temple Tokyo',
        'Shibuya Crossing Tokyo',
        'Tokyo Skytree',
      ]);
    }

    return specific;
  }

  /// Recherche un place ID pour un terme spécifique
  Future<String?> _getPlaceIdForTerm(String searchTerm) async {
    // Essayer d'abord avec les attractions touristiques
    String? placeId = await _searchPlaceWithType(
      searchTerm,
      'tourist_attraction',
    );
    if (placeId != null) return placeId;

    // Puis avec les points d'intérêt
    placeId = await _searchPlaceWithType(searchTerm, 'point_of_interest');
    if (placeId != null) return placeId;

    // Enfin recherche générale
    return await _searchPlaceGeneral(searchTerm);
  }

  /// Recherche un lieu avec un type spécifique
  Future<String?> _searchPlaceWithType(String location, String type) async {
    try {
      final url = Uri.parse(
        'https://maps.googleapis.com/maps/api/place/textsearch/json'
        '?query=${Uri.encodeComponent('$location attractions monuments')}'
        '&type=$type'
        '&fields=place_id,name,types,rating'
        '&key=$_apiKey',
      );

      final response = await http.get(url);

      if (response.statusCode == 200) {
        final data = json.decode(response.body);

        if (data['status'] == 'OK' && data['results'].isNotEmpty) {
          // Prioriser les résultats avec des ratings élevés
          final results = data['results'] as List;
          results.sort((a, b) {
            final aRating = a['rating'] ?? 0.0;
            final bRating = b['rating'] ?? 0.0;
            return bRating.compareTo(aRating);
          });

          return results.first['place_id'];
        }
      }
      return null;
    } catch (e) {
      return null;
    }
  }

  /// Recherche générale de lieu
  Future<String?> _searchPlaceGeneral(String location) async {
    try {
      final url = Uri.parse(
        'https://maps.googleapis.com/maps/api/place/findplacefromtext/json'
        '?input=${Uri.encodeComponent(location)}'
        '&inputtype=textquery'
        '&fields=place_id'
        '&key=$_apiKey',
      );

      final response = await http.get(url);

      if (response.statusCode == 200) {
        final data = json.decode(response.body);

        if (data['status'] == 'OK' && data['candidates'].isNotEmpty) {
          return data['candidates'][0]['place_id'];
        }
      }
      return null;
    } catch (e) {
      return null;
    }
  }

  /// Récupère la référence photo du lieu
  Future<String?> _getPhotoReference(String placeId) async {
    try {
      final url = Uri.parse(
        'https://maps.googleapis.com/maps/api/place/details/json'
        '?place_id=$placeId'
        '&fields=photos'
        '&key=$_apiKey',
      );

      final response = await http.get(url);

      if (response.statusCode == 200) {
        final data = json.decode(response.body);

        if (data['status'] == 'OK' &&
            data['result'] != null &&
            data['result']['photos'] != null &&
            data['result']['photos'].isNotEmpty) {
          final photos = data['result']['photos'] as List;

          // Trier les photos pour obtenir les meilleures
          final sortedPhotos = _sortPhotosByQuality(photos);

          if (sortedPhotos.isNotEmpty) {
            return sortedPhotos.first['photo_reference'];
          }
        }
      }
      return null;
    } catch (e) {
      _errorService.logError(
        'PlaceImageService',
        'Erreur lors de la récupération de la référence photo: $e',
      );
      return null;
    }
  }

  /// Trie les photos par qualité (largeur/hauteur et popularité)
  List<Map<String, dynamic>> _sortPhotosByQuality(List photos) {
    final photoList = photos.cast<Map<String, dynamic>>();

    photoList.sort((a, b) {
      // Priorité 1: Photos horizontales (largeur > hauteur)
      final aWidth = a['width'] ?? 0;
      final aHeight = a['height'] ?? 0;
      final bWidth = b['width'] ?? 0;
      final bHeight = b['height'] ?? 0;

      final aIsHorizontal = aWidth > aHeight;
      final bIsHorizontal = bWidth > bHeight;

      if (aIsHorizontal && !bIsHorizontal) return -1;
      if (!aIsHorizontal && bIsHorizontal) return 1;

      // Priorité 2: Résolution plus élevée
      final aResolution = aWidth * aHeight;
      final bResolution = bWidth * bHeight;

      if (aResolution != bResolution) {
        return bResolution.compareTo(aResolution);
      }

      // Priorité 3: Ratio d'aspect optimal pour paysage (1.5-2.0)
      final aRatio = aWidth > 0 ? aWidth / aHeight : 0;
      final bRatio = bWidth > 0 ? bWidth / bHeight : 0;

      final idealRatio = 1.7; // Ratio 16:9 environ
      final aDiff = (aRatio - idealRatio).abs();
      final bDiff = (bRatio - idealRatio).abs();

      return aDiff.compareTo(bDiff);
    });

    return photoList;
  }

  /// Télécharge l'image et la sauvegarde dans Firebase Storage
  Future<String?> _downloadAndSaveImage(
    String photoReference,
    String location,
  ) async {
    try {
      // URL pour télécharger l'image en haute qualité et format horizontal
      final imageUrl =
          'https://maps.googleapis.com/maps/api/place/photo'
          '?maxwidth=1200' // Augmenté pour une meilleure qualité
          '&maxheight=800' // Ratio horizontal ~1.5:1
          '&photo_reference=$photoReference'
          '&key=$_apiKey';

      // Télécharger l'image
      final response = await http.get(Uri.parse(imageUrl));

      if (response.statusCode == 200) {
        // Créer un nom de fichier unique basé sur la localisation normalisée
        final normalizedLocation = _normalizeLocationName(location);
        final fileName =
            '${normalizedLocation}_${DateTime.now().millisecondsSinceEpoch}.jpg';

        // Référence vers Firebase Storage
        final storageRef = _storage.ref().child('trip_images/$fileName');

        // Upload de l'image avec métadonnées
        final uploadTask = await storageRef.putData(
          response.bodyBytes,
          SettableMetadata(
            contentType: 'image/jpeg',
            customMetadata: {
              'location': location,
              'normalizedLocation': normalizedLocation,
              'source': 'google_places',
              'uploadedAt': DateTime.now().toIso8601String(),
            },
          ),
        );

        // Récupérer l'URL de téléchargement
        final downloadUrl = await uploadTask.ref.getDownloadURL();
        return downloadUrl;
      } else {}
      return null;
    } catch (e) {
      _errorService.logError(
        'PlaceImageService',
        'Erreur lors du téléchargement/sauvegarde: $e',
      );
      return null;
    }
  }

  /// Vérifie si une image existe déjà pour cette location
  Future<String?> _checkExistingImage(String location) async {
    try {
      final normalizedLocation = _normalizeLocationName(location);

      final listResult = await _storage.ref('trip_images').listAll();

      for (final item in listResult.items) {
        try {
          final metadata = await item.getMetadata();
          final storedNormalizedLocation =
              metadata.customMetadata?['normalizedLocation'];
          final storedLocation = metadata.customMetadata?['location'];

          // Méthode 1: Vérifier avec la location normalisée (nouvelles images)
          if (storedNormalizedLocation != null &&
              storedNormalizedLocation == normalizedLocation) {
            final url = await item.getDownloadURL();
            return url;
          }

          // Méthode 2: Vérifier avec la location originale normalisée (anciennes images)
          if (storedLocation != null) {
            final storedLocationNormalized = _normalizeLocationName(
              storedLocation,
            );
            if (storedLocationNormalized == normalizedLocation) {
              final url = await item.getDownloadURL();
              return url;
            }
          }
        } catch (e) {
          // Méthode 3: Essayer de deviner depuis le nom du fichier (fallback)
          final fileName = item.name;
          if (fileName.toLowerCase().contains(
            normalizedLocation.toLowerCase(),
          )) {
            try {
              final url = await item.getDownloadURL();
              return url;
            } catch (urlError) {
              _errorService.logError(
                'PlaceImageService',
                'Erreur lors de la récupération de l\'URL: $urlError',
              );
            }
          }
        }
      }

      return null;
    } catch (e) {
      return null;
    }
  }

  /// Normalise le nom de location pour la comparaison
  String _normalizeLocationName(String location) {
    return location
        .toLowerCase()
        .replaceAll(RegExp(r'[^a-z0-9]'), '_')
        .replaceAll(RegExp(r'_+'), '_')
        .replaceAll(RegExp(r'^_|_$'), '');
  }

  /// Nettoie les images inutilisées (à appeler manuellement si nécessaire)
  Future<void> cleanupUnusedImages(List<String> usedImageUrls) async {
    try {
      final listResult = await _storage.ref('trip_images').listAll();

      for (final item in listResult.items) {
        try {
          final url = await item.getDownloadURL();

          if (!usedImageUrls.contains(url)) {
            await item.delete();
          }
        } catch (e) {
          _errorService.logError(
            'PlaceImageService',
            'Erreur lors du nettoyage: $e',
          );
        }
      }
    } catch (e) {
      _errorService.logError(
        'PlaceImageService',
        'Erreur lors du nettoyage: $e',
      );
    }
  }

  /// Nettoie spécifiquement les doublons d'images pour la même location
  Future<void> cleanupDuplicateImages() async {
    try {
      final listResult = await _storage.ref('trip_images').listAll();

      // Grouper les images par location normalisée
      final Map<String, List<Reference>> locationGroups = {};

      for (final item in listResult.items) {
        String locationKey = 'unknown';

        try {
          final metadata = await item.getMetadata();
          final storedNormalizedLocation =
              metadata.customMetadata?['normalizedLocation'];
          final storedLocation = metadata.customMetadata?['location'];

          if (storedNormalizedLocation != null) {
            locationKey = storedNormalizedLocation;
          } else if (storedLocation != null) {
            locationKey = _normalizeLocationName(storedLocation);
          } else {
            // Deviner depuis le nom du fichier
            final fileName = item.name;
            final parts = fileName.split('_');
            if (parts.length >= 2) {
              locationKey = parts.take(parts.length - 1).join('_');
            }
          }
        } catch (e) {
          // Utiliser le nom du fichier comme fallback
          final fileName = item.name;
          final parts = fileName.split('_');
          if (parts.length >= 2) {
            locationKey = parts.take(parts.length - 1).join('_');
          }
        }

        if (!locationGroups.containsKey(locationKey)) {
          locationGroups[locationKey] = [];
        }
        locationGroups[locationKey]!.add(item);
      }

      // Supprimer les doublons (garder le plus récent)

      for (final entry in locationGroups.entries) {
        final images = entry.value;

        if (images.length > 1) {
          // Trier par timestamp (garder le plus récent)
          images.sort((a, b) {
            final aTimestamp = _extractTimestampFromName(a.name);
            final bTimestamp = _extractTimestampFromName(b.name);
            return bTimestamp.compareTo(aTimestamp); // Plus récent en premier
          });

          // Supprimer tous sauf le premier (plus récent)
          for (int i = 1; i < images.length; i++) {
            try {
              await images[i].delete();
            } catch (e) {
              _errorService.logError(
                'PlaceImageService',
                'Erreur lors de la suppression du doublon: $e',
              );
            }
          }
        }
      }
    } catch (e) {
      _errorService.logError(
        'PlaceImageService',
        'Erreur lors du nettoyage des doublons: $e',
      );
    }
  }

  /// Extrait le timestamp du nom de fichier
  int _extractTimestampFromName(String fileName) {
    final parts = fileName.split('_');
    if (parts.isNotEmpty) {
      final lastPart = parts.last.split('.').first; // Enlever l'extension
      return int.tryParse(lastPart) ?? 0;
    }
    return 0;
  }

  /// Récupère uniquement l'URL d'une image existante dans le Storage (sans télécharger)
  Future<String?> getExistingImageUrl(String location) async {
    try {
      final existingUrl = await _checkExistingImage(location);
      if (existingUrl != null) {
        return existingUrl;
      }
      return null;
    } catch (e) {
      _errorService.logError(
        'PlaceImageService',
        'Erreur lors de la recherche d\'image existante: $e',
      );
      return null;
    }
  }

  /// Supprimer une image du storage (optionnel, pour le nettoyage)
  Future<void> deleteImage(String imageUrl) async {
    try {
      final ref = _storage.refFromURL(imageUrl);
      await ref.delete();
    } catch (e) {
      _errorService.logError(
        'PlaceImageService',
        'Erreur lors de la suppression de l\'image: $e',
      );
    }
  }
}
