import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:travel_mate/services/error_service.dart';
import '../models/group.dart';
import '../models/group_member.dart';

class GroupRepository {
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  final FirebaseAuth _auth = FirebaseAuth.instance;
  final _errorService = ErrorService();

  CollectionReference get _groupsCollection => _firestore.collection('groups');

  CollectionReference _membersCollection(String groupId) {
    return _groupsCollection.doc(groupId).collection('members');
  }

  Future<String> createGroupWithMembers({
    required Group group,
    required List<GroupMember> members,
  }) async {
    try {
      return await _firestore.runTransaction<String>((transaction) async {
        final groupRef = _groupsCollection.doc();

        // Ajouter les IDs des membres à la liste memberIds
        final memberIds = members.map((m) => m.userId).toList();
        final groupData = group.copyWith(memberIds: memberIds).toMap();
        transaction.set(groupRef, groupData);

        for (var member in members) {
          final memberRef = groupRef.collection('members').doc(member.userId);
          transaction.set(memberRef, member.toMap());
        }

        return groupRef.id;
      });
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur création groupe: $e',
        stackTrace,
      );
      throw Exception('Impossible de créer le groupe');
    }
  }

  Stream<List<Group>> getGroupsByUserId(String userId) {
    return _groupsCollection
        .where('memberIds', arrayContains: userId)
        .snapshots()
        .map((snapshot) {
          return snapshot.docs.map((doc) {
            final groupData = doc.data() as Map<String, dynamic>;
            return Group.fromMap(groupData, doc.id);
          }).toList();
        })
        .handleError((error, stackTrace) {
          _errorService.logError(
            'GroupRepository',
            'Erreur stream groups: $error',
            stackTrace,
          );
          return <Group>[];
        });
  }

  Future<Group?> getGroupById(String groupId) async {
    try {
      final doc = await _groupsCollection.doc(groupId).get();

      if (!doc.exists) return null;

      final group = Group.fromMap(doc.data() as Map<String, dynamic>, doc.id);
      final members = await getGroupMembers(groupId);

      return group.copyWith(members: members);
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur get group: $e',
        stackTrace,
      );
      throw Exception('Impossible de récupérer le groupe');
    }
  }

  Future<Group?> getGroupByTripId(String tripId) async {
    try {
      final userId = _auth.currentUser?.uid;
      if (userId == null) return null;

      // Tentative 1: Requête optimisée avec memberIds
      var querySnapshot = await _groupsCollection
          .where('tripId', isEqualTo: tripId)
          .where('memberIds', arrayContains: userId)
          .limit(1)
          .get();

      // Tentative 2: Fallback pour le créateur (si memberIds est manquant - anciennes données)
      if (querySnapshot.docs.isEmpty) {
        querySnapshot = await _groupsCollection
            .where('tripId', isEqualTo: tripId)
            .where('createdBy', isEqualTo: userId)
            .limit(1)
            .get();

        // Si on trouve le groupe via le fallback, on lance une migration
        if (querySnapshot.docs.isNotEmpty) {
          _migrateGroupData(querySnapshot.docs.first.id);
        }
      }

      if (querySnapshot.docs.isEmpty) return null;

      final doc = querySnapshot.docs.first;
      final group = Group.fromMap(doc.data() as Map<String, dynamic>, doc.id);
      final members = await getGroupMembers(doc.id);

      return group.copyWith(members: members);
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur get group by trip: $e',
        stackTrace,
      );
      throw Exception('Impossible de récupérer le groupe du voyage');
    }
  }

  /// Méthode utilitaire pour migrer les anciennes données
  Future<void> _migrateGroupData(String groupId) async {
    try {
      final members = await getGroupMembers(groupId);
      final memberIds = members.map((m) => m.userId).toList();

      if (memberIds.isNotEmpty) {
        await _groupsCollection.doc(groupId).update({'memberIds': memberIds});
        _errorService.logSuccess(
          'GroupRepository',
          'Migration réussie pour le groupe $groupId',
        );
      }
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur de migration pour le groupe $groupId: $e',
        stackTrace,
      );
    }
  }

  Future<List<GroupMember>> getGroupMembers(String groupId) async {
    try {
      final snapshot = await _membersCollection(groupId).get();
      return snapshot.docs.map((doc) {
        return GroupMember.fromMap(doc.data() as Map<String, dynamic>, doc.id);
      }).toList();
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur get members: $e',
        stackTrace,
      );
      throw Exception('Impossible de récupérer les membres');
    }
  }

  Future<void> addMember(String groupId, GroupMember member) async {
    try {
      // 1. Récupérer le groupe pour avoir le tripId
      final group = await getGroupById(groupId);
      if (group == null) throw Exception('Groupe introuvable');

      // 2. Ajouter le membre dans la sous-collection members du groupe
      await _membersCollection(groupId).doc(member.userId).set(member.toMap());

      // 3. Mettre à jour la liste memberIds du groupe
      await _groupsCollection.doc(groupId).update({
        'updatedAt': DateTime.now().millisecondsSinceEpoch,
        'memberIds': FieldValue.arrayUnion([member.userId]),
      });

      // 4. Mettre à jour la liste participants du voyage
      await _firestore.collection('trips').doc(group.tripId).update({
        'participants': FieldValue.arrayUnion([member.userId]),
      });
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur add member: $e',
        stackTrace,
      );
      throw Exception('Impossible d\'ajouter le membre');
    }
  }

  Future<void> removeMember(String groupId, String userId) async {
    try {
      // 1. Récupérer le groupe pour avoir le tripId
      final group = await getGroupById(groupId);
      if (group == null) throw Exception('Groupe introuvable');

      // 2. Supprimer le membre de la sous-collection members du groupe
      await _membersCollection(groupId).doc(userId).delete();

      // 3. Mettre à jour la liste memberIds du groupe
      await _groupsCollection.doc(groupId).update({
        'updatedAt': DateTime.now().millisecondsSinceEpoch,
        'memberIds': FieldValue.arrayRemove([userId]),
      });

      // 4. Mettre à jour la liste participants du voyage
      await _firestore.collection('trips').doc(group.tripId).update({
        'participants': FieldValue.arrayRemove([userId]),
      });
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur remove member: $e',
        stackTrace,
      );
      throw Exception('Impossible de supprimer le membre');
    }
  }

  Future<void> updateGroup(String groupId, Group group) async {
    try {
      await _groupsCollection
          .doc(groupId)
          .update(
            group.toMap()
              ..['updatedAt'] = DateTime.now().millisecondsSinceEpoch,
          );
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur update group: $e',
        stackTrace,
      );
      throw Exception('Impossible de mettre à jour le groupe');
    }
  }

  Future<void> deleteGroup(String tripId) async {
    try {
      final userId = _auth.currentUser?.uid;
      if (userId == null) throw Exception('Utilisateur non connecté');

      final querySnapshot = await _groupsCollection
          .where('tripId', isEqualTo: tripId)
          .where('createdBy', isEqualTo: userId)
          .limit(1)
          .get();

      if (querySnapshot.docs.isEmpty) {
        throw Exception('Aucun groupe trouvé pour ce voyage');
      }

      final groupDoc = querySnapshot.docs.first;
      final groupId = groupDoc.id;

      final membersSnapshot = await _membersCollection(groupId).get();
      for (var doc in membersSnapshot.docs) {
        await doc.reference.delete();
      }

      await _groupsCollection.doc(groupId).delete();
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur delete group: $e',
        stackTrace,
      );
      throw Exception('Impossible de supprimer le groupe');
    }
  }

  Stream<List<GroupMember>> watchGroupMembers(String groupId) {
    return _membersCollection(groupId).snapshots().map(
      (snapshot) => snapshot.docs
          .map(
            (doc) =>
                GroupMember.fromMap(doc.data() as Map<String, dynamic>, doc.id),
          )
          .toList(),
    );
  }

  Future<void> updateMemberDetails({
    required String userId,
    String? firstName,
    String? lastName,
    String? profilePictureUrl,
  }) async {
    try {
      // 1. Trouver tous les groupes où l'utilisateur est membre
      final groupsSnapshot = await _groupsCollection
          .where('memberIds', arrayContains: userId)
          .get();

      // 2. Mettre à jour les infos du membre dans chaque groupe
      for (final groupDoc in groupsSnapshot.docs) {
        final memberRef = _membersCollection(groupDoc.id).doc(userId);

        final updates = <String, dynamic>{};
        if (firstName != null) updates['firstName'] = firstName;
        if (lastName != null) updates['lastName'] = lastName;
        if (profilePictureUrl != null) {
          updates['profilePictureUrl'] = profilePictureUrl;
        }

        if (updates.isNotEmpty) {
          await memberRef.update(updates);
        }
      }
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupRepository',
        'Erreur update member details: $e',
        stackTrace,
      );
      // On ne throw pas d'exception ici pour ne pas bloquer l'update user principal
      // C'est une opération "best effort"
    }
  }
}
