/// A BLoC (Business Logic Component) that manages group-related operations.
///
/// This bloc handles all group operations including creation, updates, member management,
/// and loading groups for users or trips. It provides real-time updates through streams
/// and manages the relationship between users, groups, and trips.
///
/// The bloc processes these main events:
/// - [LoadGroupsByUserId]: Loads all groups for a specific user with real-time updates
/// - [LoadGroupsByTrip]: Loads the group associated with a specific trip
/// - [CreateGroup]: Creates a new group without members
/// - [CreateGroupWithMembers]: Creates a new group with initial members
/// - [AddMemberToGroup]: Adds a member to an existing group
/// - [RemoveMemberFromGroup]: Removes a member from a group
/// - [UpdateGroup]: Updates group information
/// - [DeleteGroup]: Deletes a group
///
/// Dependencies:
/// - [GroupRepository]: Repository for group data operations
/// - [ErrorService]: Service for error logging and handling
///
/// Example usage:
/// ```dart
/// final groupBloc = GroupBloc(groupRepository);
///
/// // Load groups for a user
/// groupBloc.add(LoadGroupsByUserId('userId123'));
///
/// // Create a new group with members
/// groupBloc.add(CreateGroupWithMembers(
///   group: newGroup,
///   members: [member1, member2],
/// ));
/// ```
library;

import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:travel_mate/services/error_service.dart';
import 'group_event.dart';
import 'group_state.dart';
import '../../repositories/group_repository.dart';
import '../../models/group.dart';

/// BLoC that manages group-related operations and state.
class GroupBloc extends Bloc<GroupEvent, GroupState> {
  /// Repository for group data operations
  final GroupRepository _repository;

  /// Subscription to group stream for real-time updates
  StreamSubscription? _groupsSubscription;

  /// Service for error handling and logging
  final _errorService = ErrorService();

  /// Constructor for GroupBloc.
  ///
  /// Initializes the bloc with the group repository and sets up event handlers
  /// for all group-related operations.
  ///
  /// Args:
  ///   [_repository]: Repository for group data operations
  GroupBloc(this._repository) : super(GroupInitial()) {
    on<LoadGroupsByUserId>(_onLoadGroupsByUserId);
    on<_GroupsUpdated>(_onGroupsUpdated);
    on<LoadGroupsByTrip>(_onLoadGroupsByTrip);
    on<CreateGroup>(_onCreateGroup);
    on<CreateGroupWithMembers>(_onCreateGroupWithMembers);
    on<AddMemberToGroup>(_onAddMemberToGroup);
    on<RemoveMemberFromGroup>(_onRemoveMemberFromGroup);
    on<UpdateGroup>(_onUpdateGroup);
    on<DeleteGroup>(_onDeleteGroup);
  }

  /// Handles [LoadGroupsByUserId] events.
  ///
  /// Loads all groups for a specific user with real-time updates via stream subscription.
  /// Cancels any existing subscription before creating a new one to prevent memory leaks.
  ///
  /// Args:
  ///   [event]: The LoadGroupsByUserId event containing the user ID
  ///   [emit]: State emitter function
  Future<void> _onLoadGroupsByUserId(
    LoadGroupsByUserId event,
    Emitter<GroupState> emit,
  ) async {
    try {
      emit(GroupLoading());
      await _groupsSubscription?.cancel();
      _groupsSubscription = _repository
          .getGroupsByUserId(event.userId)
          .listen(
            (groups) {
              add(_GroupsUpdated(groups));
            },
            onError: (error) {
              add(_GroupsUpdated([], error: error.toString()));
            },
          );
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error loading groups: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible de charger les groupes'));
    }
  }

  /// Handles [_GroupsUpdated] events.
  ///
  /// Processes real-time updates from the group stream, either emitting
  /// the updated group list or an error state if the stream encountered an error.
  ///
  /// Args:
  ///   [event]: The _GroupsUpdated event containing groups or error information
  ///   [emit]: State emitter function
  Future<void> _onGroupsUpdated(
    _GroupsUpdated event,
    Emitter<GroupState> emit,
  ) async {
    if (event.error != null) {
      _errorService.logError(event.error!, StackTrace.current);
      emit(GroupError(event.error!));
    } else {
      emit(GroupsLoaded(event.groups));
    }
  }

  /// Handles [LoadGroupsByTrip] events.
  ///
  /// Loads the group associated with a specific trip. Since each trip typically
  /// has one primary group, this returns a single group or an empty list.
  ///
  /// Args:
  ///   [event]: The LoadGroupsByTrip event containing the trip ID
  ///   [emit]: State emitter function
  Future<void> _onLoadGroupsByTrip(
    LoadGroupsByTrip event,
    Emitter<GroupState> emit,
  ) async {
    try {
      emit(GroupLoading());
      final group = await _repository.getGroupByTripId(event.tripId);
      if (group != null) {
        emit(GroupsLoaded([group]));
      } else {
        emit(const GroupsLoaded([]));
      }
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error loading group by trip: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible de charger le groupe du voyage'));
    }
  }

