import 'dart:io';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../models/activity.dart';
import '../services/error_service.dart';
import '../services/logger_service.dart';

/// Service pour rechercher des activités touristiques via Google Places API
class ActivityPlacesService {
  static final ActivityPlacesService _instance =
      ActivityPlacesService._internal();
  factory ActivityPlacesService() => _instance;
  ActivityPlacesService._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'] ?? '';
  }

  /// Recherche des activités près d'une destination
  Future<List<Activity>> searchActivities({
    required String destination,
    required String tripId,
    ActivityCategory? category,
    int radius = 5000,
    int maxResults = 20,
    int offset = 0,
  }) async {
    try {
      LoggerService.info(
        'ActivityPlacesService: Recherche d\'activités pour: $destination (max: $maxResults, offset: $offset)',
      );

      // 1. Géocoder la destination
      final coordinates = await _geocodeDestination(destination);

      // 2. Rechercher les activités par catégorie ou toutes les catégories
      List<Activity> allActivities = [];

      if (category != null) {
        final activities = await _searchByCategory(
          coordinates['lat']!,
          coordinates['lng']!,
          category,
          tripId,
          radius,
        );
        allActivities.addAll(activities);
      } else {
        // Rechercher dans toutes les catégories principales
        final mainCategories = [
          ActivityCategory.attraction,
          ActivityCategory.museum,
          ActivityCategory.restaurant,
          ActivityCategory.culture,
          ActivityCategory.nature,
        ];

        for (final cat in mainCategories) {
          final activities = await _searchByCategory(
            coordinates['lat']!,
            coordinates['lng']!,
            cat,
            tripId,
            radius,
          );
          allActivities.addAll(activities);
        }
      }

      // 3. Supprimer les doublons et trier par note
      final uniqueActivities = _removeDuplicates(allActivities);
      uniqueActivities.sort((a, b) => (b.rating ?? 0).compareTo(a.rating ?? 0));

      LoggerService.info(
        'ActivityPlacesService: ${uniqueActivities.length} activités trouvées au total',
      );

      // 4. Appliquer la pagination
      final startIndex = offset;
      final endIndex = (startIndex + maxResults).clamp(
        0,
        uniqueActivities.length,
      );

      if (startIndex >= uniqueActivities.length) {
        LoggerService.info(
          'ActivityPlacesService: Offset $startIndex dépasse le nombre total (${uniqueActivities.length})',
        );
        return [];
      }

      final paginatedResults = uniqueActivities.sublist(startIndex, endIndex);
      LoggerService.info(
        'ActivityPlacesService: Retour de ${paginatedResults.length} activités (offset: $offset, max: $maxResults)',
      );

      return paginatedResults;
    } catch (e) {
      LoggerService.error(
        'ActivityPlacesService: Erreur lors de la recherche: $e',
      );
      _errorService.logError('activity_places_service', e);
      return [];
    }
  }

  /// Géocode une destination pour obtenir les coordonnées
  Future<Map<String, dynamic>> _geocodeDestination(String destination) async {
    try {
      // Vérifier que la clé API est configurée
      if (_apiKey.isEmpty) {
        LoggerService.error(
          'ActivityPlacesService: Clé API Google Maps manquante',
        );
        throw Exception('Clé API Google Maps non configurée');
      }

      final encodedDestination = Uri.encodeComponent(destination);
      final url =
          'https://maps.googleapis.com/maps/api/geocode/json?address=$encodedDestination&key=$_apiKey&language=fr';

      LoggerService.info('ActivityPlacesService: Géocodage de "$destination"');
      LoggerService.info('ActivityPlacesService: URL = $url');

      final response = await http.get(Uri.parse(url));

      LoggerService.info(
        'ActivityPlacesService: Status code = ${response.statusCode}',
      );

      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        LoggerService.info(
          'ActivityPlacesService: 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'].toDouble(),
            'lng': location['lng'].toDouble(),
          };
          LoggerService.info(
            'ActivityPlacesService: Coordonnées trouvées = $coordinates',
          );
          return coordinates;
        } else {
          LoggerService.error(
            'ActivityPlacesService: Erreur API = ${data['error_message'] ?? data['status']}',
          );
          if (data['status'] == 'REQUEST_DENIED') {
            throw Exception(
              '🔑 Clé API non autorisée. Activez les APIs suivantes dans Google Cloud Console:\n'
              '• Geocoding API\n'
              '• Places API\n'
              '• Maps JavaScript API\n'
              'Puis ajoutez des restrictions appropriées.',
            );
          } else if (data['status'] == 'ZERO_RESULTS') {
            throw Exception('Aucun résultat trouvé pour cette destination');
          } else {
            throw Exception('Erreur API: ${data['status']}');
          }
        }
      } else {
        throw Exception('Erreur HTTP ${response.statusCode}');
      }
    } catch (e) {
      LoggerService.error('ActivityPlacesService: Erreur géocodage: $e');
      rethrow; // Rethrow pour permettre la gestion d'erreur en amont
    }
  }

  /// Recherche des activités par catégorie
  Future<List<Activity>> _searchByCategory(
    double lat,
    double lng,
    ActivityCategory category,
    String tripId,
    int radius,
  ) async {
    try {
      final url =
          'https://maps.googleapis.com/maps/api/place/nearbysearch/json'
          '?location=$lat,$lng'
          '&radius=$radius'
          '&type=${category.googlePlaceType}'
          '&key=$_apiKey'
          '&language=fr';

      final response = await http.get(Uri.parse(url));

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

        if (data['status'] == 'OK') {
          final List<Activity> activities = [];

          for (final place in data['results']) {
            try {
              final activity = await _convertPlaceToActivity(
                place,
                tripId,
                category,
              );
              if (activity != null) {
                activities.add(activity);
              }
            } catch (e) {
              LoggerService.error(
                'ActivityPlacesService: Erreur conversion place: $e',
              );
            }
          }

          return activities;
        }
      }

      return [];
    } catch (e) {
      LoggerService.error(
        'ActivityPlacesService: Erreur recherche par catégorie: $e',
      );
      return [];
    }
  }

  /// Convertit un résultat Google Places en Activity
  Future<Activity?> _convertPlaceToActivity(
    Map<String, dynamic> place,
    String tripId,
    ActivityCategory category,
  ) async {
    try {
      final placeId = place['place_id'];
      if (placeId == null) return null;

      // Récupérer les détails supplémentaires
      final details = await _getPlaceDetails(placeId);

      final geometry = place['geometry']?['location'];
      final photos = place['photos'] as List?;

      // Obtenir une image de qualité
      String? imageUrl;
      if (photos != null && photos.isNotEmpty) {
        final photoReference = photos.first['photo_reference'];
        imageUrl =
            'https://maps.googleapis.com/maps/api/place/photo'
            '?maxwidth=800'
            '&photoreference=$photoReference'
            '&key=$_apiKey';
      }

      return Activity(
        id: '', // Sera généré lors de la sauvegarde
        tripId: tripId,
        name: place['name'] ?? 'Activité inconnue',
        description:
            details?['editorial_summary']?['overview'] ??
            details?['formatted_address'] ??
            'Découvrez cette activité incontournable !',
        category: category.displayName,
        imageUrl: imageUrl,
        rating: place['rating']?.toDouble(),
        priceLevel: _getPriceLevelString(place['price_level']),
        address: details?['formatted_address'] ?? place['vicinity'],
        latitude: geometry?['lat']?.toDouble(),
        longitude: geometry?['lng']?.toDouble(),
        placeId: placeId,
        website: details?['website'],
        phoneNumber: details?['formatted_phone_number'],
        openingHours: _parseOpeningHours(details?['opening_hours']),
        votes: {},
        createdAt: DateTime.now(),
        updatedAt: DateTime.now(),
      );
    } catch (e) {
      LoggerService.error('ActivityPlacesService: Erreur conversion place: $e');
      return null;
    }
  }

  /// Récupère les détails d'un lieu
  Future<Map<String, dynamic>?> _getPlaceDetails(String placeId) async {
    try {
      final url =
          'https://maps.googleapis.com/maps/api/place/details/json'
          '?place_id=$placeId'
          '&fields=formatted_address,formatted_phone_number,website,opening_hours,editorial_summary'
          '&key=$_apiKey'
          '&language=fr';

      final response = await http.get(Uri.parse(url));

      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        if (data['status'] == 'OK') {
          return data['result'];
        }
      }

      return null;
    } catch (e) {
      LoggerService.error(
        'ActivityPlacesService: Erreur récupération détails: $e',
      );
      return null;
    }
  }

  /// Convertit le niveau de prix en string
  String? _getPriceLevelString(int? priceLevel) {
    if (priceLevel == null) return null;
    final level = PriceLevel.fromLevel(priceLevel);
    return level?.displayName;
  }

  /// Parse les heures d'ouverture
  List<String> _parseOpeningHours(Map<String, dynamic>? openingHours) {
    if (openingHours == null) return [];

    final weekdayText = openingHours['weekday_text'] as List?;
    if (weekdayText == null) return [];

    return weekdayText.cast<String>();
  }

  /// Supprime les doublons basés sur le placeId
  List<Activity> _removeDuplicates(List<Activity> activities) {
    final seen = <String>{};
    return activities.where((activity) {
      if (activity.placeId == null) return true;
      if (seen.contains(activity.placeId)) return false;
      seen.add(activity.placeId!);
      return true;
    }).toList();
  }

  /// Recherche d'activités par texte libre
  Future<List<Activity>> searchActivitiesByText({
    required String query,
    required String destination,
    required String tripId,
    int radius = 5000,
  }) async {
    try {
      LoggerService.info(
        'ActivityPlacesService: Recherche textuelle: $query à $destination',
      );

      // Géocoder la destination
      final coordinates = await _geocodeDestination(destination);

      final encodedQuery = Uri.encodeComponent(query);
      final url =
          'https://maps.googleapis.com/maps/api/place/textsearch/json'
          '?query=$encodedQuery in $destination'
          '&location=${coordinates['lat']},${coordinates['lng']}'
          '&radius=$radius'
          '&key=$_apiKey'
          '&language=fr';

      final response = await http.get(Uri.parse(url));

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

        if (data['status'] == 'OK') {
          final List<Activity> activities = [];

          for (final place in data['results']) {
            try {
              // Déterminer la catégorie basée sur les types du lieu
              final types = List<String>.from(place['types'] ?? []);
              final category = _determineCategoryFromTypes(types);

              final activity = await _convertPlaceToActivity(
                place,
                tripId,
                category,
              );
              if (activity != null) {
                activities.add(activity);
              }
            } catch (e) {
              LoggerService.error(
                'ActivityPlacesService: Erreur conversion place: $e',
              );
            }
          }

          return activities;
        }
      }

      return [];
    } catch (e) {
      LoggerService.error(
        'ActivityPlacesService: Erreur recherche textuelle: $e',
      );
      return [];
    }
  }

  /// Détermine la catégorie d'activité basée sur les types Google Places
  ActivityCategory _determineCategoryFromTypes(List<String> types) {
    for (final type in types) {
      for (final category in ActivityCategory.values) {
        if (category.googlePlaceType == type) {
          return category;
        }
      }
    }

    // Catégories par défaut basées sur des types communs
    if (types.contains('restaurant') || types.contains('food')) {
      return ActivityCategory.restaurant;
    } else if (types.contains('museum')) {
      return ActivityCategory.museum;
    } else if (types.contains('park')) {
      return ActivityCategory.nature;
    } else if (types.contains('shopping_mall') || types.contains('store')) {
      return ActivityCategory.shopping;
    } else if (types.contains('night_club') || types.contains('bar')) {
      return ActivityCategory.nightlife;
    }

    return ActivityCategory.attraction; // Par défaut
  }

  /// Recherche d'activités avec pagination (6 par page)
  Future<Map<String, dynamic>> searchActivitiesPaginated({
    String? destination,
    double? latitude,
    double? longitude,
    required String tripId,
    ActivityCategory? category,
    int pageSize = 6,
    String? nextPageToken,
    int radius = 5000,
  }) async {
    try {
      double lat, lng;

      // Utiliser les coordonnées fournies ou géocoder la destination
      if (latitude != null && longitude != null) {
        lat = latitude;
        lng = longitude;
        LoggerService.info(
          'ActivityPlacesService: Utilisation des coordonnées pré-géolocalisées: $lat, $lng',
        );
      } else if (destination != null) {
        LoggerService.info(
          'ActivityPlacesService: Géolocalisation de la destination: $destination',
        );
        final coordinates = await _geocodeDestination(destination);
        lat = coordinates['lat']!;
        lng = coordinates['lng']!;
      } else {
        throw Exception('Destination ou coordonnées requises');
      }

      LoggerService.info(
        'ActivityPlacesService: Recherche paginée aux coordonnées: $lat, $lng (page: ${nextPageToken ?? "première"})',
      );

      // 2. Rechercher les activités par catégorie avec pagination
      if (category != null) {
        return await _searchByCategoryPaginated(
          lat,
          lng,
          category,
          tripId,
          radius,
          pageSize,
          nextPageToken,
        );
      } else {
        // Pour toutes les catégories, faire une recherche générale paginée
        return await _searchAllCategoriesPaginated(
          lat,
          lng,
          tripId,
          radius,
          pageSize,
          nextPageToken,
        );
      }
    } catch (e) {
      LoggerService.error(
        'ActivityPlacesService: Erreur recherche paginée: $e',
      );
      _errorService.logError('activity_places_service', e);
      return {
        'activities': <Activity>[],
        'nextPageToken': null,
        'hasMoreData': false,
      };
    }
  }

  /// Recherche paginée par catégorie spécifique
  Future<Map<String, dynamic>> _searchByCategoryPaginated(
    double lat,
    double lng,
    ActivityCategory category,
    String tripId,
    int radius,
    int pageSize,
    String? nextPageToken,
  ) async {
    try {
      String url =
          'https://maps.googleapis.com/maps/api/place/nearbysearch/json'
          '?location=$lat,$lng'
          '&radius=$radius'
          '&type=${category.googlePlaceType}'
          '&key=$_apiKey'
          '&language=fr';

      if (nextPageToken != null) {
        url += '&pagetoken=$nextPageToken';
      }

      final response = await http.get(Uri.parse(url));

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

        if (data['status'] == 'OK') {
          final List<Activity> activities = [];
          final results = data['results'] as List? ?? [];

          // Limiter à pageSize résultats
          final limitedResults = results.take(pageSize).toList();

          for (final place in limitedResults) {
            try {
              final activity = await _convertPlaceToActivity(
                place,
                tripId,
                category,
              );
              if (activity != null) {
                activities.add(activity);
              }
            } catch (e) {
              LoggerService.error(
                'ActivityPlacesService: Erreur conversion place: $e',
              );
            }
          }

          return {
            'activities': activities,
            'nextPageToken': data['next_page_token'],
            'hasMoreData': data['next_page_token'] != null,
          };
        }
      }

      return {
        'activities': <Activity>[],
        'nextPageToken': null,
        'hasMoreData': false,
      };
    } catch (e) {
      LoggerService.error(
        'ActivityPlacesService: Erreur recherche catégorie paginée: $e',
      );
      return {
        'activities': <Activity>[],
        'nextPageToken': null,
        'hasMoreData': false,
      };
    }
  }

  /// Recherche paginée pour toutes les catégories
  Future<Map<String, dynamic>> _searchAllCategoriesPaginated(
    double lat,
    double lng,
    String tripId,
    int radius,
    int pageSize,
    String? nextPageToken,
  ) async {
    try {
      // Pour toutes les catégories, on utilise une recherche plus générale
      String url =
          'https://maps.googleapis.com/maps/api/place/nearbysearch/json'
          '?location=$lat,$lng'
          '&radius=$radius'
          '&type=tourist_attraction'
          '&key=$_apiKey'
          '&language=fr';

      if (nextPageToken != null) {
        url += '&pagetoken=$nextPageToken';
      }

      final response = await http.get(Uri.parse(url));

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

        if (data['status'] == 'OK') {
          final List<Activity> activities = [];
          final results = data['results'] as List? ?? [];

          // Limiter à pageSize résultats
          final limitedResults = results.take(pageSize).toList();

          for (final place in limitedResults) {
            try {
              // Déterminer la catégorie basée sur les types du lieu
              final types = List<String>.from(place['types'] ?? []);
              final category = _determineCategoryFromTypes(types);

              final activity = await _convertPlaceToActivity(
                place,
                tripId,
                category,
              );
              if (activity != null) {
                activities.add(activity);
              }
            } catch (e) {
              LoggerService.error(
                'ActivityPlacesService: Erreur conversion place: $e',
              );
            }
          }

          return {
            'activities': activities,
            'nextPageToken': data['next_page_token'],
            'hasMoreData': data['next_page_token'] != null,
          };
        }
      }

      return {
        'activities': <Activity>[],
        'nextPageToken': null,
        'hasMoreData': false,
      };
    } catch (e) {
      LoggerService.error(
        'ActivityPlacesService: Erreur recherche toutes catégories paginée: $e',
      );
      return {
        'activities': <Activity>[],
        'nextPageToken': null,
        'hasMoreData': false,
      };
    }
  }
}
