import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:image_picker/image_picker.dart';
import 'package:island/database/message.dart';
import 'package:island/database/message_repository.dart';
import 'package:island/models/chat.dart';
import 'package:island/models/file.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/message.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/websocket.dart';
import 'package:island/route.gr.dart';
import 'package:island/screens/posts/compose.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/cloud_file_collection.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:super_context_menu/super_context_menu.dart';
import 'package:uuid/uuid.dart';
import 'chat.dart';

final messageRepositoryProvider = FutureProvider.family<MessageRepository, int>(
  (ref, roomId) async {
    final room = await ref.watch(chatroomProvider(roomId).future);
    final identity = await ref.watch(chatroomIdentityProvider(roomId).future);
    final apiClient = ref.watch(apiClientProvider);
    final database = ref.watch(databaseProvider);
    return MessageRepository(room!, identity!, apiClient, database);
  },
);

// Provider for messages with pagination
final messagesProvider = StateNotifierProvider.family<
  MessagesNotifier,
  AsyncValue<List<LocalChatMessage>>,
  int
>((ref, roomId) => MessagesNotifier(ref, roomId));

class MessagesNotifier
    extends StateNotifier<AsyncValue<List<LocalChatMessage>>> {
  final Ref _ref;
  final int _roomId;
  int _currentPage = 0;
  static const int _pageSize = 20;
  bool _hasMore = true;

  MessagesNotifier(this._ref, this._roomId)
    : super(const AsyncValue.loading()) {
    loadInitial();
  }

  Future<void> loadInitial() async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );
      final synced = await repository.syncMessages();
      final messages = await repository.listMessages(
        offset: 0,
        take: _pageSize,
        synced: synced,
      );
      state = AsyncValue.data(messages);
      _currentPage = 0;
      _hasMore = messages.length == _pageSize;
    } catch (e, stack) {
      state = AsyncValue.error(e, stack);
    }
  }

  Future<void> loadMore() async {
    if (!_hasMore || state is AsyncLoading) return;

    try {
      final currentMessages = state.value ?? [];
      _currentPage++;
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );
      final newMessages = await repository.listMessages(
        offset: _currentPage * _pageSize,
        take: _pageSize,
      );

      if (newMessages.isEmpty || newMessages.length < _pageSize) {
        _hasMore = false;
      }

      state = AsyncValue.data([...currentMessages, ...newMessages]);
    } catch (err) {
      showErrorAlert(err);
      _currentPage--;
    }
  }

  Future<void> sendMessage(
    String content,
    List<UniversalFile> attachments, {
    SnChatMessage? replyingTo,
    SnChatMessage? forwardingTo,
    SnChatMessage? editingTo,
  }) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );

      final nonce = const Uuid().v4();

      final baseUrl = _ref.read(serverUrlProvider);
      final atk = await getFreshAtk(
        _ref.watch(tokenPairProvider),
        baseUrl,
        onRefreshed: (atk, rtk) {
          setTokenPair(_ref.watch(sharedPreferencesProvider), atk, rtk);
          _ref.invalidate(tokenPairProvider);
        },
      );
      if (atk == null) throw Exception("Unauthorized");

      LocalChatMessage? pendingMessage;
      final messageTask = repository.sendMessage(
        atk,
        baseUrl,
        _roomId,
        content,
        nonce,
        attachments: attachments,
        replyingTo: replyingTo,
        forwardingTo: forwardingTo,
        editingTo: editingTo,
        onPending: (pending) {
          pendingMessage = pending;
          final currentMessages = state.value ?? [];
          state = AsyncValue.data([pending, ...currentMessages]);
        },
      );

      final message = await messageTask;

      final updatedMessages = state.value ?? [];
      if (pendingMessage != null) {
        final index = updatedMessages.indexWhere(
          (m) => m.id == pendingMessage!.id,
        );
        if (index >= 0) {
          final newList = [...updatedMessages];
          newList[index] = message;
          state = AsyncValue.data(newList);
        }
      } else {
        state = AsyncValue.data([message, ...updatedMessages]);
      }
    } catch (err) {
      showErrorAlert(err);
    }
  }

  Future<void> retryMessage(String pendingMessageId) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );
      final updatedMessage = await repository.retryMessage(pendingMessageId);

      // Update the message in the list
      final currentMessages = state.value ?? [];
      final index = currentMessages.indexWhere((m) => m.id == pendingMessageId);
      if (index >= 0) {
        final newList = [...currentMessages];
        newList[index] = updatedMessage;
        state = AsyncValue.data(newList);
      }
    } catch (err) {
      showErrorAlert(err);
    }
  }

  Future<void> receiveMessage(SnChatMessage remoteMessage) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );

      // Skip if this message is not for this room
      if (remoteMessage.chatRoomId != _roomId) return;

      final localMessage = await repository.receiveMessage(remoteMessage);

      // Add the new message to the state
      final currentMessages = state.value ?? [];

      // Check if the message already exists (by id or nonce)
      final existingIndex = currentMessages.indexWhere(
        (m) =>
            m.id == localMessage.id ||
            (localMessage.nonce != null && m.nonce == localMessage.nonce),
      );

      if (existingIndex >= 0) {
        // Replace existing message
        final newList = [...currentMessages];
        newList[existingIndex] = localMessage;
        state = AsyncValue.data(newList);
      } else {
        // Add new message at the beginning (newest first)
        state = AsyncValue.data([localMessage, ...currentMessages]);
      }
    } catch (err) {
      showErrorAlert(err);
    }
  }

  Future<void> receiveMessageUpdate(SnChatMessage remoteMessage) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );

      // Skip if this message is not for this room
      if (remoteMessage.chatRoomId != _roomId) return;

      final updatedMessage = await repository.receiveMessageUpdate(
        remoteMessage,
      );

      // Update the message in the list
      final currentMessages = state.value ?? [];
      final index = currentMessages.indexWhere(
        (m) => m.id == updatedMessage.id,
      );

      if (index >= 0) {
        final newList = [...currentMessages];
        newList[index] = updatedMessage;
        state = AsyncValue.data(newList);
      }
    } catch (err) {
      showErrorAlert(err);
    }
  }

  Future<void> receiveMessageDeletion(String messageId) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );

      await repository.receiveMessageDeletion(messageId);

      // Remove the message from the list
      final currentMessages = state.value ?? [];
      final filteredMessages =
          currentMessages.where((m) => m.id != messageId).toList();

      if (filteredMessages.length != currentMessages.length) {
        state = AsyncValue.data(filteredMessages);
      }
    } catch (err) {
      showErrorAlert(err);
    }
  }

  Future<void> updateMessage(
    String messageId,
    String content, {
    List<SnCloudFile>? attachments,
    Map<String, dynamic>? meta,
  }) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );

      final updatedMessage = await repository.updateMessage(
        messageId,
        content,
        attachments: attachments,
        meta: meta,
      );

      // Update the message in the list
      final currentMessages = state.value ?? [];
      final index = currentMessages.indexWhere((m) => m.id == messageId);

      if (index >= 0) {
        final newList = [...currentMessages];
        newList[index] = updatedMessage;
        state = AsyncValue.data(newList);
      }
    } catch (err) {
      showErrorAlert(err);
    }
  }

  Future<void> deleteMessage(String messageId) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );

      await repository.deleteMessage(messageId);

      // Remove the message from the list
      final currentMessages = state.value ?? [];
      final filteredMessages =
          currentMessages.where((m) => m.id != messageId).toList();

      if (filteredMessages.length != currentMessages.length) {
        state = AsyncValue.data(filteredMessages);
      }
    } catch (err) {
      showErrorAlert(err);
    }
  }

  Future<LocalChatMessage?> fetchMessageById(String messageId) async {
    try {
      final repository = await _ref.read(
        messageRepositoryProvider(_roomId).future,
      );
      return await repository.getMessageById(messageId);
    } catch (err) {
      showErrorAlert(err);
      return null;
    }
  }
}

