import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../../core/utils/app_logger.dart';
import '../../domain/entities/chat_message.dart';
import '../../domain/entities/conversation.dart';
import '../../domain/repositories/chat_repository.dart';

/// Bloc pour la gestion des événements et états liés au chat.
///
/// Ce bloc gère:
/// - Le chargement des conversations
/// - Le chargement des messages d'une conversation
/// - L'envoi de messages
/// - Le marquage des messages comme lus
/// - La suppression de messages et conversations
/// - Le compteur de messages non lus
class ChatBloc extends Bloc<ChatEvent, ChatState> {
  /// Constructeur avec injection du repository de chat.
  ChatBloc({required this.chatRepository}) : super(const ChatState()) {
    on<LoadConversations>(_onLoadConversations);
    on<LoadMessages>(_onLoadMessages);
    on<SendMessage>(_onSendMessage);
    on<MarkMessageAsRead>(_onMarkMessageAsRead);
    on<MarkMessageAsDelivered>(_onMarkMessageAsDelivered);
    on<MarkConversationAsRead>(_onMarkConversationAsRead);
    on<DeleteMessage>(_onDeleteMessage);
    on<DeleteConversation>(_onDeleteConversation);
    on<LoadUnreadCount>(_onLoadUnreadCount);
    on<AddMessageToConversation>(_onAddMessageToConversation);
    on<UpdateTypingStatus>(_onUpdateTypingStatus);
  }

  final ChatRepository chatRepository;

  void _log(String message) {
    AppLogger.d(message, tag: 'ChatBloc');
  }

  /// Charge toutes les conversations d'un utilisateur.
  Future<void> _onLoadConversations(
    LoadConversations event,
    Emitter<ChatState> emit,
  ) async {
    _log('Chargement des conversations pour ${event.userId}');
    emit(state.copyWith(conversationsStatus: ConversationsStatus.loading));

    final result = await chatRepository.getConversations(event.userId);
    result.fold(
      (failure) {
        _log('Erreur: ${failure.message}');
        emit(state.copyWith(
          conversationsStatus: ConversationsStatus.error,
          errorMessage: failure.message,
        ));
      },
      (conversations) {
        _log('${conversations.length} conversations chargées');
        emit(state.copyWith(
          conversationsStatus: ConversationsStatus.success,
          conversations: conversations,
        ));
      },
    );
  }

  /// Charge les messages d'une conversation.
  Future<void> _onLoadMessages(
    LoadMessages event,
    Emitter<ChatState> emit,
  ) async {
    _log('Chargement des messages de ${event.conversationId}');
    emit(state.copyWith(messagesStatus: MessagesStatus.loading));

    final result = await chatRepository.getMessages(
      event.conversationId,
      page: event.page,
      size: event.size,
    );
    result.fold(
      (failure) {
        _log('Erreur: ${failure.message}');
        emit(state.copyWith(
          messagesStatus: MessagesStatus.error,
          errorMessage: failure.message,
        ));
      },
      (messages) {
        _log('${messages.length} messages chargés');
        emit(state.copyWith(
          messagesStatus: MessagesStatus.success,
          messages: messages,
          currentConversationId: event.conversationId,
        ));
      },
    );
  }

