import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/activity.dart';
import '../services/error_service.dart';

/// Repository pour la gestion des activités dans Firestore
class ActivityRepository {
  static final ActivityRepository _instance = ActivityRepository._internal();
  factory ActivityRepository() => _instance;
  ActivityRepository._internal();

  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  final ErrorService _errorService = ErrorService();

  static const String _collection = 'activities';

  /// Ajoute une nouvelle activité
  Future<String?> addActivity(Activity activity) async {
    try {
      _errorService.logInfo(
        'ActivityRepository',
        'Ajout d\'une activité: ${activity.name}',
      );

      final docRef = await _firestore
          .collection(_collection)
          .add(activity.toMap());

      // Mettre à jour l'activité avec l'ID généré
      await docRef.update({'id': docRef.id});

      _errorService.logSuccess(
        'ActivityRepository',
        'Activité ajoutée avec ID: ${docRef.id}',
      );
      return docRef.id;
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur lors de l\'ajout: $e',
      );
      _errorService.logError(
        'activity_repository',
        'Erreur ajout activité: $e',
      );
      return null;
    }
  }

  /// Récupère toutes les activités d'un voyage
  Future<List<Activity>> getActivitiesByTrip(String tripId) async {
    try {
      _errorService.logInfo(
        'ActivityRepository',
        'Récupération des activités pour le voyage: $tripId',
      );

      // Modifié pour éviter l'erreur d'index composite
      // On récupère d'abord par tripId, puis on trie en mémoire
      final querySnapshot = await _firestore
          .collection(_collection)
          .where('tripId', isEqualTo: tripId)
          .get();

      final activities = querySnapshot.docs
          .map((doc) => Activity.fromSnapshot(doc))
          .toList();

      // Tri en mémoire par date de mise à jour (plus récent en premier)
      activities.sort((a, b) => b.updatedAt.compareTo(a.updatedAt));

      _errorService.logInfo(
        'ActivityRepository',
        '${activities.length} activités trouvées',
      );
      return activities;
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur lors de la récupération: $e',
      );
      _errorService.logError(
        'activity_repository',
        'Erreur récupération activités: $e',
      );
      return [];
    }
  }

  /// Récupère une activité par son ID (alias pour getActivityById pour compatibilité)
  Future<Activity?> getActivity(String tripId, String activityId) async {
    return getActivityById(activityId);
  }

  /// Récupère une activité par son ID
  Future<Activity?> getActivityById(String activityId) async {
    try {
      final doc = await _firestore
          .collection(_collection)
          .doc(activityId)
          .get();

      if (doc.exists) {
        return Activity.fromSnapshot(doc);
      }

      return null;
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur récupération activité: $e',
      );
      _errorService.logError(
        'activity_repository',
        'Erreur récupération activité: $e',
      );
      return null;
    }
  }

  /// Met à jour une activité
  Future<bool> updateActivity(Activity activity) async {
    try {
      _errorService.logInfo(
        'ActivityRepository',
        'Mise à jour de l\'activité: ${activity.id}',
      );

      await _firestore
          .collection(_collection)
          .doc(activity.id)
          .update(activity.copyWith(updatedAt: DateTime.now()).toMap());

      _errorService.logSuccess(
        'ActivityRepository',
        'Activité mise à jour avec succès',
      );
      return true;
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur lors de la mise à jour: $e',
      );
      _errorService.logError(
        'activity_repository',
        'Erreur mise à jour activité: $e',
      );
      return false;
    }
  }

  /// Supprime une activité
  Future<bool> deleteActivity(String activityId) async {
    try {
      _errorService.logInfo(
        'ActivityRepository',
        'Suppression de l\'activité: $activityId',
      );

      await _firestore.collection(_collection).doc(activityId).delete();

      _errorService.logSuccess(
        'ActivityRepository',
        'Activité supprimée avec succès',
      );
      return true;
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur lors de la suppression: $e',
      );
      _errorService.logError(
        'activity_repository',
        'Erreur suppression activité: $e',
      );
      return false;
    }
  }

  /// Vote pour une activité
  Future<bool> voteForActivity(
    String activityId,
    String userId,
    int vote,
  ) async {
    try {
      // Validation des paramètres
      if (activityId.isEmpty) {
        _errorService.logError('ActivityRepository', 'ID d\'activité vide');
        _errorService.logError(
          'activity_repository',
          'ID d\'activité vide pour le vote',
        );
        return false;
      }

      if (userId.isEmpty) {
        _errorService.logError('ActivityRepository', 'ID d\'utilisateur vide');
        _errorService.logError(
          'activity_repository',
          'ID d\'utilisateur vide pour le vote',
        );
        return false;
      }

      _errorService.logInfo(
        'ActivityRepository',
        'Vote pour l\'activité $activityId: $vote',
      );

      // vote: 1 pour positif, -1 pour négatif, 0 pour supprimer le vote
      final activityRef = _firestore.collection(_collection).doc(activityId);

      await _firestore.runTransaction((transaction) async {
        final snapshot = await transaction.get(activityRef);

        if (!snapshot.exists) {
          throw Exception('Activité non trouvée');
        }

        final activity = Activity.fromSnapshot(snapshot);
        final newVotes = Map<String, int>.from(activity.votes);

        if (vote == 0) {
          // Supprimer le vote
          newVotes.remove(userId);
        } else {
          // Ajouter ou modifier le vote
          newVotes[userId] = vote;
        }

        transaction.update(activityRef, {
          'votes': newVotes,
          'updatedAt': Timestamp.fromDate(DateTime.now()),
        });
      });

      _errorService.logSuccess(
        'ActivityRepository',
        'Vote enregistré avec succès',
      );
      return true;
    } catch (e) {
      _errorService.logError('ActivityRepository', 'Erreur lors du vote: $e');
      _errorService.logError('activity_repository', 'Erreur vote: $e');
      return false;
    }
  }

  /// Récupère les activités avec un stream pour les mises à jour en temps réel
  Stream<List<Activity>> getActivitiesStream(String tripId) {
    try {
      return _firestore
          .collection(_collection)
          .where('tripId', isEqualTo: tripId)
          .snapshots()
          .map((snapshot) {
            final activities = snapshot.docs
                .map((doc) => Activity.fromSnapshot(doc))
                .toList();

            // Tri en mémoire par date de mise à jour (plus récent en premier)
            activities.sort((a, b) => b.updatedAt.compareTo(a.updatedAt));

            return activities;
          });
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur stream activités: $e',
      );
      _errorService.logError(
        'activity_repository',
        'Erreur stream activités: $e',
      );
      return Stream.value([]);
    }
  }

  /// Ajoute plusieurs activités en lot
  Future<List<String>> addActivitiesBatch(List<Activity> activities) async {
    try {
      _errorService.logInfo(
        'ActivityRepository',
        'Ajout en lot de ${activities.length} activités',
      );

      final batch = _firestore.batch();
      final addedIds = <String>[];

      for (final activity in activities) {
        final docRef = _firestore.collection(_collection).doc();
        final activityWithId = activity.copyWith(id: docRef.id);
        batch.set(docRef, activityWithId.toMap());
        addedIds.add(docRef.id);
      }

      await batch.commit();

      _errorService.logSuccess(
        'ActivityRepository',
        '${addedIds.length} activités ajoutées en lot',
      );
      return addedIds;
    } catch (e) {
      _errorService.logError('ActivityRepository', 'Erreur ajout en lot: $e');
      _errorService.logError('activity_repository', 'Erreur ajout en lot: $e');
      return [];
    }
  }

  /// Recherche des activités par catégorie
  Future<List<Activity>> getActivitiesByCategory(
    String tripId,
    String category,
  ) async {
    try {
      _errorService.logInfo(
        'ActivityRepository',
        'Recherche par catégorie: $category pour le voyage: $tripId',
      );

      // Récupérer toutes les activités du voyage puis filtrer en mémoire
      final querySnapshot = await _firestore
          .collection(_collection)
          .where('tripId', isEqualTo: tripId)
          .get();

      final activities = querySnapshot.docs
          .map((doc) => Activity.fromSnapshot(doc))
          .where((activity) => activity.category == category)
          .toList();

      // Tri en mémoire par date de mise à jour (plus récent en premier)
      activities.sort((a, b) => b.updatedAt.compareTo(a.updatedAt));

      return activities;
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur recherche par catégorie: $e',
      );
      _errorService.logError(
        'activity_repository',
        'Erreur recherche par catégorie: $e',
      );
      return [];
    }
  }

  /// Récupère les activités les mieux notées d'un voyage
  Future<List<Activity>> getTopRatedActivities(
    String tripId, {
    int limit = 10,
  }) async {
    try {
      final activities = await getActivitiesByTrip(tripId);

      // Trier par nombre de votes positifs puis par note Google
      activities.sort((a, b) {
        final aScore = a.totalVotes;
        final bScore = b.totalVotes;

        if (aScore != bScore) {
          return bScore.compareTo(aScore);
        }

        return (b.rating ?? 0).compareTo(a.rating ?? 0);
      });

      return activities.take(limit).toList();
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur activités top rated: $e',
      );
      _errorService.logError('activity_repository', 'Erreur top rated: $e');
      return [];
    }
  }

  /// Vérifie si une activité existe déjà pour un voyage (basé sur placeId)
  Future<Activity?> findExistingActivity(String tripId, String placeId) async {
    try {
      final querySnapshot = await _firestore
          .collection(_collection)
          .where('tripId', isEqualTo: tripId)
          .where('placeId', isEqualTo: placeId)
          .limit(1)
          .get();

      if (querySnapshot.docs.isNotEmpty) {
        return Activity.fromSnapshot(querySnapshot.docs.first);
      }

      return null;
    } catch (e) {
      _errorService.logError(
        'ActivityRepository',
        'Erreur recherche activité existante: $e',
      );
      return null;
    }
  }
}