@RoutePage()
class ChatRoomScreen extends HookConsumerWidget {
  final int id;
  const ChatRoomScreen({super.key, @PathParam("id") required this.id});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final chatRoom = ref.watch(chatroomProvider(id));
    final chatIdentity = ref.watch(chatroomIdentityProvider(id));
    final messages = ref.watch(messagesProvider(id));
    final messagesNotifier = ref.read(messagesProvider(id).notifier);
    final ws = ref.watch(websocketProvider);

    final messageController = useTextEditingController();
    final scrollController = useScrollController();

    final messageReplyingTo = useState<SnChatMessage?>(null);
    final messageForwardingTo = useState<SnChatMessage?>(null);
    final messageEditingTo = useState<SnChatMessage?>(null);

    // Add scroll listener for pagination
    useEffect(() {
      void onScroll() {
        if (scrollController.position.pixels >=
            scrollController.position.maxScrollExtent - 200) {
          messagesNotifier.loadMore();
        }
      }

      scrollController.addListener(onScroll);
      return () => scrollController.removeListener(onScroll);
    }, [scrollController]);

    // Add websocket listener
    // Add websocket listener for new messages
    useEffect(() {
      void onMessage(WebSocketPacket pkt) {
        if (!pkt.type.startsWith('messages')) return;
        final message = SnChatMessage.fromJson(pkt.data!);
        if (message.chatRoomId != chatRoom.value?.id) return;
        switch (pkt.type) {
          case 'messages.new':
            messagesNotifier.receiveMessage(message);
          case 'messages.update':
            messagesNotifier.receiveMessageUpdate(message);
          case 'messages.delete':
            messagesNotifier.receiveMessageDeletion(message.id);
        }
      }

      final subscription = ws.dataStream.listen(onMessage);
      return () => subscription.cancel();
    }, [ws, chatRoom]);