  /// Envoie un nouveau message.
  Future<void> _onSendMessage(
    SendMessage event,
    Emitter<ChatState> emit,
  ) async {
    _log('Envoi d\'un message');

    // 🚀 OPTIMISTIC UI: Créer un message temporaire IMMÉDIATEMENT
    final tempId = 'temp_${DateTime.now().millisecondsSinceEpoch}';
    final tempMessage = ChatMessage(
      id: tempId,
      conversationId: state.currentConversationId ?? '',
      senderId: event.senderId,
      senderFirstName: '', // Sera mis à jour par le serveur
      senderLastName: '',
      senderProfileImageUrl: '',
      content: event.content,
      timestamp: DateTime.now(),
      isRead: false,
      isDelivered: false, // Pas encore délivré
      attachmentUrl: event.mediaUrl ?? '',
      attachmentType: event.messageType == 'image' ? AttachmentType.image :
                      event.messageType == 'video' ? AttachmentType.video :
                      event.messageType == 'audio' ? AttachmentType.audio :
                      event.messageType == 'file' ? AttachmentType.file :
                      AttachmentType.none,
    );

    AppLogger.d('Message optimiste créé avec ID temporaire: $tempId', tag: 'ChatBloc');

    // Ajouter immédiatement à la liste (avant la requête HTTP)
    final optimisticMessages = List<ChatMessage>.from(state.messages);
    optimisticMessages.insert(0, tempMessage);

    emit(state.copyWith(
      sendMessageStatus: SendMessageStatus.sending,
      messages: optimisticMessages,
    ));

    // Maintenant envoyer au serveur
    final result = await chatRepository.sendMessage(
      senderId: event.senderId,
      recipientId: event.recipientId,
      content: event.content,
      messageType: event.messageType,
      mediaUrl: event.mediaUrl,
    );

    result.fold(
      (failure) {
        _log('Erreur: ${failure.message}');
        AppLogger.e('Échec envoi - Retrait du message temporaire', tag: 'ChatBloc');

        // Retirer le message temporaire en cas d'erreur
        final revertedMessages = state.messages
            .where((msg) => msg.id != tempId)
            .toList();

        emit(state.copyWith(
          sendMessageStatus: SendMessageStatus.error,
          errorMessage: failure.message,
          messages: revertedMessages,
        ));
      },
      (message) {
        _log('Message envoyé avec succès');
        AppLogger.d('Message envoyé - ID réel: ${message.id}, isDelivered: ${message.isDelivered}, isRead: ${message.isRead}', tag: 'ChatBloc');

        // Remplacer le message temporaire par le vrai message du serveur
        final updatedMessages = state.messages.map((msg) {
          if (msg.id == tempId) {
            AppLogger.d('Remplacement du message temporaire par le message réel', tag: 'ChatBloc');
            return message;
          }
          return msg;
        }).toList();

        emit(state.copyWith(
          sendMessageStatus: SendMessageStatus.success,
          messages: updatedMessages,
        ));
      },
    );
  }

  /// Marque un message comme lu.
  Future<void> _onMarkMessageAsRead(
    MarkMessageAsRead event,
    Emitter<ChatState> emit,
  ) async {
    _log('Marquage du message ${event.messageId} comme lu');

    final result = await chatRepository.markMessageAsRead(event.messageId);
    result.fold(
      (failure) => _log('Erreur: ${failure.message}'),
      (_) => _log('Message marqué comme lu'),
    );
  }

  /// Marque un message comme délivré (reçu par le destinataire).
  void _onMarkMessageAsDelivered(
    MarkMessageAsDelivered event,
    Emitter<ChatState> emit,
  ) {
    _log('Marquage du message ${event.messageId} comme délivré');
    AppLogger.d('_onMarkMessageAsDelivered appelé pour ${event.messageId}', tag: 'ChatBloc');
    AppLogger.d('Nombre de messages actuels: ${state.messages.length}', tag: 'ChatBloc');

    // Mettre à jour le message dans la liste des messages
    final updatedMessages = state.messages.map((msg) {
      if (msg.id == event.messageId) {
        AppLogger.d('Message trouvé! Avant: isDelivered=${msg.isDelivered}, Après: isDelivered=true', tag: 'ChatBloc');
        return msg.copyWith(isDelivered: true);
      }
      return msg;
    }).toList();

    AppLogger.d('Émission du nouveau state avec ${updatedMessages.length} messages', tag: 'ChatBloc');
    emit(state.copyWith(messages: updatedMessages));
  }

  /// Marque tous les messages d'une conversation comme lus.
  Future<void> _onMarkConversationAsRead(
    MarkConversationAsRead event,
    Emitter<ChatState> emit,
  ) async {
    _log('Marquage de la conversation ${event.conversationId} comme lue');

    final result = await chatRepository.markConversationAsRead(
      event.conversationId,
      event.userId,
    );
    result.fold(
      (failure) => _log('Erreur: ${failure.message}'),
      (_) {
        _log('Conversation marquée comme lue');
        // Mettre à jour le compteur de non lus dans la conversation
        final updatedConversations = state.conversations.map((conv) {
          if (conv.id == event.conversationId) {
            return Conversation(
              id: conv.id,
              participantId: conv.participantId,
              participantFirstName: conv.participantFirstName,
              participantLastName: conv.participantLastName,
              participantProfileImageUrl: conv.participantProfileImageUrl,
              lastMessage: conv.lastMessage,
              lastMessageTimestamp: conv.lastMessageTimestamp,
              unreadCount: 0,
              isTyping: conv.isTyping,
            );
          }
          return conv;
        }).toList();

        emit(state.copyWith(conversations: updatedConversations));
      },
    );
  }

