import 'dart:async';

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart'
    hide MessageActions;
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/memory_item.model.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/chat_reaction_row.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_ask_a_friend.entry.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_contacts.entry.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_flame_restored.entry.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_media_entry.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_text_entry.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/chat_unknown.entry.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/message_context_menu.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/message_reply_drag.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/response_container.dart';

class ChatListEntry extends StatefulWidget {
  const ChatListEntry({
    required this.group,
    required this.message,
    this.galleryItems = const [],
    this.scrollToMessage,
    this.onResponseTriggered,
    this.prevMessage,
    this.nextMessage,
    this.userIdToContact,
    this.hideReactions = false,
    super.key,
  });
  final Message? prevMessage;
  final Message? nextMessage;
  final Message message;
  final Group group;
  final Map<int, Contact>? userIdToContact;
  final bool hideReactions;
  final List<MemoryItem> galleryItems;
  final void Function(String)? scrollToMessage;
  final void Function()? onResponseTriggered;

  @override
  State<ChatListEntry> createState() => _ChatListEntryState();
}

class _ChatListEntryState extends State<ChatListEntry> {
  MediaFileService? mediaService;

  List<Reaction> reactions = [];
  StreamSubscription<List<Reaction>>? reactionsSub;

  StreamSubscription<MediaFile?>? mediaFileSub;

  @override
  void initState() {
    super.initState();
    initAsync();
  }

  @override
  void dispose() {
    mediaFileSub?.cancel();
    reactionsSub?.cancel();
    super.dispose();
  }

  Future<void> initAsync() async {
    if (widget.message.mediaId != null) {
      final mediaFileStream = twonlyDB.mediaFilesDao.watchMedia(
        widget.message.mediaId!,
      );
      mediaFileSub = mediaFileStream.listen((mediaFiles) {
        if (mediaFiles != null) {
          mediaService = MediaFileService(mediaFiles);
          if (mounted) setState(() {});
        } else {
          Log.error(
            'Media file not found for ${widget.message.messageId} => ${widget.message.mediaId}',
          );
        }
      });
    }
    final stream = twonlyDB.reactionsDao.watchReactions(
      widget.message.messageId,
    );

    reactionsSub = stream.listen((update) {
      if (!mounted) return;
      setState(() {
        reactions = update;
      });
    });
    setState(() {});
  }

  Widget? _getChatEntry(
    BorderRadius borderRadius,
    int reactionsForWidth,
    BubbleInfo info,
  ) {
    if (widget.message.type == MessageType.text.name) {
      return ChatTextEntry(
        message: widget.message,
        borderRadius: borderRadius,
        info: info,
      );
    }

    if (widget.message.type == MessageType.media.name) {
      if (mediaService == null) return null;
      if (mediaService!.mediaFile.type == MediaType.audio) {
        return ChatAudioEntry(
          message: widget.message,
          mediaService: mediaService!,
          borderRadius: borderRadius,
          info: info,
        );
      }
      return ChatMediaEntry(
        message: widget.message,
        group: widget.group,
        mediaService: mediaService!,
        galleryItems: widget.galleryItems,
        borderRadius: borderRadius,
        info: info,
      );
    }

    if (widget.message.type == MessageType.contacts.name) {
      return ChatContactsEntry(
        message: widget.message,
        borderRadius: borderRadius,
        info: info,
      );
    }

    if (widget.message.type == MessageType.restoreFlameCounter.name) {
      return ChatFlameRestoredEntry(
        message: widget.message,
        borderRadius: borderRadius,
        info: info,
      );
    }

    if (widget.message.type == MessageType.askAboutUser.name) {
      return ChatAskAFriendEntry(
        message: widget.message,
        borderRadius: borderRadius,
        info: info,
      );
    }

    return const ChatUnknownEntry();
  }

