import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:travel_mate/services/error_service.dart';
import '../blocs/user/user_state.dart';

/// Service for managing user operations with Firestore and Firebase Auth.
/// 
/// This service provides functionality for user management including creating,
/// retrieving, updating, and deleting user data in Firestore. It also handles
/// user authentication state and provides methods for user profile management.
class UserService {
  /// Error service for logging user operation errors.
  final _errorService = ErrorService();
  
  /// Firestore instance for database operations.
  final FirebaseFirestore _firestore;
  
  /// Firebase Auth instance for user authentication.
  final FirebaseAuth _auth;
  
  /// Collection name for users in Firestore.
  static const String _usersCollection = 'users';

  /// Creates a new [UserService] with optional Firestore and Auth instances.
  /// 
  /// If [firestore] or [auth] are not provided, the default instances will be used.
  UserService({
    FirebaseFirestore? firestore,
    FirebaseAuth? auth,
  })  : _firestore = firestore ?? FirebaseFirestore.instance,
        _auth = auth ?? FirebaseAuth.instance;

  /// Gets the currently authenticated Firebase user.
  /// 
  /// Returns the [User] object if authenticated, null otherwise.
  User? getCurrentFirebaseUser() {
    return _auth.currentUser;
  }

  /// Gets the ID of the currently authenticated user.
  /// 
  /// Returns the user ID string if authenticated, null otherwise.
  String? getCurrentUserId() {
    return _auth.currentUser?.uid;
  }

  /// Creates a new user document in Firestore.
  /// 
  /// Takes a [UserModel] object and stores it in the users collection.
  /// Returns true if successful, false if an error occurs.
  /// 
  /// [user] - The user model to create in Firestore
  Future<bool> createUser(UserModel user) async {
    try {
      await _firestore
          .collection(_usersCollection)
          .doc(user.id)
          .set(user.toJson());
      return true;
    } catch (e) {
      _errorService.logError('Error creating user: $e', StackTrace.current);
      return false;
    }
  }

  /// Retrieves a user by their ID from Firestore.
  /// 
  /// Returns a [UserModel] if the user exists, null otherwise.
  /// 
  /// [userId] - The ID of the user to retrieve
  Future<UserModel?> getUserById(String userId) async {
    try {
      final doc = await _firestore
          .collection(_usersCollection)
          .doc(userId)
          .get();

      if (doc.exists) {
        return UserModel.fromJson({
          'id': doc.id,
          ...doc.data() as Map<String, dynamic>,
        });
      }
      return null;
    } catch (e) {
      _errorService.logError('Erreur lors de la récupération de l\'utilisateur: $e', StackTrace.current);
      return null;
    }
  }

  // Obtenir un utilisateur par son email
  Future<UserModel?> getUserByEmail(String email) async {
    try {
      final querySnapshot = await _firestore
          .collection(_usersCollection)
          .where('email', isEqualTo: email)
          .limit(1)
          .get();

      if (querySnapshot.docs.isNotEmpty) {
        final doc = querySnapshot.docs.first;
        return UserModel.fromJson({
          'id': doc.id,
          ...doc.data(),
        });
      }
      return null;
    } catch (e) {
      _errorService.logError('Erreur lors de la récupération de l\'utilisateur par email: $e', StackTrace.current);
      return null;
    }
  }

  // Obtenir l'ID d'un utilisateur par son email
  Future<String?> getUserIdByEmail(String email) async {
    try {
      final querySnapshot = await _firestore
          .collection(_usersCollection)
          .where('email', isEqualTo: email)
          .limit(1)
          .get();

      if (querySnapshot.docs.isNotEmpty) {
        return querySnapshot.docs.first.id;
      }
      return null;
    } catch (e) {
      _errorService.logError('Erreur lors de la récupération de l\'ID utilisateur: $e', StackTrace.current);
      return null;
    }
  }

  // Mettre à jour un utilisateur
  Future<bool> updateUser(String userId, Map<String, dynamic> userData) async {
    try {
      await _firestore
          .collection(_usersCollection)
          .doc(userId)
          .update(userData);
      return true;
    } catch (e) {
      _errorService.logError('Erreur lors de la mise à jour de l\'utilisateur: $e', StackTrace.current);
      return false;
    }
  }

  // Supprimer un utilisateur
  Future<bool> deleteUser(String userId) async {
    try {
      await _firestore
          .collection(_usersCollection)
          .doc(userId)
          .delete();
      return true;
    } catch (e) {
      _errorService.logError('Erreur lors de la suppression de l\'utilisateur: $e', StackTrace.current);
      return false;
    }
  }

