/// Business Logic Component for managing authentication state.
///
/// The [AuthBloc] handles authentication-related events and manages the
/// authentication state throughout the application. It coordinates with
/// the [AuthRepository] to perform authentication operations and emits
/// appropriate states based on the results.
///
/// Supported authentication methods:
/// - Email and password authentication
/// - Google Sign-In
/// - Apple Sign-In
/// - Password reset functionality
///
/// This bloc handles the following events:
/// - [AuthCheckRequested]: Verifies current authentication status
/// - [AuthSignInRequested]: Processes email/password sign-in
/// - [AuthSignUpRequested]: Processes user registration
/// - [AuthGoogleSignInRequested]: Processes Google authentication
/// - [AuthAppleSignInRequested]: Processes Apple authentication
/// - [AuthSignOutRequested]: Processes user sign-out
/// - [AuthPasswordResetRequested]: Processes password reset requests
library;

import 'package:flutter_bloc/flutter_bloc.dart';
import '../../repositories/auth_repository.dart';
import 'auth_event.dart';
import 'auth_state.dart';
import '../../services/notification_service.dart';
import '../../services/analytics_service.dart';

/// BLoC for managing authentication state and operations.
class AuthBloc extends Bloc<AuthEvent, AuthState> {
  /// Repository for authentication operations.
  final AuthRepository _authRepository;
  final NotificationService _notificationService;
  final AnalyticsService _analyticsService;

  /// Creates an [AuthBloc] with the provided [authRepository].
  ///
  /// The bloc starts in the [AuthInitial] state and registers event handlers
  /// for all supported authentication events.
  AuthBloc({
    required AuthRepository authRepository,
    NotificationService? notificationService,
    AnalyticsService? analyticsService,
  }) : _authRepository = authRepository,
       _notificationService = notificationService ?? NotificationService(),
       _analyticsService = analyticsService ?? AnalyticsService(),
       super(AuthInitial()) {
    on<AuthCheckRequested>(_onAuthCheckRequested);
    on<AuthSignInRequested>(_onSignInRequested);
    on<AuthSignUpRequested>(_onSignUpRequested);
    on<AuthGoogleSignInRequested>(_onGoogleSignInRequested);
    on<AuthGoogleSignUpRequested>(_onGoogleSignUpRequested);
    on<AuthAppleSignInRequested>(_onAppleSignInRequested);
    on<AuthAppleSignUpRequested>(_onAppleSignUpRequested);
    on<AuthSignOutRequested>(_onSignOutRequested);
    on<AuthPasswordResetRequested>(_onPasswordResetRequested);
  }

  /// Handles [AuthCheckRequested] events.
  ///
  /// Checks if a user is currently authenticated and emits the appropriate state.
  /// If a user is found, attempts to fetch user data from Firestore.
  Future<void> _onAuthCheckRequested(
    AuthCheckRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());

    try {
      final currentUser = _authRepository.currentUser;

      if (currentUser != null) {
        // Fetch user data from Firestore
        final user = await _authRepository.getUserFromFirestore(
          currentUser.uid,
        );

        if (user != null) {
          // Save FCM Token on auto-login
          await _notificationService.saveTokenToFirestore(user.id!);
          await _analyticsService.setUserId(user.id);
          emit(AuthAuthenticated(user: user));
        } else {
          emit(AuthUnauthenticated());
        }
      } else {
        emit(AuthUnauthenticated());
      }
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }

  /// Handles [AuthSignInRequested] events.
  ///
  /// Attempts to sign in a user with the provided email and password.
  /// Emits [AuthAuthenticated] on success or [AuthError] on failure.
  Future<void> _onSignInRequested(
    AuthSignInRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());

    try {
      final user = await _authRepository.signInWithEmailAndPassword(
        email: event.email,
        password: event.password,
      );

      if (user != null) {
        // Save FCM Token
        await NotificationService().saveTokenToFirestore(user.id!);
        await _analyticsService.setUserId(user.id);
        await _analyticsService.logEvent(
          name: 'login',
          parameters: {'method': 'email'},
        );
        emit(AuthAuthenticated(user: user));
      } else {
        emit(const AuthError(message: 'Invalid email or password'));
      }
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }

  /// Handles [AuthSignUpRequested] events.
  ///
  /// Attempts to create a new user account with the provided information.
  /// Emits [AuthAuthenticated] on success or [AuthError] on failure.
  Future<void> _onSignUpRequested(
    AuthSignUpRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());

