/// A BLoC (Business Logic Component) that manages message-related operations for group chats.
///
/// This bloc handles all messaging functionality including sending, updating, deleting messages,
/// and managing reactions. It provides real-time updates through streams and manages the
/// relationship between users and group conversations.
///
/// The bloc processes these main events:
/// - [LoadMessages]: Loads messages for a group with real-time updates
/// - [SendMessage]: Sends a new message to a group chat
/// - [DeleteMessage]: Deletes a message from the chat
/// - [UpdateMessage]: Updates/edits an existing message
/// - [ReactToMessage]: Adds an emoji reaction to a message
/// - [RemoveReaction]: Removes a user's reaction from a message
///
/// Dependencies:
/// - [MessageService]: Service for message operations and business logic
/// - [MessageRepository]: Repository for message data operations
///
/// Example usage:
/// ```dart
/// final messageBloc = MessageBloc();
///
/// // Load messages for a group
/// messageBloc.add(LoadMessages('groupId123'));
///
/// // Send a message
/// messageBloc.add(SendMessage(
///   groupId: 'groupId123',
///   text: 'Hello everyone!',
///   senderId: 'userId123',
///   senderName: 'John Doe',
/// ));
///
/// // React to a message
/// messageBloc.add(ReactToMessage(
///   groupId: 'groupId123',
///   messageId: 'msgId456',
///   userId: 'userId123',
///   reaction: '👍',
/// ));
/// ```
library;

import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../models/message.dart';
import '../../services/message_service.dart';
import '../../repositories/message_repository.dart';
import 'message_event.dart';
import 'message_state.dart';

/// BLoC that manages message-related operations and real-time chat state.
class MessageBloc extends Bloc<MessageEvent, MessageState> {
  /// Service for message operations and business logic
  final MessageService _messageService;

  /// Subscription to message stream for real-time updates
  StreamSubscription<List<Message>>? _messagesSubscription;

  /// Constructor for MessageBloc.
  ///
  /// Initializes the bloc with an optional message service. If no service is provided,
  /// creates a default MessageService with MessageRepository. Sets up event handlers
  /// for all message-related operations.
  ///
  /// Args:
  ///   [messageService]: Optional service for message operations (auto-created if null)
  MessageBloc({MessageService? messageService})
    : _messageService =
          messageService ??
          MessageService(messageRepository: MessageRepository()),
      super(MessageInitial()) {
    on<LoadMessages>(_onLoadMessages);
    on<SendMessage>(_onSendMessage);
    on<DeleteMessage>(_onDeleteMessage);
    on<UpdateMessage>(_onUpdateMessage);
    on<ReactToMessage>(_onReactToMessage);
    on<RemoveReaction>(_onRemoveReaction);
    on<_MessagesUpdated>(_onMessagesUpdated);
  }

  /// Handles [LoadMessages] events.
  ///
  /// Loads messages for a specific group with real-time updates via stream subscription.
  /// Cancels any existing subscription before creating a new one to prevent memory leaks.
  ///
  /// Args:
  ///   [event]: The LoadMessages event containing the group ID
  ///   [emit]: State emitter function
  Future<void> _onLoadMessages(
    LoadMessages event,
    Emitter<MessageState> emit,
  ) async {
    emit(MessageLoading());

    await _messagesSubscription?.cancel();

    _messagesSubscription = _messageService
        .getMessagesStream(event.groupId)
        .listen(
          (messages) {
            add(_MessagesUpdated(messages: messages, groupId: event.groupId));
          },
          onError: (error) {
            add(const _MessagesError('Impossible de charger les messages'));
          },
        );
  }

  /// Handles [_MessagesUpdated] events.
  ///
  /// Processes real-time updates from the message stream, emitting the
  /// updated message list with the associated group ID.
  ///
  /// Args:
  ///   [event]: The _MessagesUpdated event containing messages and group ID
  ///   [emit]: State emitter function
  void _onMessagesUpdated(_MessagesUpdated event, Emitter<MessageState> emit) {
    emit(MessagesLoaded(messages: event.messages, groupId: event.groupId));
  }

