import React, {memo, useCallback, useEffect, useMemo, useRef} from 'react';
import BottomSheet from '@gorhom/bottom-sheet';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {tagNewUser} from 'tag-users';
import {
  MentionBottomSheetFlatList,
  MentionSuggestionUserItemData,
} from '@components/_custom/MentionBottomSheetFlatList';
import {useChannelUsersForMentions} from '@modules/chats/hooks';
import {MentionUser} from '@modules/chats/interfaces';
import {useInputValue, useMentions} from '@modules/chats/contexts';
import {useIsChannel} from '@modules/chats/store';
import {ALL_MENTION} from '@modules/chats/constants';
import {SPACING} from '@shared/theme';
import {windowDimensions} from '@shared/constants';
import {isIos} from '@shared/utils';

import {LIST_ITEM_HEIGHT, styles} from './styles';
import {useKeyboardOffsetStyleContext} from '../KeyboardOffsetStyleProvider';

interface Props {
  channel: string;
}

const getItemLayout = (_data: unknown, index: number) => ({
  length: LIST_ITEM_HEIGHT,
  offset: LIST_ITEM_HEIGHT * index,
  index,
});

const keyExtractor = (item: MentionUser) => item.id.toString();

interface MentionMemberItemData extends MentionSuggestionUserItemData {
  mentionUser: MentionUser;
}

const DialogWithMembers = ({channel}: Props) => {
  const {top} = useSafeAreaInsets();
  const bottomSheetRef = useRef<BottomSheet>(null);
  const {
    isDialogVisible,
    searchText,
    setIsDialogVisible,
    setSearchText,
    setMessageBodyWithoutLastTag,
    tags,
    messageBodyWithoutLastTag,
    tagUserStartPosition,
    addTag,
    changeTagPosition,
    chatFooterHeight,
  } = useMentions();
  const isTagging = useRef(false);
  const [_, setValue] = useInputValue();
  const {groupMembers, getMoreMembers, getMembers, hasNextPage, pageSize} =
    useChannelUsersForMentions({
      channel,
      search: searchText,
      onSearchResultIsEmpty: () => {
        // Avoid closing dialog if 'all' command is visible
        if (!searchText || ALL_MENTION.isMatch(searchText)) {
          return;
        }
        setIsDialogVisible(false);
        setSearchText('');
      },
    });

  const members = useMemo(() => {
    const result = [...groupMembers];

    // Add 'all' command as first item if apply
    if (
      (!searchText || ALL_MENTION.isMatch(searchText)) &&
      result.at(0)?.id !== ALL_MENTION.id
    ) {
      result.unshift(ALL_MENTION.item);
    }
    return result;
  }, [groupMembers, searchText]);
  const membersLength = members.length;
  const snapPoints = useMemo(() => {
    if (membersLength === 0) {
      return [1]; // Minimum snap point when no members
    }
    const baseSnapPoint = chatFooterHeight + SPACING.x2;
    const firstItems = Math.min(membersLength, 3);
    const firstSnapPoint = firstItems * LIST_ITEM_HEIGHT + baseSnapPoint;
    const totalItemsHeight = membersLength * LIST_ITEM_HEIGHT + baseSnapPoint;
    const maxHeight = windowDimensions.height - top;
    const secondSnapPoint = Math.min(maxHeight, totalItemsHeight);
    return firstSnapPoint === secondSnapPoint
      ? [firstSnapPoint]
      : [firstSnapPoint, secondSnapPoint + (isIos ? 0 : 6)];
  }, [membersLength, top, chatFooterHeight]);
  const snapPointsLength = snapPoints.length;
  const hasSnapPoints = snapPointsLength > 0;
  const contentContainerStyle = useMemo(
    () => ({paddingBottom: chatFooterHeight}),
    [chatFooterHeight],
  );

  const closeDialog = useCallback(() => {
    isTagging.current = false;
    setIsDialogVisible(false);
  }, [setIsDialogVisible]);

  const onPress = useCallback(
    (user: MentionUser) => {
      // Calling onPress quickly causes an infinite loop of tagging the same user.
      if (isTagging.current) {
        return;
      }

      isTagging.current = true;
      closeDialog();
      tagNewUser({
        user,
        setTagUser: () => setIsDialogVisible(false),
        setUserSearch: () => setSearchText(''),
        taggedUsers: tags,
        onChangeText: setValue,
        messageBodyWithoutLastTag,
        setMessageBodyWithoutLastTag,
        tagUserStartPosition,
        addTaggedUser: addTag,
        changeTaggedUserStartPosition: changeTagPosition,
      });

      setTimeout(() => {
        isTagging.current = false;
      }, 1000);
    },
    [
      closeDialog,
      tags,
      setValue,
      messageBodyWithoutLastTag,
      setMessageBodyWithoutLastTag,
      tagUserStartPosition,
      addTag,
      changeTagPosition,
      setIsDialogVisible,
      setSearchText,
    ],
  );

  const onEndReached = useCallback(() => {
    if (hasNextPage) {
      getMoreMembers();
    }
  }, [hasNextPage, getMoreMembers]);

  const mentionMembersData = useMemo<MentionMemberItemData[]>(
    () =>
      members.map(member => ({
        id: member.id,
        title:
          member.id === ALL_MENTION.id
            ? ALL_MENTION.label
            : `${member.firstName} ${member.lastName}`.trim(),
        profilePicture: member.profilePicture,
        mentionUser: member,
      })),
    [members],
  );
  const mentionMembersKeyExtractor = useCallback(
    (item: MentionMemberItemData) => keyExtractor(item.mentionUser),
    [],
  );
  const onMentionMemberPress = useCallback(
    (item: MentionMemberItemData) => onPress(item.mentionUser),
    [onPress],
  );

  const isFirstRender = useRef(true);

  /**
   * Open the dialog if it is visible
   */
  useEffect(() => {
    if (isDialogVisible) {
      getMembers();
      hasSnapPoints && bottomSheetRef.current?.snapToIndex(0);
    }
    // Avoid closing the bottom sheet on the first render when the dialog is not visible
    else if (!isFirstRender.current) {
      bottomSheetRef.current?.close();

      // Reset the members list when the dialog is closed.
      setTimeout(() => {
        getMembers();
      }, 100);
    }
    isFirstRender.current = false;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isDialogVisible, hasSnapPoints]);

  const offsetStyle = useKeyboardOffsetStyleContext();

  return (
    <BottomSheet
      ref={bottomSheetRef}
      index={-1}
      snapPoints={snapPoints}
      enableDynamicSizing={!isIos}
      onClose={closeDialog}
      enablePanDownToClose
      enableOverDrag={false}
      containerAnimatedStyle={offsetStyle}
      accessible={!isIos}>
      <MentionBottomSheetFlatList
        data={mentionMembersData}
        keyExtractor={mentionMembersKeyExtractor}
        getItemLayout={getItemLayout}
        pageSize={pageSize}
        onEndReached={onEndReached}
        contentContainerStyle={contentContainerStyle}
        onItemPress={onMentionMemberPress}
        withAllMention
        allMentionId={ALL_MENTION.id}
        allMentionLabel={ALL_MENTION.label}
        isAccessible={!isIos}
        allMentionItemStyle={styles.allMentionItem}
        allMentionItemContentStyle={styles.allMentionItemContent}
        listItemStyle={styles.listItem}
      />
    </BottomSheet>
  );
};

const DialogWithMembersWrapper = ({channel}: Props) => {
  const isChannel = useIsChannel(channel);

  return isChannel ? <DialogWithMembers channel={channel} /> : null;
};

export const DialogMentionMembers = memo(DialogWithMembersWrapper);