    try {
      final user = await _authRepository.signUpWithEmailAndPassword(
        email: event.email,
        password: event.password,
        nom: event.nom,
        prenom: event.prenom,
        phoneNumber: event.phoneNumber,
      );

      if (user != null) {
        // Save FCM Token
        await NotificationService().saveTokenToFirestore(user.id!);
        await _analyticsService.setUserId(user.id);
        await _analyticsService.logEvent(
          name: 'sign_up',
          parameters: {'method': 'email'},
        );
        emit(AuthAuthenticated(user: user));
      } else {
        emit(const AuthError(message: 'Failed to create account'));
      }
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }

  /// Handles [AuthGoogleSignInRequested] events.
  ///
  /// Attempts to sign in the user using Google authentication.
  /// Emits [AuthAuthenticated] on success or [AuthError] on failure.
  Future<void> _onGoogleSignInRequested(
    AuthGoogleSignInRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());

    try {
      final user = await _authRepository.signInWithGoogle();

      if (user != null) {
        // Save FCM Token
        await NotificationService().saveTokenToFirestore(user.id!);
        await _analyticsService.setUserId(user.id);
        await _analyticsService.logEvent(
          name: 'login',
          parameters: {'method': 'google'},
        );
        emit(AuthAuthenticated(user: user));
      } else {
        emit(
          const AuthError(
            message:
                'Utilisateur n\'est pas encore inscrit avec Google, veuillez créer un compte avec Google',
          ),
        );
      }
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }

  Future<void> _onGoogleSignUpRequested(
    AuthGoogleSignUpRequested event,
    Emitter<AuthState> emit,
  ) async {
    // This method can be implemented if needed for Google sign-up specific logic.
    emit(AuthLoading());
    try {
      final user = await _authRepository.signUpWithGoogle(
        event.phoneNumber,
        event.name,
        event.firstname,
      );

      if (user != null) {
        await _analyticsService.setUserId(user.id);
        await _analyticsService.logEvent(
          name: 'sign_up',
          parameters: {'method': 'google'},
        );
        emit(AuthAuthenticated(user: user));
      } else {
        emit(const AuthError(message: 'Failed to create account with Google'));
      }
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }

  Future<void> _onAppleSignUpRequested(
    AuthAppleSignUpRequested event,
    Emitter<AuthState> emit,
  ) async {
    // This method can be implemented if needed for Apple sign-up specific logic.
    emit(AuthLoading());
    try {
      final user = await _authRepository.signUpWithApple(
        event.phoneNumber,
        event.name,
        event.firstname,
      );

      if (user != null) {
        await _analyticsService.setUserId(user.id);
        await _analyticsService.logEvent(
          name: 'sign_up',
          parameters: {'method': 'apple'},
        );
        emit(AuthAuthenticated(user: user));
      } else {
        emit(const AuthError(message: 'Failed to create account with Apple'));
      }
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }

  /// Handles [AuthAppleSignInRequested] events.
  ///
  /// Attempts to sign in the user using Apple authentication.
  /// Emits [AuthAuthenticated] on success or [AuthError] on failure.
  Future<void> _onAppleSignInRequested(
    AuthAppleSignInRequested event,
    Emitter<AuthState> emit,
  ) async {
    emit(AuthLoading());

    try {
      final user = await _authRepository.signInWithApple();

      if (user != null) {
        // Save FCM Token
        await NotificationService().saveTokenToFirestore(user.id!);
        await _analyticsService.setUserId(user.id);
        await _analyticsService.logEvent(
          name: 'login',
          parameters: {'method': 'apple'},
        );
        emit(AuthAuthenticated(user: user));
      } else {
        emit(
          const AuthError(
            message:
                'Utilisateur n\'est pas encore inscrit avec Apple, veuillez créer un compte avec Apple',
          ),
        );
      }
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }

  /// Handles [AuthSignOutRequested] events.
  ///
  /// Signs out the current user and emits [AuthUnauthenticated].
  Future<void> _onSignOutRequested(
    AuthSignOutRequested event,
    Emitter<AuthState> emit,
  ) async {
    await _authRepository.signOut();
    await _analyticsService.setUserId(null); // Clear user ID
    emit(AuthUnauthenticated());
  }

  /// Handles [AuthPasswordResetRequested] events.
  ///
  /// Sends a password reset email to the specified email address.
  /// Emits [AuthPasswordResetSent] on success or [AuthError] on failure.
  Future<void> _onPasswordResetRequested(
    AuthPasswordResetRequested event,
    Emitter<AuthState> emit,
  ) async {
    try {
      await _authRepository.resetPassword(event.email);
      emit(AuthPasswordResetSent(email: event.email));
    } catch (e) {
      emit(AuthError(message: e.toString().replaceAll('Exception: ', '')));
    }
  }
}
