import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../repositories/expense_repository.dart';
import '../../services/expense_service.dart';
import '../../services/error_service.dart';
import 'expense_event.dart';
import 'expense_state.dart';

/// BLoC for managing expense operations and state.
///
/// This BLoC handles expense-related operations including loading expenses,
/// creating new expenses, updating existing ones, deleting expenses, and
/// managing expense splits. It coordinates with the expense repository and
/// service to provide business logic and data persistence.
class ExpenseBloc extends Bloc<ExpenseEvent, ExpenseState> {
  /// Repository for expense data operations.
  final ExpenseRepository _expenseRepository;

  /// Service for expense business logic and validation.
  final ExpenseService _expenseService;

  /// Service for error handling and logging.
  final ErrorService _errorService;

  /// Subscription to the expenses stream for real-time updates.
  StreamSubscription? _expensesSubscription;

  /// Creates a new [ExpenseBloc] with required dependencies.
  ///
  /// [expenseRepository] is required for data operations.
  /// [expenseService] and [errorService] have default implementations if not provided.
  ExpenseBloc({
    required ExpenseRepository expenseRepository,
    ExpenseService? expenseService,
    ErrorService? errorService,
  }) : _expenseRepository = expenseRepository,
       _expenseService =
           expenseService ??
           ExpenseService(expenseRepository: expenseRepository),
       _errorService = errorService ?? ErrorService(),
       super(ExpenseInitial()) {
    on<LoadExpensesByGroup>(_onLoadExpensesByGroup);
    on<ExpensesUpdated>(_onExpensesUpdated);
    on<CreateExpense>(_onCreateExpense);
    on<UpdateExpense>(_onUpdateExpense);
    on<DeleteExpense>(_onDeleteExpense);
    on<MarkSplitAsPaid>(_onMarkSplitAsPaid);
    on<ArchiveExpense>(_onArchiveExpense);
  }

  /// Handles [LoadExpensesByGroup] events.
  ///
  /// Sets up a stream subscription to receive real-time updates for expenses
  /// in the specified group. Cancels any existing subscription before creating a new one.
  Future<void> _onLoadExpensesByGroup(
    LoadExpensesByGroup event,
    Emitter<ExpenseState> emit,
  ) async {
    try {
      // Emit empty state initially to avoid infinite spinner
      // The stream will update with actual data when available
      emit(const ExpensesLoaded(expenses: []));

      await _expensesSubscription?.cancel();

      _expensesSubscription = _expenseRepository
          .getExpensesStream(event.groupId)
          .listen(
            (expenses) => add(ExpensesUpdated(expenses)),
            onError: (error) =>
                add(ExpensesUpdated([], error: error.toString())),
          );
    } catch (e) {
      _errorService.logError('ExpenseBloc', 'Error loading expenses: $e');
      emit(const ExpenseError('Impossible de charger les dépenses'));
    }
  }

  /// Handles [ExpensesUpdated] events.
  ///
  /// Processes real-time updates from the expense stream, either emitting
  /// the updated expense list or an error state if the stream encountered an error.
  ///
  /// Args:
  ///   [event]: The ExpensesUpdated event containing expenses or error information
  ///   [emit]: State emitter function
  Future<void> _onExpensesUpdated(
    ExpensesUpdated event,
    Emitter<ExpenseState> emit,
  ) async {
    if (event.error != null) {
      emit(ExpenseError(event.error!));
    } else {
      emit(ExpensesLoaded(expenses: event.expenses));
    }
  }

  /// Handles [CreateExpense] events.
  ///
  /// Creates a new expense with validation and optional receipt image upload.
  /// Uses the expense service to handle business logic and validation,
  /// including currency conversion and split calculations.
  ///
  /// Args:
  ///   [event]: The CreateExpense event containing expense data and optional receipt
  ///   [emit]: State emitter function
  Future<void> _onCreateExpense(
    CreateExpense event,
    Emitter<ExpenseState> emit,
  ) async {
    try {
      await _expenseService.createExpenseWithValidation(
        event.expense,
        event.receiptImage,
      );
      emit(const ExpenseOperationSuccess('Expense created successfully'));
    } catch (e) {
      _errorService.logError('ExpenseBloc', 'Error creating expense: $e');
      emit(const ExpenseError('Impossible de créer la dépense'));
    }
  }

