import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:whitenoise/hooks/use_active_chat.dart';
import 'package:whitenoise/hooks/use_blocked_pubkeys.dart';
import 'package:whitenoise/hooks/use_chat_messages.dart';
import 'package:whitenoise/hooks/use_chat_summary.dart';
import 'package:whitenoise/l10n/l10n.dart';
import 'package:whitenoise/providers/account_pubkey_provider.dart';
import 'package:whitenoise/providers/active_chat_provider.dart';
import 'package:whitenoise/providers/notification_provider.dart';
import 'package:whitenoise/providers/offline_provider.dart';
import 'package:whitenoise/routes.dart';
import 'package:whitenoise/services/user_service.dart';
import 'package:whitenoise/src/rust/api/account_groups.dart' as account_groups_api;
import 'package:whitenoise/src/rust/api/groups.dart' show GroupType;
import 'package:whitenoise/theme.dart';
import 'package:whitenoise/utils/bubble_grouping.dart';
import 'package:whitenoise/utils/chat_summary_display.dart';
import 'package:whitenoise/utils/metadata.dart';
import 'package:whitenoise/widgets/chat_message_bubble.dart';
import 'package:whitenoise/widgets/chat_scroll_down_button.dart';
import 'package:whitenoise/widgets/offline_system_notice.dart';
import 'package:whitenoise/widgets/wn_avatar.dart';
import 'package:whitenoise/widgets/wn_button.dart';
import 'package:whitenoise/widgets/wn_chat_system_message.dart';
import 'package:whitenoise/widgets/wn_slate.dart';
import 'package:whitenoise/widgets/wn_slate_chat_header.dart';
import 'package:whitenoise/widgets/wn_system_notice.dart';

class ChatInviteScreen extends HookConsumerWidget {
  final String mlsGroupId;

  const ChatInviteScreen({super.key, required this.mlsGroupId});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final colors = context.colors;
    final typography = context.typographyScaled;
    final pubkey = ref.watch(accountPubkeyProvider);
    final isOffline = ref.watch(offlineProvider).value ?? false;

    final isAccepting = useState(false);
    final isDeclining = useState(false);
    final isProcessing = isAccepting.value || isDeclining.value;
    final noticeMessage = useState<String?>(null);
    final blockedPubkeysState = useBlockedPubkeys(pubkey);

    void showNotice(String message) {
      noticeMessage.value = message;
    }

    void dismissNotice() {
      noticeMessage.value = null;
    }

    final chatSummary = useChatSummary(context, pubkey, mlsGroupId);
    final display = chatSummaryDisplay(chatSummary.data, mlsGroupId);
    final chatMessages = useChatMessages(
      mlsGroupId,
      pubkey: pubkey,
      hiddenPubkeys: blockedPubkeysState.blockedPubkeys,
    );

    final accountGroup = useMemoized(
      () => account_groups_api.getAccountGroup(
        accountPubkey: pubkey,
        mlsGroupId: mlsGroupId,
      ),
      [pubkey, mlsGroupId],
    );
    final accountGroupSnapshot = useFuture(accountGroup);
    final accountGroupIsLoading = accountGroupSnapshot.connectionState == ConnectionState.waiting;
    final welcomerPubkey = accountGroupIsLoading ? null : accountGroupSnapshot.data?.welcomerPubkey;
    final isBlockedInvite =
        !accountGroupIsLoading &&
        welcomerPubkey != null &&
        blockedPubkeysState.blockedPubkeys.contains(welcomerPubkey);
    final inviterName = useStream(
      useMemoized(() {
        if (welcomerPubkey == null) return null;
        return UserService(welcomerPubkey).watchMetadata().map(presentName);
      }, [welcomerPubkey]),
    );

