/// A BLoC (Business Logic Component) that manages trip-related operations.
///
/// This bloc handles all trip operations including creation, updates, deletion,
/// and loading trips for users. It provides real-time updates through streams
/// and manages the trip lifecycle with proper state transitions.
///
/// The bloc processes these main events:
/// - [LoadTripsByUserId]: Loads all trips for a specific user with real-time updates
/// - [TripCreateRequested]: Creates a new trip and reloads the user's trip list
/// - [TripUpdateRequested]: Updates an existing trip and refreshes the list
/// - [TripDeleteRequested]: Deletes a trip and refreshes the list
/// - [ResetTrips]: Resets the trip state and cancels subscriptions
///
/// Dependencies:
/// - [TripRepository]: Repository for trip data operations
///
/// State Management:
/// The bloc maintains the current user ID to enable automatic list refreshing
/// after operations like create, update, or delete. This ensures the UI stays
/// in sync with the latest data.
///
/// Example usage:
/// ```dart
/// final tripBloc = TripBloc(tripRepository);
///
/// // Load trips for a user
/// tripBloc.add(LoadTripsByUserId(userId: 'userId123'));
///
/// // Create a new trip
/// tripBloc.add(TripCreateRequested(trip: newTrip));
///
/// // Update a trip
/// tripBloc.add(TripUpdateRequested(trip: updatedTrip));
///
/// // Delete a trip
/// tripBloc.add(TripDeleteRequested(tripId: 'tripId456'));
/// ```
library;

import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:travel_mate/models/trip.dart';
import 'trip_event.dart';
import 'trip_state.dart';
import '../../repositories/trip_repository.dart';
import '../../services/error_service.dart';

/// BLoC that manages trip-related operations and state.
class TripBloc extends Bloc<TripEvent, TripState> {
  /// Repository for trip data operations
  final TripRepository _repository;
  final _errorService = ErrorService();

  /// Subscription to trip stream for real-time updates
  StreamSubscription? _tripsSubscription;

  /// Current user ID for automatic list refreshing after operations
  String? _currentUserId;

  /// Constructor for TripBloc.
  ///
  /// Initializes the bloc with the trip repository and sets up event handlers
  /// for all trip-related operations.
  ///
  /// Args:
  ///   [_repository]: Repository for trip data operations
  TripBloc(this._repository) : super(TripInitial()) {
    on<LoadTripsByUserId>(_onLoadTripsByUserId);
    on<TripCreateRequested>(_onTripCreateRequested);
    on<TripUpdateRequested>(_onTripUpdateRequested);
    on<TripDeleteRequested>(_onTripDeleteRequested);
    on<_TripsUpdated>(_onTripsUpdated);
    on<ResetTrips>(_onResetTrips);
  }

  /// Handles [LoadTripsByUserId] events.
  ///
  /// Loads all trips for a specific user with real-time updates via stream subscription.
  /// Stores the user ID for future automatic refreshing after operations and cancels
  /// any existing subscription to prevent memory leaks.
  ///
  /// Args:
  ///   [event]: The LoadTripsByUserId event containing the user ID
  ///   [emit]: State emitter function
  Future<void> _onLoadTripsByUserId(
    LoadTripsByUserId event,
    Emitter<TripState> emit,
  ) async {
    emit(TripLoading());

    _currentUserId = event.userId;
    await _tripsSubscription?.cancel();

    _tripsSubscription = _repository
        .getTripsByUserId(event.userId)
        .listen(
          (trips) {
            add(_TripsUpdated(trips));
          },
          onError: (error, stackTrace) {
            _errorService.logError(
              'TripBloc',
              'Error loading trips: $error',
              stackTrace,
            );
            add(
              const _TripsUpdated(
                [],
                error: 'Impossible de charger les voyages',
              ),
            );
          },
        );
  }

  /// Handles [_TripsUpdated] events.
  ///
  /// Processes real-time updates from the trip stream and emits the
  /// updated trip list to the UI.
  ///
  /// Args:
  ///   [event]: The _TripsUpdated event containing the updated trip list
  ///   [emit]: State emitter function
  void _onTripsUpdated(_TripsUpdated event, Emitter<TripState> emit) {
    if (event.error != null) {
      emit(TripError(event.error!));
    } else {
      emit(TripLoaded(event.trips));
    }
  }