    final attachments = useState<List<UniversalFile>>([]);

    Future<void> pickPhotoMedia() async {
      final result = await ref
          .watch(imagePickerProvider)
          .pickMultiImage(requestFullMetadata: true);
      if (result.isEmpty) return;
      attachments.value = [
        ...attachments.value,
        ...result.map(
          (e) => UniversalFile(data: e, type: UniversalFileType.image),
        ),
      ];
    }

    Future<void> pickVideoMedia() async {
      final result = await ref
          .watch(imagePickerProvider)
          .pickVideo(source: ImageSource.gallery);
      if (result == null) return;
      attachments.value = [
        ...attachments.value,
        UniversalFile(data: result, type: UniversalFileType.video),
      ];
    }

    void sendMessage() {
      if (messageController.text.trim().isNotEmpty ||
          attachments.value.isNotEmpty) {
        messagesNotifier.sendMessage(
          messageController.text.trim(),
          attachments.value,
          editingTo: messageEditingTo.value,
          forwardingTo: messageForwardingTo.value,
          replyingTo: messageReplyingTo.value,
        );
        messageController.clear();
        messageEditingTo.value = null;
        messageReplyingTo.value = null;
        messageForwardingTo.value = null;
        attachments.value = [];
      }
    }

    return Scaffold(
      appBar: AppBar(
        title: chatRoom.when(
          data:
              (room) => Row(
                spacing: 8,
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  SizedBox(
                    height: 26,
                    width: 26,
                    child:
                        room!.type == 1
                            ? ProfilePictureWidget(
                              fileId:
                                  room.members!.first.account.profile.pictureId,
                            )
                            : room.pictureId != null
                            ? ProfilePictureWidget(
                              fileId: room.pictureId,
                              fallbackIcon: Symbols.chat,
                            )
                            : CircleAvatar(
                              child: Text(
                                room.name[0].toUpperCase(),
                                style: const TextStyle(fontSize: 12),
                              ),
                            ),
                  ),
                  Text(
                    room.type == 1
                        ? room.members!.first.account.nick
                        : room.name,
                  ).fontSize(19),
                ],
              ),
          loading: () => const Text('Loading...'),
          error: (_, __) => const Text('Error'),
        ),
        actions: [
          IconButton(
            icon: const Icon(Icons.more_vert),
            onPressed: () {
              context.router.push(ChatDetailRoute(id: id));
            },
          ),
          const Gap(8),
        ],
      ),
      body: Column(
        children: [
          Expanded(
            child: messages.when(
              data:
                  (messageList) =>
                      messageList.isEmpty
                          ? Center(child: Text('No messages yet'.tr()))
                          : ListView.builder(
                            padding: EdgeInsets.symmetric(vertical: 16),
                            controller: scrollController,
                            reverse: true, // Show newest messages at the bottom
                            itemCount: messageList.length,
                            itemBuilder: (context, index) {
                              final message = messageList[index];
                              return chatIdentity.when(
                                skipError: true,
                                data:
                                    (identity) => _MessageBubble(
                                      message: message,
                                      isCurrentUser:
                                          identity?.id == message.senderId,
                                      onAction: (action) {
                                        switch (action) {
                                          case _MessageBubbleAction.delete:
                                            messagesNotifier.deleteMessage(
                                              message.id,
                                            );
                                          case _MessageBubbleAction.edit:
                                            messageEditingTo.value =
                                                message.toRemoteMessage();
                                            messageController.text =
                                                messageEditingTo
                                                    .value
                                                    ?.content ??
                                                '';
                                            attachments.value =
                                                messageEditingTo
                                                    .value!
                                                    .attachments
                                                    .map(
                                                      (e) =>
                                                          UniversalFile.fromAttachment(
                                                            e,
                                                          ),
                                                    )
                                                    .toList();
                                          case _MessageBubbleAction.forward:
                                            messageForwardingTo.value =
                                                message.toRemoteMessage();
                                          case _MessageBubbleAction.reply:
                                            messageReplyingTo.value =
                                                message.toRemoteMessage();
                                        }
                                      },
                                    ),
                                loading:
                                    () => _MessageBubble(
                                      message: message,
                                      isCurrentUser: false,
                                      onAction: null,
                                    ),
                                error: (_, __) => const SizedBox.shrink(),
                              );
                            },
                          ),
              loading: () => const Center(child: CircularProgressIndicator()),
              error:
                  (error, stack) => Center(
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Text('Error: $error'),
                        ElevatedButton(
                          onPressed: () => messagesNotifier.loadInitial(),
                          child: Text('Retry'.tr()),
                        ),
                      ],
                    ),
                  ),
            ),
          ),
          chatRoom.when(
            data:
                (room) => _ChatInput(
                  messageController: messageController,
                  chatRoom: room!,
                  onSend: sendMessage,
                  onClear: () {
                    if (messageEditingTo.value != null) {
                      attachments.value.clear();
                      messageController.clear();
                    }
                    messageEditingTo.value = null;
                    messageReplyingTo.value = null;
                    messageForwardingTo.value = null;
                  },
                  messageEditingTo: messageEditingTo.value,
                  messageReplyingTo: messageReplyingTo.value,
                  messageForwardingTo: messageForwardingTo.value,
                  onPickFile: (bool isPhoto) {
                    if (isPhoto) {
                      pickPhotoMedia();
                    } else {
                      pickVideoMedia();
                    }
                  },
                  attachments: attachments.value,
                  onUploadAttachment: (_) {
                    // not going to do anything, only upload when send the message
                  },
                  onDeleteAttachment: (index) async {
                    final attachment = attachments.value[index];
                    if (attachment.isOnCloud) {
                      final client = ref.watch(apiClientProvider);
                      await client.delete('/files/${attachment.data.id}');
                    }
                    final clone = List.of(attachments.value);
                    clone.removeAt(index);
                    attachments.value = clone;
                  },
                  onMoveAttachment: (idx, delta) {
                    if (idx + delta < 0 ||
                        idx + delta >= attachments.value.length) {
                      return;
                    }
                    final clone = List.of(attachments.value);
                    clone.insert(idx + delta, clone.removeAt(idx));
                    attachments.value = clone;
                  },
                ),
            error: (_, __) => const SizedBox.shrink(),
            loading: () => const SizedBox.shrink(),
          ),
        ],
      ),
    );
  }
}