  /// Handles [SendMessage] events.
  ///
  /// Sends a new message to a group chat. The stream subscription will
  /// automatically update the UI with the new message, so no state is emitted here.
  ///
  /// Args:
  ///   [event]: The SendMessage event containing message details
  ///   [emit]: State emitter function
  Future<void> _onSendMessage(
    SendMessage event,
    Emitter<MessageState> emit,
  ) async {
    try {
      // Just perform the action, the stream will update
      await _messageService.sendMessage(
        groupId: event.groupId,
        text: event.text,
        senderId: event.senderId,
        senderName: event.senderName,
      );
    } catch (e) {
      emit(const MessageError('Impossible d\'envoyer le message'));
    }
  }

  /// Handles [DeleteMessage] events.
  ///
  /// Deletes a message from the group chat. The Firestore stream will
  /// automatically update the UI, so no state is emitted here unless there's an error.
  ///
  /// Args:
  ///   [event]: The DeleteMessage event containing group ID and message ID
  ///   [emit]: State emitter function
  Future<void> _onDeleteMessage(
    DeleteMessage event,
    Emitter<MessageState> emit,
  ) async {
    try {
      // Don't emit state, just perform the action
      // The Firestore stream will update automatically
      await _messageService.deleteMessage(
        groupId: event.groupId,
        messageId: event.messageId,
      );
    } catch (e) {
      emit(const MessageError('Impossible de supprimer le message'));
    }
  }

  /// Handles [UpdateMessage] events.
  ///
  /// Updates/edits an existing message in the group chat. The Firestore stream will
  /// automatically update the UI with the edited message, so no state is emitted here.
  ///
  /// Args:
  ///   [event]: The UpdateMessage event containing message ID and new text
  ///   [emit]: State emitter function
  Future<void> _onUpdateMessage(
    UpdateMessage event,
    Emitter<MessageState> emit,
  ) async {
    try {
      // Don't emit state, just perform the action
      // The Firestore stream will update automatically
      await _messageService.updateMessage(
        groupId: event.groupId,
        messageId: event.messageId,
        newText: event.newText,
      );
    } catch (e) {
      emit(const MessageError('Impossible de modifier le message'));
    }
  }

  /// Handles [ReactToMessage] events.
  ///
  /// Adds an emoji reaction to a message. The Firestore stream will
  /// automatically update the UI with the new reaction, so no state is emitted here.
  ///
  /// Args:
  ///   [event]: The ReactToMessage event containing message ID, user ID, and reaction
  ///   [emit]: State emitter function
  Future<void> _onReactToMessage(
    ReactToMessage event,
    Emitter<MessageState> emit,
  ) async {
    try {
      // Don't emit state, just perform the action
      // The Firestore stream will update automatically
      await _messageService.reactToMessage(
        groupId: event.groupId,
        messageId: event.messageId,
        userId: event.userId,
        reaction: event.reaction,
      );
    } catch (e) {
      emit(const MessageError('Impossible d\'ajouter la réaction'));
    }
  }

  /// Handles [RemoveReaction] events.
  ///
  /// Removes a user's reaction from a message. The Firestore stream will
  /// automatically update the UI with the removed reaction, so no state is emitted here.
  ///
  /// Args:
  ///   [event]: The RemoveReaction event containing message ID and user ID
  ///   [emit]: State emitter function
  Future<void> _onRemoveReaction(
    RemoveReaction event,
    Emitter<MessageState> emit,
  ) async {
    try {
      // Don't emit state, just perform the action
      // The Firestore stream will update automatically
      await _messageService.removeReaction(
        groupId: event.groupId,
        messageId: event.messageId,
        userId: event.userId,
      );
    } catch (e) {
      emit(const MessageError('Impossible de supprimer la réaction'));
    }
  }

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

/// Private event for handling real-time message updates from streams.
///
/// This internal event is used to process updates from the message stream
/// subscription and emit appropriate states based on the received data.
class _MessagesUpdated extends MessageEvent {
  /// List of messages received from the stream
  final List<Message> messages;

  /// Group ID associated with the messages
  final String groupId;

  /// Creates a _MessagesUpdated event.
  ///
  /// Args:
  ///   [messages]: List of messages from the stream update
  ///   [groupId]: ID of the group these messages belong to
  const _MessagesUpdated({required this.messages, required this.groupId});

  @override
  List<Object?> get props => [messages, groupId];
}

/// Private event for handling message stream errors.
///
/// This internal event is used to process errors from the message stream
/// subscription and emit appropriate error states.
class _MessagesError extends MessageEvent {
  /// Error message from the stream
  final String error;

  /// Creates a _MessagesError event.
  ///
  /// Args:
  ///   [error]: Error message from the stream failure
  const _MessagesError(this.error);

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