  @override
  Widget build(BuildContext context) {
    final right = widget.message.senderId == null;

    final (padding, borderRadius, hideContactAvatar) = getMessageLayout(
      widget.message,
      widget.prevMessage,
      widget.nextMessage,
      reactions.isNotEmpty,
    );

    final seen = <String>{};
    var reactionsForWidth = reactions
        .where((t) => seen.add(t.emoji))
        .toList()
        .length;
    if (reactionsForWidth > 4) reactionsForWidth = 4;

    final info = getBubbleInfo(
      context,
      widget.message,
      widget.nextMessage,
      widget.prevMessage,
      widget.userIdToContact,
      reactionsForWidth * 43.0,
    );

    Widget child = Stack(
      // overflow: Overflow.visible,
      // clipBehavior: Clip.none,
      alignment: right ? Alignment.centerRight : Alignment.centerLeft,
      children: [
        if (widget.message.isDeletedFromSender)
          ChatTextEntry(
            message: widget.message,
            borderRadius: borderRadius,
            info: info,
          )
        else
          Column(
            crossAxisAlignment: right
                ? CrossAxisAlignment.end
                : CrossAxisAlignment.start,
            children: [
              if (info.displayUserName != '' && !widget.group.isDirectChat)
                Padding(
                  padding: const EdgeInsets.only(bottom: 2, left: 4),
                  child: Text(
                    info.displayUserName,
                    style: TextStyle(
                      color: context.color.onSurfaceVariant,
                      fontWeight: FontWeight.w600,
                      fontSize: 13,
                    ),
                  ),
                ),
              ResponseContainer(
                msg: widget.message,
                group: widget.group,
                mediaService: mediaService,
                borderRadius: borderRadius,
                scrollToMessage: widget.scrollToMessage,
                child: _getChatEntry(borderRadius, reactionsForWidth, info),
              ),
              if (reactionsForWidth > 0) const SizedBox(height: 20, width: 10),
            ],
          ),
        if (!widget.message.isDeletedFromSender && !widget.hideReactions)
          Positioned(
            bottom: -20,
            left: 5,
            right: 5,
            child: ReactionRow(
              message: widget.message,
              reactions: reactions,
            ),
          ),
      ],
    );

    if (widget.onResponseTriggered != null) {
      child = MessageReplyDrag(
        message: widget.message,
        onResponseTriggered: widget.onResponseTriggered!,
        child: child,
      );

      child = MessageContextMenu(
        message: widget.message,
        group: widget.group,
        onResponseTriggered: widget.onResponseTriggered!,
        galleryItems: widget.galleryItems,
        mediaFileService: mediaService,
        child: Container(
          child: child,
        ),
      );
    }

    return Align(
      alignment: right ? Alignment.centerRight : Alignment.centerLeft,
      child: Padding(
        padding: padding,
        child: Row(
          mainAxisAlignment: right
              ? MainAxisAlignment.end
              : MainAxisAlignment.start,
          children: [
            if (!right && !widget.group.isDirectChat)
              if (hideContactAvatar)
                const SizedBox(width: 24)
              else
                GestureDetector(
                  onTap: () => context.push(
                    Routes.profileContact(widget.message.senderId!),
                  ),
                  child: AvatarIcon(
                    contactId: widget.message.senderId,
                    fontSize: 12,
                  ),
                ),
            child,
          ],
        ),
      ),
    );
  }
}

(EdgeInsetsGeometry, BorderRadius, bool) getMessageLayout(
  Message message,
  Message? prevMessage,
  Message? nextMessage,
  bool hasReactions,
) {
  var bottom = 10.0;
  var top = 10.0;
  var hideContactAvatar = false;

  var topLeft = 12.0;
  var topRight = 12.0;
  var bottomRight = 12.0;
  var bottomLeft = 12.0;

  if (nextMessage != null) {
    if (message.senderId == nextMessage.senderId) {
      bottom = 3;
    }
  }

  if (prevMessage != null) {
    final combinesWidthNext = combineTextMessageWithNext(prevMessage, message);
    if (combinesWidthNext) {
      top = 1;
      topLeft = 5.0;
    }
  }

  final combinesWidthNext = combineTextMessageWithNext(message, nextMessage);
  if (combinesWidthNext) {
    bottom = 0;
    bottomLeft = 5.0;
    hideContactAvatar = true;
  }

  if (message.senderId == null) {
    final tmp = topLeft;
    topLeft = topRight;
    topRight = tmp;

    final tmp2 = bottomLeft;
    bottomLeft = bottomRight;
    bottomRight = tmp2;
    hideContactAvatar = true;
  }

  return (
    EdgeInsets.only(top: top, bottom: bottom, right: 10, left: 10),
    BorderRadius.only(
      topLeft: Radius.circular(topLeft),
      topRight: Radius.circular(topRight),
      bottomRight: Radius.circular(bottomRight),
      bottomLeft: Radius.circular(bottomLeft),
    ),
    hideContactAvatar,
  );
}