  /// Handles [UpdateExpense] events.
  ///
  /// Updates an existing expense with validation and optional new receipt image.
  /// Uses the expense service to handle business logic, validation, and
  /// recalculation of splits if expense details change.
  ///
  /// Args:
  ///   [event]: The UpdateExpense event containing updated expense data and optional new receipt
  ///   [emit]: State emitter function
  Future<void> _onUpdateExpense(
    UpdateExpense event,
    Emitter<ExpenseState> emit,
  ) async {
    try {
      await _expenseService.updateExpenseWithValidation(
        event.expense,
        event.newReceiptImage,
      );
      emit(const ExpenseOperationSuccess('Expense updated successfully'));
    } catch (e) {
      _errorService.logError('ExpenseBloc', 'Error updating expense: $e');
      emit(const ExpenseError('Impossible de mettre à jour la dépense'));
    }
  }

  /// Handles [DeleteExpense] events.
  ///
  /// Permanently deletes an expense from the database. This action
  /// cannot be undone and will affect group balance calculations.
  ///
  /// Args:
  ///   [event]: The DeleteExpense event containing the expense ID to delete
  ///   [emit]: State emitter function
  Future<void> _onDeleteExpense(
    DeleteExpense event,
    Emitter<ExpenseState> emit,
  ) async {
    try {
      await _expenseRepository.deleteExpense(event.expenseId);
      emit(const ExpenseOperationSuccess('Expense deleted successfully'));
    } catch (e) {
      _errorService.logError('ExpenseBloc', 'Error deleting expense: $e');
      emit(const ExpenseError('Impossible de supprimer la dépense'));
    }
  }

  /// Handles [MarkSplitAsPaid] events.
  ///
  /// Marks a user's portion of an expense split as paid, updating the
  /// expense's split information and affecting balance calculations.
  /// This helps track who has settled their portion of shared expenses.
  ///
  /// Args:
  ///   [event]: The MarkSplitAsPaid event containing expense ID and user ID
  ///   [emit]: State emitter function
  Future<void> _onMarkSplitAsPaid(
    MarkSplitAsPaid event,
    Emitter<ExpenseState> emit,
  ) async {
    try {
      await _expenseRepository.markSplitAsPaid(event.expenseId, event.userId);
      emit(const ExpenseOperationSuccess('Payment marked as completed'));
    } catch (e) {
      _errorService.logError('ExpenseBloc', 'Error marking split as paid: $e');
      emit(const ExpenseError('Impossible de marquer comme payé'));
    }
  }

  /// Handles [ArchiveExpense] events.
  ///
  /// Archives an expense, moving it out of the active expense list
  /// while preserving it for historical records and audit purposes.
  /// Archived expenses are not included in current balance calculations.
  ///
  /// Args:
  ///   [event]: The ArchiveExpense event containing the expense ID to archive
  ///   [emit]: State emitter function
  Future<void> _onArchiveExpense(
    ArchiveExpense event,
    Emitter<ExpenseState> emit,
  ) async {
    try {
      await _expenseRepository.archiveExpense(event.expenseId);
      emit(const ExpenseOperationSuccess('Expense archived successfully'));
    } catch (e) {
      _errorService.logError('ExpenseBloc', 'Error archiving expense: $e');
      emit(const ExpenseError('Impossible d\'archiver la dépense'));
    }
  }

  /// Cleans up resources when the bloc is closed.
  ///
  /// Cancels the expense stream subscription to prevent memory leaks
  /// and ensure proper disposal of resources.
  @override
  Future<void> close() {
    _expensesSubscription?.cancel();
    return super.close();
  }
}
