import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

import 'package:travel_mate/repositories/group_repository.dart';
import 'package:travel_mate/services/notification_service.dart';
import 'package:travel_mate/services/logger_service.dart';
import 'package:travel_mate/services/error_service.dart';
import 'user_event.dart' as event;
import 'user_state.dart' as state;

/// BLoC for managing user data and operations.
///
/// This BLoC handles user-related operations including loading user data,
/// updating user information, and managing user state throughout the application.
/// It coordinates with Firebase Auth and Firestore to manage user data persistence.
class UserBloc extends Bloc<event.UserEvent, state.UserState> {
  /// Firebase Auth instance for authentication operations.
  final FirebaseAuth _auth = FirebaseAuth.instance;

  /// Firestore instance for user data operations.
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  final GroupRepository _groupRepository = GroupRepository();

  final _errorService = ErrorService();

  /// Creates a new [UserBloc] with initial state.
  ///
  /// Registers event handlers for all user-related events.
  UserBloc() : super(state.UserInitial()) {
    on<event.UserInitialized>(_onUserInitialized);
    on<event.LoadUser>(_onLoadUser);
    on<event.UserUpdated>(_onUserUpdated);
    on<event.UserLoggedOut>(_onUserLoggedOut);
  }

  /// Handles [UserInitialized] events.
  ///
  /// Initializes the current authenticated user's data by fetching it from Firestore.
  /// If the user doesn't exist in Firestore, creates a default user document.
  /// This is typically called when the app starts or after successful authentication.
  Future<void> _onUserInitialized(
    event.UserInitialized event,
    Emitter<state.UserState> emit,
  ) async {
    emit(state.UserLoading());

    try {
      final currentUser = _auth.currentUser;

      if (currentUser == null) {
        emit(state.UserError('No user currently authenticated'));
        return;
      }

      // Initialize notifications and update token
      final notificationService = NotificationService();
      await notificationService.initialize();
      final fcmToken = await notificationService.getFCMToken();
      LoggerService.info('UserBloc - FCM Token retrieved: $fcmToken');

      // Fetch user data from Firestore
      final userDoc = await _firestore
          .collection('users')
          .doc(currentUser.uid)
          .get();

      if (!userDoc.exists) {
        // Create a default user if it doesn't exist
        final defaultUser = state.UserModel(
          id: currentUser.uid,
          email: currentUser.email ?? '',
          prenom: currentUser.displayName ?? 'Voyageur',
          fcmToken: fcmToken,
        );

        await _firestore
            .collection('users')
            .doc(currentUser.uid)
            .set(defaultUser.toJson());

        emit(state.UserLoaded(defaultUser));
      } else {
        final user = state.UserModel.fromJson({
          'id': currentUser.uid,
          ...userDoc.data()!,
        });

        // Update FCM token if it changed
        if (fcmToken != null && user.fcmToken != fcmToken) {
          LoggerService.info('UserBloc - Updating FCM token in Firestore');
          await _firestore.collection('users').doc(currentUser.uid).set({
            'fcmToken': fcmToken,
          }, SetOptions(merge: true));
          LoggerService.info('UserBloc - FCM token updated');
        } else {
          LoggerService.info(
            'UserBloc - FCM token not updated. Local: $fcmToken, Firestore: ${user.fcmToken}',
          );
        }

        emit(state.UserLoaded(user));
      }
    } catch (e, stackTrace) {
      _errorService.logError('UserBloc', 'Error loading user: $e', stackTrace);
      emit(state.UserError('Impossible de charger l\'utilisateur'));
    }
  }

  /// Handles [LoadUser] events.
  ///
  /// Loads a specific user's data from Firestore by their user ID.
  /// This is useful when you need to display information about other users.
  Future<void> _onLoadUser(
    event.LoadUser event,
    Emitter<state.UserState> emit,
  ) async {
    emit(state.UserLoading());

    try {
      final userDoc = await _firestore
          .collection('users')
          .doc(event.userId)
          .get();

      if (userDoc.exists) {
        final user = state.UserModel.fromJson({
          'id': event.userId,
          ...userDoc.data()!,
        });
        emit(state.UserLoaded(user));
      } else {
        emit(state.UserError('User not found'));
      }
    } catch (e, stackTrace) {
      _errorService.logError('UserBloc', 'Error loading user: $e', stackTrace);
      emit(state.UserError('Impossible de charger l\'utilisateur'));
    }
  }

  /// Handles [UserUpdated] events.
  ///
  /// Updates the current user's data in Firestore with the provided information.
  /// Only updates the fields specified in the userData map, allowing for partial updates.
  /// After successful update, reloads the user data to reflect changes.
  Future<void> _onUserUpdated(
    event.UserUpdated event,
    Emitter<state.UserState> emit,
  ) async {
    if (this.state is state.UserLoaded) {
      final currentUser = (this.state as state.UserLoaded).user;

      try {
        await _firestore
            .collection('users')
            .doc(currentUser.id)
            .update(event.userData);

        final updatedDoc = await _firestore
            .collection('users')
            .doc(currentUser.id)
            .get();

        final updatedUser = state.UserModel.fromJson({
          'id': currentUser.id,
          ...updatedDoc.data()!,
        });

        emit(state.UserLoaded(updatedUser));

        // Propager les changements aux groupes
        await _groupRepository.updateMemberDetails(
          userId: currentUser.id,
          firstName: event
              .userData['prenom'], // 'prenom' dans Firestore map map to firstName usually? Wait, UserModel has prenom/nom.
          lastName: event.userData['nom'],
          profilePictureUrl: event
              .userData['profilePictureUrl'], // Key was 'profilePictureUrl' in ProfileContent
        );
      } catch (e, stackTrace) {
        _errorService.logError(
          'UserBloc',
          'Error updating user: $e',
          stackTrace,
        );
        emit(state.UserError('Impossible de mettre à jour l\'utilisateur'));
      }
    }
  }

  /// Handles [UserLoggedOut] events.
  ///
  /// Resets the user bloc state to initial when the user logs out.
  /// This clears any cached user data from the application state.
  Future<void> _onUserLoggedOut(
    event.UserLoggedOut event,
    Emitter<state.UserState> emit,
  ) async {
    emit(state.UserInitial());
  }
}