  /// Supprime un message.
  Future<void> _onDeleteMessage(
    DeleteMessage event,
    Emitter<ChatState> emit,
  ) async {
    _log('Suppression du message ${event.messageId}');

    final result = await chatRepository.deleteMessage(event.messageId);
    result.fold(
      (failure) {
        _log('Erreur: ${failure.message}');
        emit(state.copyWith(errorMessage: failure.message));
      },
      (_) {
        _log('Message supprimé');
        // Retirer le message de la liste
        final updatedMessages = state.messages
            .where((msg) => msg.id != event.messageId)
            .toList();

        emit(state.copyWith(messages: updatedMessages));
      },
    );
  }

  /// Supprime une conversation.
  Future<void> _onDeleteConversation(
    DeleteConversation event,
    Emitter<ChatState> emit,
  ) async {
    _log('Suppression de la conversation ${event.conversationId}');

    final result = await chatRepository.deleteConversation(event.conversationId);
    result.fold(
      (failure) {
        _log('Erreur: ${failure.message}');
        emit(state.copyWith(errorMessage: failure.message));
      },
      (_) {
        _log('Conversation supprimée');
        // Retirer la conversation de la liste
        final updatedConversations = state.conversations
            .where((conv) => conv.id != event.conversationId)
            .toList();

        emit(state.copyWith(conversations: updatedConversations));
      },
    );
  }

  /// Charge le nombre de messages non lus.
  Future<void> _onLoadUnreadCount(
    LoadUnreadCount event,
    Emitter<ChatState> emit,
  ) async {
    _log('Chargement du nombre de messages non lus');

    final result = await chatRepository.getUnreadMessagesCount(event.userId);
    result.fold(
      (failure) => _log('Erreur: ${failure.message}'),
      (count) {
        _log('$count messages non lus');
        emit(state.copyWith(unreadCount: count));
      },
    );
  }

  /// Ajoute un message reçu via WebSocket à la conversation.
  void _onAddMessageToConversation(
    AddMessageToConversation event,
    Emitter<ChatState> emit,
  ) {
    _log('Ajout d\'un message reçu via WebSocket');

    final updatedMessages = List<ChatMessage>.from(state.messages);
    updatedMessages.insert(0, event.message);

    emit(state.copyWith(messages: updatedMessages));
  }

  /// Met à jour le statut de frappe d'un utilisateur.
  void _onUpdateTypingStatus(
    UpdateTypingStatus event,
    Emitter<ChatState> emit,
  ) {
    _log('Mise à jour du statut de frappe pour ${event.conversationId}');

    final updatedConversations = state.conversations.map((conv) {
      if (conv.id == event.conversationId) {
        return Conversation(
          id: conv.id,
          participantId: conv.participantId,
          participantFirstName: conv.participantFirstName,
          participantLastName: conv.participantLastName,
          participantProfileImageUrl: conv.participantProfileImageUrl,
          lastMessage: conv.lastMessage,
          lastMessageTimestamp: conv.lastMessageTimestamp,
          unreadCount: conv.unreadCount,
          isTyping: event.isTyping,
        );
      }
      return conv;
    }).toList();

    emit(state.copyWith(conversations: updatedConversations));
  }
}

// ============================================================================
// ÉVÉNEMENTS
// ============================================================================

/// Classe de base pour tous les événements du chat.
abstract class ChatEvent extends Equatable {
  const ChatEvent();

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

/// Événement pour charger les conversations d'un utilisateur.
class LoadConversations extends ChatEvent {
  const LoadConversations(this.userId);

  final String userId;

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

/// Événement pour charger les messages d'une conversation.
class LoadMessages extends ChatEvent {
  const LoadMessages({
    required this.conversationId,
    this.page = 0,
    this.size = 50,
  });

  final String conversationId;
  final int page;
  final int size;