  // Vérifier si un email existe déjà
  Future<bool> emailExists(String email) async {
    try {
      final querySnapshot = await _firestore
          .collection(_usersCollection)
          .where('email', isEqualTo: email)
          .limit(1)
          .get();

      return querySnapshot.docs.isNotEmpty;
    } catch (e) {
      _errorService.logError('Erreur lors de la vérification de l\'email: $e', StackTrace.current);
      return false;
    }
  }

  // Obtenir plusieurs utilisateurs par leurs IDs
  Future<List<UserModel>> getUsersByIds(List<String> userIds) async {
    try {
      if (userIds.isEmpty) return [];

      final List<UserModel> users = [];

      // Firestore a une limite de 10 éléments pour les requêtes 'in'
      // Donc on divise en chunks de 10
      for (int i = 0; i < userIds.length; i += 10) {
        final chunk = userIds.skip(i).take(10).toList();
        
        final querySnapshot = await _firestore
            .collection(_usersCollection)
            .where(FieldPath.documentId, whereIn: chunk)
            .get();

        for (var doc in querySnapshot.docs) {
          users.add(UserModel.fromJson({
            'id': doc.id,
            ...doc.data(),
          }));
        }
      }

      return users;
    } catch (e) {
      _errorService.logError('Erreur lors de la récupération des utilisateurs: $e', StackTrace.current);
      return [];
    }
  }

  // Obtenir tous les utilisateurs (à utiliser avec précaution)
  Future<List<UserModel>> getAllUsers() async {
    try {
      final querySnapshot = await _firestore
          .collection(_usersCollection)
          .get();

      return querySnapshot.docs.map((doc) {
        return UserModel.fromJson({
          'id': doc.id,
          ...doc.data(),
        });
      }).toList();
    } catch (e) {
      _errorService.logError('Erreur lors de la récupération de tous les utilisateurs: $e', StackTrace.current);
      return [];
    }
  }

  // Stream pour écouter les changements d'un utilisateur
  Stream<UserModel?> getUserStream(String userId) {
    return _firestore
        .collection(_usersCollection)
        .doc(userId)
        .snapshots()
        .map((doc) {
      if (doc.exists) {
        return UserModel.fromJson({
          'id': doc.id,
          ...doc.data() as Map<String, dynamic>,
        });
      }
      return null;
    });
  }

  // Rechercher des utilisateurs par nom ou email
  Future<List<UserModel>> searchUsers(String query) async {
    try {
      if (query.isEmpty) return [];

      final queryLower = query.toLowerCase();

      // Recherche par email
      final emailResults = await _firestore
          .collection(_usersCollection)
          .where('email', isGreaterThanOrEqualTo: queryLower)
          .where('email', isLessThanOrEqualTo: '$queryLower\uf8ff')
          .limit(10)
          .get();

      // Recherche par prénom
      final prenomResults = await _firestore
          .collection(_usersCollection)
          .where('prenom', isGreaterThanOrEqualTo: queryLower)
          .where('prenom', isLessThanOrEqualTo: '$queryLower\uf8ff')
          .limit(10)
          .get();

      // Combiner et dédupliquer les résultats
      final Map<String, UserModel> usersMap = {};

      for (var doc in emailResults.docs) {
        usersMap[doc.id] = UserModel.fromJson({
          'id': doc.id,
          ...doc.data(),
        });
      }

      for (var doc in prenomResults.docs) {
        usersMap[doc.id] = UserModel.fromJson({
          'id': doc.id,
          ...doc.data(),
        });
      }

      return usersMap.values.toList();
    } catch (e) {
      _errorService.logError('Erreur lors de la recherche d\'utilisateurs: $e', StackTrace.current);
      return [];
    }
  }

  // Mettre à jour la dernière connexion de l'utilisateur
  Future<bool> updateLastLogin(String userId) async {
    try {
      await _firestore
          .collection(_usersCollection)
          .doc(userId)
          .update({
        'lastLogin': FieldValue.serverTimestamp(),
      });
      return true;
    } catch (e) {
      _errorService.logError('Erreur lors de la mise à jour de la dernière connexion: $e', StackTrace.current);
      return false;
    }
  }

  // Vérifier si l'utilisateur existe dans Firestore
  Future<bool> userExists(String userId) async {
    try {
      final doc = await _firestore
          .collection(_usersCollection)
          .doc(userId)
          .get();
      return doc.exists;
    } catch (e) {
      _errorService.logError('Erreur lors de la vérification de l\'existence de l\'utilisateur: $e', StackTrace.current);
      return false;
    }
  }
}