class _ChatInput extends StatelessWidget {
  final TextEditingController messageController;
  final SnChatRoom chatRoom;
  final VoidCallback onSend;
  final VoidCallback onClear;
  final Function(bool isPhoto) onPickFile;
  final SnChatMessage? messageReplyingTo;
  final SnChatMessage? messageForwardingTo;
  final SnChatMessage? messageEditingTo;
  final List<UniversalFile> attachments;
  final Function(int) onUploadAttachment;
  final Function(int) onDeleteAttachment;
  final Function(int, int) onMoveAttachment;

  const _ChatInput({
    required this.messageController,
    required this.chatRoom,
    required this.onSend,
    required this.onClear,
    required this.onPickFile,
    required this.messageReplyingTo,
    required this.messageForwardingTo,
    required this.messageEditingTo,
    required this.attachments,
    required this.onUploadAttachment,
    required this.onDeleteAttachment,
    required this.onMoveAttachment,
  });

  @override
  Widget build(BuildContext context) {
    return Material(
      elevation: 8,
      color: Theme.of(context).colorScheme.surface,
      child: Column(
        children: [
          if (attachments.isNotEmpty)
            SizedBox(
              height: 280,
              child: ListView.separated(
                padding: EdgeInsets.symmetric(horizontal: 12),
                scrollDirection: Axis.horizontal,
                itemCount: attachments.length,
                itemBuilder: (context, idx) {
                  return AttachmentPreview(
                    item: attachments[idx],
                    onRequestUpload: () => onUploadAttachment(idx),
                    onDelete: () => onDeleteAttachment(idx),
                    onMove: (delta) => onMoveAttachment(idx, delta),
                  );
                },
                separatorBuilder: (_, __) => const Gap(8),
              ),
            ),
          if (messageReplyingTo != null ||
              messageForwardingTo != null ||
              messageEditingTo != null)
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 16),
              decoration: BoxDecoration(
                color: Theme.of(context).colorScheme.surfaceContainer,
                borderRadius: BorderRadius.circular(8),
              ),
              margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
              child: Row(
                children: [
                  Icon(
                    messageReplyingTo != null
                        ? Symbols.reply
                        : messageForwardingTo != null
                        ? Symbols.forward
                        : Symbols.edit,
                    size: 20,
                    color: Theme.of(context).colorScheme.primary,
                  ),
                  const Gap(8),
                  Expanded(
                    child: Text(
                      messageReplyingTo != null
                          ? 'Replying to ${messageReplyingTo?.sender.account.nick}'
                          : messageForwardingTo != null
                          ? 'Forwarding message'
                          : 'Editing message',
                      style: Theme.of(context).textTheme.bodySmall,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                  IconButton(
                    icon: const Icon(Icons.close, size: 20),
                    onPressed: onClear,
                    padding: EdgeInsets.zero,
                    style: ButtonStyle(
                      minimumSize: WidgetStatePropertyAll(Size(28, 28)),
                    ),
                  ),
                ],
              ),
            ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
            child: Row(
              children: [
                PopupMenuButton(
                  icon: const Icon(Symbols.photo_library),
                  itemBuilder:
                      (context) => [
                        PopupMenuItem(
                          onTap: () => onPickFile(true),
                          child: Row(
                            spacing: 12,
                            children: [
                              const Icon(Symbols.photo),
                              Text('addPhoto').tr(),
                            ],
                          ),
                        ),
                        PopupMenuItem(
                          onTap: () => onPickFile(false),
                          child: Row(
                            spacing: 12,
                            children: [
                              const Icon(Symbols.video_call),
                              Text('addVideo').tr(),
                            ],
                          ),
                        ),
                      ],
                ),
                Expanded(
                  child: TextField(
                    controller: messageController,
                    decoration: InputDecoration(
                      hintText:
                          chatRoom.type == 1
                              ? 'chatDirectMessageHint'.tr(
                                args: [chatRoom.members!.first.account.nick],
                              )
                              : 'chatMessageHint'.tr(args: [chatRoom.name]),
                      border: InputBorder.none,
                      isDense: true,
                      contentPadding: const EdgeInsets.symmetric(
                        horizontal: 12,
                        vertical: 4,
                      ),
                    ),
                    maxLines: null,
                    onTapOutside:
                        (_) => FocusManager.instance.primaryFocus?.unfocus(),
                    onSubmitted: (_) => onSend(),
                  ),
                ),
                IconButton(
                  icon: const Icon(Icons.send),
                  color: Theme.of(context).colorScheme.primary,
                  onPressed: onSend,
                ),
              ],
            ).padding(bottom: MediaQuery.of(context).padding.bottom),
          ),
        ],
      ),
    );
  }
}

