/// A BLoC (Business Logic Component) that manages balance-related operations for groups.
///
/// This bloc handles the calculation and management of user balances within groups,
/// including optimal settlement calculations and marking settlements as completed.
///
/// The bloc processes three main events:
/// - [LoadGroupBalances]: Loads and calculates all user balances for a specific group
/// - [RefreshBalance]: Refreshes the balance data while maintaining the current state when possible
/// - [MarkSettlementAsCompleted]: Records a settlement transaction between users
///
/// Dependencies:
/// - [BalanceRepository]: Repository for balance data operations
/// - [BalanceService]: Service for balance calculations and settlement logic
/// - [ErrorService]: Service for error logging and handling
///
/// Example usage:
/// ```dart
/// final balanceBloc = BalanceBloc(
///   balanceRepository: balanceRepository,
///   expenseRepository: expenseRepository,
/// );
///
/// // Load balances for a group
/// balanceBloc.add(LoadGroupBalances('groupId123'));
///
/// // Mark a settlement as completed
/// balanceBloc.add(MarkSettlementAsCompleted(
///   groupId: 'groupId123',
///   fromUserId: 'user1',
///   toUserId: 'user2',
///   amount: 50.0,
/// ));
/// ```
library;

import 'package:flutter_bloc/flutter_bloc.dart';
import '../../repositories/balance_repository.dart';
import '../../repositories/expense_repository.dart';
import '../../services/balance_service.dart';
import '../../services/error_service.dart';
import 'balance_event.dart';
import 'balance_state.dart';

class BalanceBloc extends Bloc<BalanceEvent, BalanceState> {
  final BalanceRepository _balanceRepository;
  final BalanceService _balanceService;
  final ErrorService _errorService;

  /// Constructor for BalanceBloc.
  ///
  /// Initializes the bloc with required repositories and optional services.
  /// Sets up event handlers for balance-related operations.
  ///
  /// Args:
  ///   [balanceRepository]: Repository for balance data operations
  ///   [expenseRepository]: Repository for expense data operations
  ///   [balanceService]: Optional service for balance calculations (auto-created if null)
  ///   [errorService]: Optional service for error handling (auto-created if null)
  BalanceBloc({
    required BalanceRepository balanceRepository,
    required ExpenseRepository expenseRepository,
    BalanceService? balanceService,
    ErrorService? errorService,
  }) : _balanceRepository = balanceRepository,
       _balanceService =
           balanceService ??
           BalanceService(
             balanceRepository: balanceRepository,
             expenseRepository: expenseRepository,
           ),
       _errorService = errorService ?? ErrorService(),
       super(BalanceInitial()) {
    on<LoadGroupBalances>(_onLoadGroupBalance);
    on<RefreshBalance>(_onRefreshBalance);
    on<MarkSettlementAsCompleted>(_onMarkSettlementAsCompleted);
  }

  /// Handles [LoadGroupBalances] events.
  ///
  /// Loads and calculates user balances for a specific group along with
  /// optimal settlement recommendations. This provides a complete overview
  /// of who owes money to whom and the most efficient payment strategy.
  ///
  /// Args:
  ///   [event]: The LoadGroupBalances event containing the group ID
  ///   [emit]: State emitter function
  Future<void> _onLoadGroupBalance(
    LoadGroupBalances event,
    Emitter<BalanceState> emit,
  ) async {
    try {
      // Emit empty state initially to avoid infinite spinner
      emit(const GroupBalancesLoaded(balances: [], settlements: []));

      // Calculate group user balances
      final userBalances = await _balanceRepository.calculateGroupUserBalances(
        event.groupId,
      );

      // Calculate optimal settlements
      final settlements = await _balanceService.calculateOptimalSettlements(
        event.groupId,
      );

      emit(
        GroupBalancesLoaded(balances: userBalances, settlements: settlements),
      );
    } catch (e, stackTrace) {
      _errorService.logError(
        'BalanceBloc',
        'Error loading balance: $e',
        stackTrace,
      );
      emit(const BalanceError('Impossible de charger la balance'));
    }
  }

  /// Handles [RefreshBalance] events.
  ///
  /// Refreshes the balance data for a group while trying to maintain the current
  /// state when possible to provide a smoother user experience. Only shows loading
  /// state if there's no existing balance data.
  ///
  /// Args:
  ///   [event]: The RefreshBalance event containing the group ID
  ///   [emit]: State emitter function
  Future<void> _onRefreshBalance(
    RefreshBalance event,
    Emitter<BalanceState> emit,
  ) async {
    try {
      // Keep current state during refresh if possible
      if (state is! GroupBalancesLoaded) {
        emit(BalanceLoading());
      }

      // Calculate group user balances
      final userBalances = await _balanceRepository.calculateGroupUserBalances(
        event.groupId,
      );

      // Calculate optimal settlements
      final settlements = await _balanceService.calculateOptimalSettlements(
        event.groupId,
      );

      emit(
        GroupBalancesLoaded(balances: userBalances, settlements: settlements),
      );
    } catch (e, stackTrace) {
      _errorService.logError(
        'BalanceBloc',
        'Error refreshing balance: $e',
        stackTrace,
      );
      emit(const BalanceError('Impossible de rafraîchir la balance'));
    }
  }

  /// Handles [MarkSettlementAsCompleted] events.
  ///
  /// Records a settlement transaction between two users, marking that
  /// a debt has been paid. This updates the balance calculations and
  /// automatically refreshes the group balance data to reflect the change.
  ///
  /// Args:
  ///   [event]: The MarkSettlementAsCompleted event containing settlement details
  ///   [emit]: State emitter function
  Future<void> _onMarkSettlementAsCompleted(
    MarkSettlementAsCompleted event,
    Emitter<BalanceState> emit,
  ) async {
    try {
      await _balanceService.markSettlementAsCompleted(
        groupId: event.groupId,
        fromUserId: event.fromUserId,
        toUserId: event.toUserId,
        amount: event.amount,
      );

      emit(const BalanceOperationSuccess('Settlement marked as completed'));

      // Reload balance after settlement
      add(RefreshBalance(event.groupId));
    } catch (e, stackTrace) {
      _errorService.logError(
        'BalanceBloc',
        'Error marking settlement: $e',
        stackTrace,
      );
      emit(
        const BalanceError('Impossible de marquer le règlement comme terminé'),
      );
    }
  }
}