  /// Handles [TripCreateRequested] events.
  ///
  /// Creates a new trip and automatically refreshes the user's trip list
  /// to show the newly created trip. Includes a delay to allow the creation
  /// to complete before refreshing.
  ///
  /// Args:
  ///   [event]: The TripCreateRequested event containing the trip data
  ///   [emit]: State emitter function
  Future<void> _onTripCreateRequested(
    TripCreateRequested event,
    Emitter<TripState> emit,
  ) async {
    try {
      emit(TripLoading());

      final tripId = await _repository.createTrip(event.trip);

      emit(TripCreated(tripId: tripId));

      await Future.delayed(const Duration(milliseconds: 800));
      if (_currentUserId != null) {
        add(LoadTripsByUserId(userId: _currentUserId!));
      }
    } catch (e, stackTrace) {
      _errorService.logError('TripBloc', 'Error creating trip: $e', stackTrace);
      emit(const TripError('Impossible de créer le voyage'));
    }
  }

  /// Handles [TripUpdateRequested] events.
  ///
  /// Updates an existing trip and automatically refreshes the user's trip list
  /// to show the updated information. Includes a delay to allow the update
  /// to complete before refreshing.
  ///
  /// Args:
  ///   [event]: The TripUpdateRequested event containing the updated trip data
  ///   [emit]: State emitter function
  Future<void> _onTripUpdateRequested(
    TripUpdateRequested event,
    Emitter<TripState> emit,
  ) async {
    try {
      await _repository.updateTrip(event.trip.id!, event.trip);
      emit(const TripOperationSuccess('Trip updated successfully'));
      await Future.delayed(const Duration(milliseconds: 500));
      if (_currentUserId != null) {
        add(LoadTripsByUserId(userId: _currentUserId!));
      }
    } catch (e, stackTrace) {
      _errorService.logError('TripBloc', 'Error updating trip: $e', stackTrace);
      emit(const TripError('Impossible de mettre à jour le voyage'));
    }
  }

  /// Handles [TripDeleteRequested] events.
  ///
  /// Deletes a trip and automatically refreshes the user's trip list
  /// to remove the deleted trip from the UI. Includes a delay to allow
  /// the deletion to complete before refreshing.
  ///
  /// Args:
  ///   [event]: The TripDeleteRequested event containing the trip ID to delete
  ///   [emit]: State emitter function
  Future<void> _onTripDeleteRequested(
    TripDeleteRequested event,
    Emitter<TripState> emit,
  ) async {
    try {
      await _repository.deleteTrip(event.tripId);

      emit(const TripOperationSuccess('Trip deleted successfully'));

      await Future.delayed(const Duration(milliseconds: 500));
      if (_currentUserId != null) {
        add(LoadTripsByUserId(userId: _currentUserId!));
      }
    } catch (e, stackTrace) {
      _errorService.logError('TripBloc', 'Error deleting trip: $e', stackTrace);
      emit(const TripError('Impossible de supprimer le voyage'));
    }
  }

  /// Handles [ResetTrips] events.
  ///
  /// Resets the trip state to initial and cleans up resources.
  /// Cancels the trip stream subscription and clears the current user ID.
  /// This is useful for user logout or when switching contexts.
  ///
  /// Args:
  ///   [event]: The ResetTrips event
  ///   [emit]: State emitter function
  Future<void> _onResetTrips(ResetTrips event, Emitter<TripState> emit) async {
    await _tripsSubscription?.cancel();
    _currentUserId = null;
    emit(TripInitial());
  }

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

/// Private event for handling real-time trip updates from streams.
///
/// This internal event is used to process updates from the trip stream
/// subscription and emit appropriate states based on the received data.
/// internal event
class _TripsUpdated extends TripEvent {
  final List<Trip> trips;
  final String? error;
  const _TripsUpdated(this.trips, {this.error});
  @override
  List<Object?> get props => [trips, error];
}