class _MessageBubbleAction {
  static const String edit = "edit";
  static const String delete = "delete";
  static const String reply = "reply";
  static const String forward = "forward";
}

class _MessageBubble extends HookConsumerWidget {
  final LocalChatMessage message;
  final bool isCurrentUser;
  final Function(String action)? onAction;

  const _MessageBubble({
    required this.message,
    required this.isCurrentUser,
    required this.onAction,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final textColor =
        isCurrentUser
            ? Theme.of(context).colorScheme.onPrimaryContainer
            : Theme.of(context).colorScheme.onSurfaceVariant;
    final containerColor =
        isCurrentUser
            ? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.5)
            : Theme.of(context).colorScheme.surfaceContainer;

    return ContextMenuWidget(
      menuProvider: (_) {
        if (onAction == null) return Menu(children: []);
        return Menu(
          children: [
            if (isCurrentUser)
              MenuAction(
                title: 'edit'.tr(),
                image: MenuImage.icon(Symbols.edit),
                callback: () {
                  onAction!.call(_MessageBubbleAction.edit);
                },
              ),
            if (isCurrentUser)
              MenuAction(
                title: 'delete'.tr(),
                image: MenuImage.icon(Symbols.delete),
                callback: () {
                  onAction!.call(_MessageBubbleAction.delete);
                },
              ),
            if (isCurrentUser) MenuSeparator(),
            MenuAction(
              title: 'reply'.tr(),
              image: MenuImage.icon(Symbols.reply),
              callback: () {
                onAction!.call(_MessageBubbleAction.reply);
              },
            ),
            MenuAction(
              title: 'forward'.tr(),
              image: MenuImage.icon(Symbols.forward),
              callback: () {
                onAction!.call(_MessageBubbleAction.forward);
              },
            ),
          ],
        );
      },
      child: Material(
        color: Theme.of(context).colorScheme.surface,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.end,
            mainAxisAlignment:
                isCurrentUser ? MainAxisAlignment.end : MainAxisAlignment.start,
            children: [
              if (!isCurrentUser)
                ProfilePictureWidget(
                  fileId:
                      message
                          .toRemoteMessage()
                          .sender
                          .account
                          .profile
                          .pictureId,
                  radius: 18,
                ),
              const Gap(8),
              Flexible(
                child: Container(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 12,
                    vertical: 8,
                  ),
                  decoration: BoxDecoration(
                    color: containerColor,
                    borderRadius: BorderRadius.circular(16),
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      if (message.toRemoteMessage().repliedMessageId != null)
                        _MessageQuoteWidget(
                          message: message,
                          textColor: textColor,
                          isReply: true,
                        ),
                      if (message.toRemoteMessage().forwardedMessageId != null)
                        _MessageQuoteWidget(
                          message: message,
                          textColor: textColor,
                          isReply: false,
                        ),
                      if (message.toRemoteMessage().content?.isNotEmpty ??
                          false)
                        Text(
                          message.toRemoteMessage().content!,
                          style: TextStyle(color: textColor),
                        ),
                      if (message.toRemoteMessage().attachments.isNotEmpty)
                        CloudFileList(
                          files: message.toRemoteMessage().attachments,
                        ).padding(top: 4),
                      const Gap(4),
                      Row(
                        spacing: 4,
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          Text(
                            DateFormat.Hm().format(message.createdAt.toLocal()),
                            style: TextStyle(fontSize: 10, color: textColor),
                          ),
                          if (message.toRemoteMessage().editedAt != null)
                            Text(
                              'edited'.tr().toLowerCase(),
                              style: TextStyle(fontSize: 10, color: textColor),
                            ),
                          if (isCurrentUser)
                            _buildStatusIcon(
                              context,
                              message.status,
                              textColor,
                            ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              const Gap(8),
              if (isCurrentUser)
                ProfilePictureWidget(
                  fileId:
                      message
                          .toRemoteMessage()
                          .sender
                          .account
                          .profile
                          .pictureId,
                  radius: 18,
                ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildStatusIcon(
    BuildContext context,
    MessageStatus status,
    Color textColor,
  ) {
    switch (status) {
      case MessageStatus.pending:
        return Icon(Icons.access_time, size: 12, color: textColor);
      case MessageStatus.sent:
        return Icon(Icons.check, size: 12, color: textColor);
      case MessageStatus.failed:
        return Consumer(
          builder:
              (context, ref, _) => GestureDetector(
                onTap: () {
                  ref
                      .read(messagesProvider(message.roomId).notifier)
                      .retryMessage(message.id);
                },
                child: const Icon(
                  Icons.error_outline,
                  size: 12,
                  color: Colors.red,
                ),
              ),
        );
    }
  }
}

class _MessageQuoteWidget extends HookConsumerWidget {
  final LocalChatMessage message;
  final Color textColor;
  final bool isReply;

  const _MessageQuoteWidget({
    required this.message,
    required this.textColor,
    required this.isReply,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final messagesNotifier = ref.watch(
      messagesProvider(message.roomId).notifier,
    );

    return FutureBuilder<LocalChatMessage?>(
      future: messagesNotifier.fetchMessageById(
        isReply
            ? message.toRemoteMessage().repliedMessageId!
            : message.toRemoteMessage().forwardedMessageId!,
      ),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return ClipRRect(
            borderRadius: BorderRadius.all(Radius.circular(8)),
            child: Container(
              padding: EdgeInsets.symmetric(vertical: 4, horizontal: 6),
              color: Theme.of(
                context,
              ).colorScheme.primaryFixedDim.withOpacity(0.4),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  if (isReply)
                    Row(
                      spacing: 4,
                      children: [
                        Icon(Symbols.reply, size: 16, color: textColor),
                        Text(
                          'Replying to ${snapshot.data!.toRemoteMessage().sender.account.nick}',
                        ).textColor(textColor).bold(),
                      ],
                    )
                  else
                    Row(
                      spacing: 4,
                      children: [
                        Icon(Symbols.forward, size: 16, color: textColor),
                        Text(
                          'Forwarded from ${snapshot.data!.toRemoteMessage().sender.account.nick}',
                        ).textColor(textColor).bold(),
                      ],
                    ),
                  if (snapshot.data!.toRemoteMessage().content?.isNotEmpty ??
                      false)
                    Text(
                      snapshot.data!.toRemoteMessage().content!,
                      style: TextStyle(color: textColor),
                    ),
                ],
              ),
            ),
          ).padding(bottom: 4);
        } else {
          return SizedBox.shrink();
        }
      },
    );
  }
}