    useEffect(() {
      if (!isBlockedInvite) return null;
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (context.mounted) Routes.goToChatList(context);
      });
      return null;
    }, [isBlockedInvite]);

    useActiveChat(
      groupId: mlsGroupId,
      setActiveChat: ref.read(activeChatProvider.notifier).set,
      clearActiveChat: ref.read(activeChatProvider.notifier).clear,
      cancelGroupNotifications: ref.read(notificationServiceProvider).cancelForGroup,
    );

    final isDm = chatSummary.data == null || chatSummary.data!.groupType == GroupType.directMessage;
    final peerPubkey = chatSummary.data?.dmPeerPubkey;

    void handleAvatarTap() {
      if (peerPubkey != null) {
        unawaited(Routes.pushToInviteInfo(context, peerPubkey));
      } else {
        unawaited(Routes.pushToGroupInfo(context, mlsGroupId));
      }
    }

    Future<void> handleAccept() async {
      isAccepting.value = true;
      try {
        await account_groups_api.acceptAccountGroup(
          accountPubkey: pubkey,
          mlsGroupId: mlsGroupId,
        );
        if (context.mounted) {
          Routes.goToChat(context, mlsGroupId);
        }
      } catch (e) {
        if (context.mounted) {
          showNotice(context.l10n.failedToAcceptInvitation);
        }
      } finally {
        if (context.mounted) isAccepting.value = false;
      }
    }

    Future<void> handleDecline() async {
      isDeclining.value = true;
      try {
        await account_groups_api.declineAccountGroup(
          accountPubkey: pubkey,
          mlsGroupId: mlsGroupId,
        );
        if (context.mounted) {
          Routes.goToChatList(context);
        }
      } catch (e) {
        if (context.mounted) {
          showNotice(context.l10n.failedToDeclineInvitation);
        }
      } finally {
        if (context.mounted) isDeclining.value = false;
      }
    }

    if (blockedPubkeysState.isLoading || accountGroupIsLoading || isBlockedInvite) {
      return Scaffold(backgroundColor: colors.backgroundPrimary);
    }

    return Scaffold(
      backgroundColor: colors.backgroundPrimary,
      body: SafeArea(
        child: Column(
          children: [
            WnSlate(
              tag: 'wn-slate-invite-header',
              systemNotice: isOffline
                  ? const OfflineSystemNotice()
                  : (noticeMessage.value != null
                        ? WnSystemNotice(
                            key: ValueKey(noticeMessage.value),
                            title: noticeMessage.value!,
                            type: WnSystemNoticeType.error,
                            onDismiss: dismissNotice,
                          )
                        : null),
              header: WnSlateChatHeader(
                displayName:
                    display.displayName ??
                    (isDm ? context.l10n.unknownUser : context.l10n.unknownGroup),
                avatarColor: display.color,
                pictureUrl: display.pictureUrl,
                onBack: () => Routes.goToChatList(context),
                onAvatarTap: handleAvatarTap,
              ),
            ),
            Column(
              children: [
                SizedBox(height: 48.h),
                GestureDetector(
                  key: const Key('large_avatar_tap_area'),
                  onTap: handleAvatarTap,
                  child: WnAvatar(
                    pictureUrl: display.pictureUrl,
                    displayName: display.displayName,
                    size: WnAvatarSize.large,
                    color: display.color,
                  ),
                ),
                SizedBox(height: 16.h),
                Text(
                  display.displayName ??
                      (isDm ? context.l10n.unknownUser : context.l10n.unknownGroup),
                  style: typography.semiBold18.copyWith(
                    color: colors.backgroundContentPrimary,
                  ),
                  textAlign: TextAlign.center,
                ),
                SizedBox(height: 8.h),
              ],
            ),
            if (inviterName.data != null && inviterName.data!.isNotEmpty)
              Padding(
                padding: EdgeInsets.only(top: 8.h),
                child: WnChatSystemMessage(
                  text: '${inviterName.data!}${context.l10n.invitedYouToChatSuffix}',
                  textSpan: TextSpan(
                    style: typography.medium14.copyWith(
                      color: colors.backgroundContentSecondary,
                    ),
                    children: [
                      TextSpan(
                        text: inviterName.data,
                        style: const TextStyle(fontWeight: FontWeight.bold),
                      ),
                      TextSpan(text: context.l10n.invitedYouToChatSuffix),
                    ],
                  ),
                ),
              ),
            Expanded(
              child: chatMessages.isLoading
                  ? Center(
                      child: CircularProgressIndicator(
                        color: colors.backgroundContentPrimary,
                      ),
                    )
                  : _InviteMessageList(
                      chatMessages: chatMessages,
                      pubkey: pubkey,
                      groupId: mlsGroupId,
                      isGroupChat: !isDm,
                    ),
            ),
            WnSlate(
              child: Padding(
                padding: EdgeInsets.all(14.w),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  spacing: 12.h,
                  children: [
                    WnButton(
                      key: const Key('chat_invite_decline_button'),
                      text: context.l10n.decline,
                      type: WnButtonType.outline,
                      loading: isDeclining.value,
                      disabled: isOffline || isProcessing,
                      onPressed: handleDecline,
                    ),
                    WnButton(
                      key: const Key('chat_invite_accept_button'),
                      text: context.l10n.accept,
                      loading: isAccepting.value,
                      disabled: isOffline || isProcessing,
                      onPressed: handleAccept,
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _InviteMessageList extends HookWidget {
  const _InviteMessageList({
    required this.chatMessages,
    required this.pubkey,
    required this.groupId,
    required this.isGroupChat,
  });

  final ChatMessagesResult chatMessages;
  final String pubkey;
  final String groupId;
  final bool isGroupChat;

  @override
  Widget build(BuildContext context) {
    final scrollController = useScrollController();
    final hasMoreBelow = useState(false);
    final hasUserScrolled = useRef(false);
    final debounceTimer = useRef<Timer?>(null);
    final lastMarkedId = useRef<String?>(null);

    useEffect(() {
      void markVisibleMessagesAsRead() {
        if (!scrollController.hasClients) return;
        if (!hasUserScrolled.value) return;
        if (chatMessages.messageCount == 0) return;

        final position = scrollController.position;
        final viewportHeight = position.viewportDimension;
        final bottomEdge = position.pixels + viewportHeight;
        final totalExtent = position.maxScrollExtent + viewportHeight;

        final lastVisibleIndex = totalExtent > 0
            ? ((bottomEdge / totalExtent) * chatMessages.messageCount).floor().clamp(
                0,
                chatMessages.messageCount - 1,
              )
            : 0;

        final reversedIndex = chatMessages.messageCount - 1 - lastVisibleIndex;
        final messageId = chatMessages.getMessage(reversedIndex).id;
        if (messageId == lastMarkedId.value) return;
        lastMarkedId.value = messageId;
        unawaited(
          account_groups_api.markMessageRead(
            accountPubkey: pubkey,
            messageId: messageId,
          ),
        );
      }

      void onScrollUpdate() {
        hasUserScrolled.value = true;
        debounceTimer.value?.cancel();
        debounceTimer.value = Timer(
          const Duration(milliseconds: 300),
          markVisibleMessagesAsRead,
        );
      }

      scrollController.addListener(onScrollUpdate);
      return () {
        scrollController.removeListener(onScrollUpdate);
        debounceTimer.value?.cancel();
      };
    }, [scrollController, chatMessages.messageCount]);

    useEffect(() {
      void updateHasMoreBelow() {
        if (!scrollController.hasClients) return;
        final position = scrollController.position;
        hasMoreBelow.value = position.pixels < position.maxScrollExtent - 50;
      }

      scrollController.addListener(updateHasMoreBelow);
      WidgetsBinding.instance.addPostFrameCallback((_) => updateHasMoreBelow());
      return () => scrollController.removeListener(updateHasMoreBelow);
    }, [scrollController]);

    Future<void> scrollToBottom() async {
      await scrollController.animateTo(
        scrollController.position.maxScrollExtent,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeOutCubic,
      );
      if (scrollController.hasClients) {
        scrollController.jumpTo(scrollController.position.maxScrollExtent);
      }
    }

    return Stack(
      children: [
        ListView.builder(
          controller: scrollController,
          padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 8.h),
          itemCount: chatMessages.messageCount,
          itemBuilder: (context, index) {
            final reversedIndex = chatMessages.messageCount - 1 - index;
            final message = chatMessages.getMessage(reversedIndex);
            final isOwnMessage = message.pubkey == pubkey;
            final replyPreview = message.isReply
                ? chatMessages.getChatMessageQuote(message.replyToId)
                : null;
            final nextMessage = reversedIndex > 0
                ? chatMessages.getMessage(reversedIndex - 1)
                : null;
            final showAvatar = shouldShowAvatar(
              current: message,
              next: nextMessage,
              isOwnMessage: isOwnMessage,
              isGroupChat: isGroupChat,
            );
            final showTail = shouldShowTail(
              current: message,
              next: nextMessage,
            );
            final authorMetadata = chatMessages.getAuthorMetadata(
              message.pubkey,
            );
            final senderName = isOwnMessage
                ? context.l10n.you
                : presentName(authorMetadata) ?? context.l10n.unknownUser;
            final senderPictureUrl = authorMetadata?.picture;
            return ChatMessageBubble(
              message: message,
              isOwnMessage: isOwnMessage,
              currentUserPubkey: pubkey,
              replyPreview: replyPreview,
              senderName: senderName,
              senderPictureUrl: senderPictureUrl,
              showAvatar: showAvatar,
              showTail: showTail,
              isGroupChat: isGroupChat,
            );
          },
        ),
        if (hasMoreBelow.value)
          Positioned(
            bottom: 8.h,
            left: 0,
            right: 0,
            child: Center(
              child: ChatScrollDownButton(show: true, onTap: scrollToBottom),
            ),
          ),
      ],
    );
  }
}