  /// Handles [CreateGroup] events.
  ///
  /// Creates a new group without any initial members. The group creator
  /// can add members later using AddMemberToGroup events.
  ///
  /// Args:
  ///   [event]: The CreateGroup event containing the group data
  ///   [emit]: State emitter function
  Future<void> _onCreateGroup(
    CreateGroup event,
    Emitter<GroupState> emit,
  ) async {
    try {
      emit(GroupLoading());
      final groupId = await _repository.createGroupWithMembers(
        group: event.group,
        members: [],
      );
      emit(GroupCreated(groupId: groupId));
      emit(const GroupOperationSuccess('Group created successfully'));
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error creating group: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible de créer le groupe'));
    }
  }

  /// Handles [CreateGroupWithMembers] events.
  ///
  /// Creates a new group with an initial set of members. This is useful
  /// for setting up complete groups in one operation, such as when
  /// planning a trip with known participants.
  ///
  /// Args:
  ///   [event]: The CreateGroupWithMembers event containing group data and member list
  ///   [emit]: State emitter function
  Future<void> _onCreateGroupWithMembers(
    CreateGroupWithMembers event,
    Emitter<GroupState> emit,
  ) async {
    try {
      emit(GroupLoading());
      final groupId = await _repository.createGroupWithMembers(
        group: event.group,
        members: event.members,
      );
      emit(GroupCreated(groupId: groupId));
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error creating group with members: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible de créer le groupe'));
    }
  }

  /// Handles [AddMemberToGroup] events.
  ///
  /// Adds a new member to an existing group. The member will be able to
  /// participate in group expenses and access group features.
  ///
  /// Args:
  ///   [event]: The AddMemberToGroup event containing group ID and member data
  ///   [emit]: State emitter function
  Future<void> _onAddMemberToGroup(
    AddMemberToGroup event,
    Emitter<GroupState> emit,
  ) async {
    try {
      await _repository.addMember(event.groupId, event.member);
      emit(const GroupOperationSuccess('Member added'));
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error adding member: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible d\'ajouter le membre'));
    }
  }

  /// Handles [RemoveMemberFromGroup] events.
  ///
  /// Removes a member from a group. This will affect expense calculations
  /// and the member will no longer have access to group features.
  ///
  /// Args:
  ///   [event]: The RemoveMemberFromGroup event containing group ID and user ID
  ///   [emit]: State emitter function
  Future<void> _onRemoveMemberFromGroup(
    RemoveMemberFromGroup event,
    Emitter<GroupState> emit,
  ) async {
    try {
      await _repository.removeMember(event.groupId, event.userId);
      emit(const GroupOperationSuccess('Member removed'));
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error removing member: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible de supprimer le membre'));
    }
  }

  /// Handles [UpdateGroup] events.
  ///
  /// Updates group information such as name, description, or settings.
  /// Member lists are managed through separate add/remove member events.
  ///
  /// Args:
  ///   [event]: The UpdateGroup event containing group ID and updated group data
  ///   [emit]: State emitter function
  Future<void> _onUpdateGroup(
    UpdateGroup event,
    Emitter<GroupState> emit,
  ) async {
    try {
      await _repository.updateGroup(event.groupId, event.group);
      emit(const GroupOperationSuccess('Group updated'));
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error updating group: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible de mettre à jour le groupe'));
    }
  }

  /// Handles [DeleteGroup] events.
  ///
  /// Permanently deletes a group and all associated data. This action
  /// cannot be undone and will affect all group members and expenses.
  ///
  /// Args:
  ///   [event]: The DeleteGroup event containing the trip ID to delete
  ///   [emit]: State emitter function
  Future<void> _onDeleteGroup(
    DeleteGroup event,
    Emitter<GroupState> emit,
  ) async {
    try {
      await _repository.deleteGroup(event.tripId);
      emit(const GroupOperationSuccess('Group deleted'));
    } catch (e, stackTrace) {
      _errorService.logError(
        'GroupBloc',
        'Error deleting group: $e',
        stackTrace,
      );
      emit(const GroupError('Impossible de supprimer le groupe'));
    }
  }

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

/// Private event for handling real-time group updates from streams.
///
/// This internal event is used to process updates from the group stream
/// subscription and emit appropriate states based on the received data.
class _GroupsUpdated extends GroupEvent {
  /// List of groups received from the stream
  final List<Group> groups;

  /// Error message if the stream encountered an error
  final String? error;

  /// Creates a _GroupsUpdated event.
  ///
  /// Args:
  ///   [groups]: List of groups from the stream update
  ///   [error]: Optional error message if stream failed
  const _GroupsUpdated(this.groups, {this.error});

  @override
  List<Object?> get props => [groups, error];
}