  @override
  List<Object?> get props => [conversationId, page, size];
}

/// Événement pour envoyer un message.
class SendMessage extends ChatEvent {
  const SendMessage({
    required this.senderId,
    required this.recipientId,
    required this.content,
    this.messageType,
    this.mediaUrl,
  });

  final String senderId;
  final String recipientId;
  final String content;
  final String? messageType;
  final String? mediaUrl;

  @override
  List<Object?> get props => [senderId, recipientId, content, messageType, mediaUrl];
}

/// Événement pour marquer un message comme lu.
class MarkMessageAsRead extends ChatEvent {
  const MarkMessageAsRead(this.messageId);

  final String messageId;

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

/// Événement pour marquer tous les messages d'une conversation comme lus.
class MarkConversationAsRead extends ChatEvent {
  const MarkConversationAsRead({
    required this.conversationId,
    required this.userId,
  });

  final String conversationId;
  final String userId;

  @override
  List<Object?> get props => [conversationId, userId];
}

/// Événement pour supprimer un message.
class DeleteMessage extends ChatEvent {
  const DeleteMessage(this.messageId);

  final String messageId;

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

/// Événement pour supprimer une conversation.
class DeleteConversation extends ChatEvent {
  const DeleteConversation(this.conversationId);

  final String conversationId;

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

/// Événement pour charger le nombre de messages non lus.
class LoadUnreadCount extends ChatEvent {
  const LoadUnreadCount(this.userId);

  final String userId;

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

/// Événement pour ajouter un message reçu via WebSocket.
class AddMessageToConversation extends ChatEvent {
  const AddMessageToConversation(this.message);

  final ChatMessage message;

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

/// Événement pour marquer un message comme délivré.
class MarkMessageAsDelivered extends ChatEvent {
  const MarkMessageAsDelivered(this.messageId);

  final String messageId;

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

/// Événement pour mettre à jour le statut de frappe.
class UpdateTypingStatus extends ChatEvent {
  const UpdateTypingStatus({
    required this.conversationId,
    required this.isTyping,
  });

  final String conversationId;
  final bool isTyping;

  @override
  List<Object?> get props => [conversationId, isTyping];
}

// ============================================================================
// ÉTATS
// ============================================================================

/// Énumération des statuts de chargement des conversations.
enum ConversationsStatus { initial, loading, success, error }

/// Énumération des statuts de chargement des messages.
enum MessagesStatus { initial, loading, success, error }

/// Énumération des statuts d'envoi de message.
enum SendMessageStatus { initial, sending, success, error }

/// État du ChatBloc.
class ChatState extends Equatable {
  const ChatState({
    this.conversationsStatus = ConversationsStatus.initial,
    this.messagesStatus = MessagesStatus.initial,
    this.sendMessageStatus = SendMessageStatus.initial,
    this.conversations = const [],
    this.messages = const [],
    this.currentConversationId,
    this.unreadCount = 0,
    this.errorMessage,
  });

  final ConversationsStatus conversationsStatus;
  final MessagesStatus messagesStatus;
  final SendMessageStatus sendMessageStatus;
  final List<Conversation> conversations;
  final List<ChatMessage> messages;
  final String? currentConversationId;
  final int unreadCount;
  final String? errorMessage;

  ChatState copyWith({
    ConversationsStatus? conversationsStatus,
    MessagesStatus? messagesStatus,
    SendMessageStatus? sendMessageStatus,
    List<Conversation>? conversations,
    List<ChatMessage>? messages,
    String? currentConversationId,
    int? unreadCount,
    String? errorMessage,
  }) {
    return ChatState(
      conversationsStatus: conversationsStatus ?? this.conversationsStatus,
      messagesStatus: messagesStatus ?? this.messagesStatus,
      sendMessageStatus: sendMessageStatus ?? this.sendMessageStatus,
      conversations: conversations ?? this.conversations,
      messages: messages ?? this.messages,
      currentConversationId: currentConversationId ?? this.currentConversationId,
      unreadCount: unreadCount ?? this.unreadCount,
      errorMessage: errorMessage,
    );
  }

  @override
  List<Object?> get props => [
        conversationsStatus,
        messagesStatus,
        sendMessageStatus,
        conversations,
        messages,
        currentConversationId,
        unreadCount,
        errorMessage,
      ];
}
