import React, { useState, useCallback, useEffect, useRef } from 'react';
import {
  View,
  TouchableOpacity,
  Image,
  Text,
  FlatList,
  Dimensions,
  Alert,
  ScrollView,
  Linking,
  ActivityIndicator,
  AppState,
  AppStateStatus,
  TextInput,
  Platform
} from 'react-native';
import {
  GiftedChat,
  Bubble,
  InputToolbar,
  IMessage,
  Send,
  BubbleProps,
  Composer,
  TimeProps,
  MessageProps,
  Actions,
  isSameUser,
  isSameDay,
  SystemMessage,
  MessageText
} from 'react-native-gifted-chat';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { GestureHandlerRootView, Swipeable } from 'react-native-gesture-handler';
import { Header, WarningModal } from 'src/components';
import { Colors } from 'src/theme';
import { useFocusEffect, useNavigation } from '@react-navigation/native';
import { Audio } from 'expo-av';
import ChatMessageBox from '../Components/ChatMessageBox';
import ReplyMessageBar from '../Components/ReplyMessageBar';
import { useSharedValue, withTiming } from 'react-native-reanimated';
import { BlurView } from 'expo-blur';
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
import Clipboard from '@react-native-clipboard/clipboard';
import { trigger } from 'react-native-haptic-feedback';
import ReactModal from 'react-native-modal';
import { storage, StoreType } from 'src/storage';
import {
  usePostGetGroupChatQuery,
  usePostSendGroupMessageMutation,
  usePostReactToGroupMessageMutation,
  usePostGroupMessagesReadMutation,
  usePostDeleteGroupMessageMutation,
  usePostGetPinnedGroupMessageQuery,
  usePostSetPinGroupMessageMutation,
  usePostGetGroupSettingsQuery,
  usePostGetGroupMembersQuery
} from '@api/chat';
import { CustomMessage, GroupMessage, Reaction } from '../types';
import { API_HOST, APP_VERSION, WEBSOCKET_URL } from 'src/constants';
import ReactionBar from '../Components/ReactionBar';
import OptionsMenu from '../Components/OptionsMenu';
import EmojiSelectorModal from '../Components/EmojiSelectorModal';
import TypingIndicator from '../Components/TypingIndicator';
import { styles } from '../ChatScreen/styles';
import SendIcon from 'assets/icons/messages/send.svg';
import { SheetManager } from 'react-native-actions-sheet';
import { NAVIGATION_PAGES } from 'src/types';
import { usePushNotification } from 'src/contexts/PushNotificationContext';
import ReactionsListModal from '../Components/ReactionsListModal';
import { dismissChatNotifications } from '../utils';
import { useMessagesStore } from 'src/stores/unreadMessagesStore';
import FileViewer from 'react-native-file-viewer';
import * as FileSystem from 'expo-file-system';
import ImageView from 'better-react-native-image-viewing';
import * as MediaLibrary from 'expo-media-library';

import BanIcon from 'assets/icons/messages/ban.svg';
import AttachmentsModal from '../Components/AttachmentsModal';
import RenderMessageVideo from '../Components/renderMessageVideo';
import MessageLocation from '../Components/MessageLocation';
import GroupIcon from 'assets/icons/messages/group-chat.svg';
import { CACHED_ATTACHMENTS_DIR } from 'src/constants/constants';
import GroupStatusModal from '../Components/GroupStatusModal';
import PinIcon from 'assets/icons/messages/pin.svg';
import MentionsList from '../Components/MentionsList';

const options = {
  enableVibrateFallback: true,
  ignoreAndroidSystemSettings: false
};

const reactionEmojis = ['👍', '❤️', '😂', '😮', '😭'];

const GroupChatScreen = ({ route }: { route: any }) => {
  const token = storage.get('token', StoreType.STRING) as string;
  const {
    group_token,
    name,
    avatar,
    userType = 'normal'
  }: {
    group_token: string;
    name: string;
    avatar: string | null;
    userType: 'normal' | 'not_exist' | 'blocked';
  } = route.params;
  const groupName =
    userType === 'blocked'
      ? 'Account is blocked'
      : userType === 'not_exist'
        ? 'Account does not exist'
        : name;

  const currentUserId = storage.get('uid', StoreType.STRING) as number;
  const insets = useSafeAreaInsets();
  const [groupAvatar, setGroupAvatar] = useState<string | null>(null);
  const [messages, setMessages] = useState<CustomMessage[] | null>();
  const navigation = useNavigation();
  const [prevThenMessageId, setPrevThenMessageId] = useState<number>(-1);
  const {
    data: chatData,
    refetch: refetch,
    isFetching: isFetching
  } = usePostGetGroupChatQuery(token, group_token, 50, prevThenMessageId, true);
  const [canSeeMembers, setCanSeeMembers] = useState(false);

  const { data: pinData, refetch: refetchPinned } = usePostGetPinnedGroupMessageQuery(
    token,
    group_token,
    true
  );
  const { data } = usePostGetGroupSettingsQuery(token, group_token, true);
  const { data: members, refetch: refetchMembers } = usePostGetGroupMembersQuery(
    token,
    group_token,
    canSeeMembers
  );

  const { mutateAsync: sendMessage } = usePostSendGroupMessageMutation();

  const [isSearchingMessage, setIsSearchingMessage] = useState<number | null>(null);

  const swipeableRowRef = useRef<Swipeable | null>(null);
  const messageContainerRef = useRef<FlatList<IMessage> | null>(null);
  const [selectedMedia, setSelectedMedia] = useState<any>(null);

  const [pinned, setPinned] = useState<any>(null);
  const [replyMessage, setReplyMessage] = useState<CustomMessage | null>(null);
  const [modalInfo, setModalInfo] = useState({
    visible: false,
    type: 'confirm',
    message: '',
    action: () => {},
    buttonTitle: '',
    title: ''
  });

  const [selectedMessage, setSelectedMessage] = useState<BubbleProps<CustomMessage> | null>(null);
  const [emojiSelectorVisible, setEmojiSelectorVisible] = useState(false);
  const [messagePosition, setMessagePosition] = useState<{
    x: number;
    y: number;
    width: number;
    height: number;
    isMine: boolean;
  } | null>(null);

  const [isModalVisible, setIsModalVisible] = useState(false);
  const [unreadMessageIndex, setUnreadMessageIndex] = useState<number | null>(null);
  const { mutateAsync: markMessagesAsRead } = usePostGroupMessagesReadMutation();
  const { mutateAsync: deleteMessage } = usePostDeleteGroupMessageMutation();
  const { mutateAsync: reactToMessage } = usePostReactToGroupMessageMutation();
  const { mutateAsync: pinMessage } = usePostSetPinGroupMessageMutation();

  const [highlightedMessageId, setHighlightedMessageId] = useState<number | null>(null);
  const [isRerendering, setIsRerendering] = useState<boolean>(false);
  const [isTyping, setIsTyping] = useState<string | null>(null);

  const messageRefs = useRef<{ [key: string]: any }>({});
  const flatList = useRef<FlatList | null>(null);
  const scrollY = useSharedValue(0);
  const { isSubscribed } = usePushNotification();
  const [isLoadingEarlier, setIsLoadingEarlier] = useState(false);
  const [hasMoreMessages, setHasMoreMessages] = useState(true);
  const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
  const [insetsColor, setInsetsColor] = useState(Colors.FILL_LIGHT);

  const [text, setText] = useState('');
  const [mentionList, setMentionList] = useState<any>([]);
  const [showMentions, setShowMentions] = useState(false);
  const [inputHeight, setInputHeight] = useState(45);

  const appState = useRef(AppState.currentState);
  const textInputRef = useRef<TextInput>(null);

  const socket = useRef<WebSocket | null>(null);

  const closeModal = () => {
    setModalInfo({ ...modalInfo, visible: false });
  };

  useEffect(() => {
    Audio.setAudioModeAsync({
      allowsRecordingIOS: false,
      staysActiveInBackground: false,
      playsInSilentModeIOS: true,
      shouldDuckAndroid: true,
      playThroughEarpieceAndroid: false
    });
  }, []);

  useEffect(() => {
    if (pinData && pinData?.message) {
      setPinned(pinData.message);
    }
  }, [pinData]);

  const onSendMedia = useCallback(
    async (files: { uri: string; type: 'image' | 'video' }[]) => {
      for (const file of files) {
        const tempMessage: CustomMessage = {
          _id: Date.now() + Math.random(),
          text: '',
          createdAt: new Date(),
          user: { _id: +currentUserId, name: 'Me', avatar: null as never },
          reactions: {},
          deleted: false,
          attachment: {
            id: -1,
            filename: file.type,
            filetype: file.type,
            attachment_link: file.uri,
            attachment_full_url: file.uri
          },
          pending: true,
          isSending: true,
          image: file.type === 'image' ? file.uri : undefined,
          video: file.type === 'video' ? file.uri : undefined
        };

        if (replyMessage) {
          tempMessage.replyMessage = {
            text: replyMessage.text,
            id: replyMessage._id,
            name:
              replyMessage.user._id !== +currentUserId ? (replyMessage.user.name as string) : 'Me'
          };
        }

        setMessages((previousMessages) => GiftedChat.append(previousMessages ?? [], [tempMessage]));

        const messageData = {
          token,
          to_group_token: group_token,
          text: '',
          reply_to_id: replyMessage ? (replyMessage._id as number) : -1,
          attachment: {
            uri: file.uri,
            type: file.type,
            name: file.uri.split('/').pop()
          }
        };

        const res = await sendMessage(messageData, {
          onSuccess: (res) => {
            const { attachment, message_id } = res;

            const newMessage = {
              _id: message_id,
              text: '',
              attachment,
              replyMessage: { ...tempMessage.replyMessage, sender: replyMessage?.user?._id },
              image: file.type === 'image' ? API_HOST + attachment.attachment_full_url : undefined,
              video: file.type === 'video' ? file.uri : undefined
            };

            setMessages((previousMessages) =>
              (previousMessages ?? []).map((msg) =>
                msg._id === tempMessage._id
                  ? {
                      ...msg,
                      _id: res.message_id,
                      isSending: false
                    }
                  : msg
              )
            );

            sendWebSocketMessage('new_message', newMessage as unknown as CustomMessage);
          }
        });

        clearReplyMessage();
      }
    },
    [replyMessage]
  );

  const onSendLocation = useCallback(
    async (coords: { latitude: number; longitude: number }) => {
      const tempMessage: CustomMessage = {
        _id: Date.now() + Math.random(),
        text: '',
        createdAt: new Date(),
        user: { _id: +currentUserId, name: 'Me', avatar: null as never },
        pending: true,
        deleted: false,
        reactions: {},
        attachment: {
          id: -1,
          filename: 'location.json',
          filetype: 'nomadmania/location',
          lat: coords.latitude,
          lng: coords.longitude
        }
      };

      if (replyMessage) {
        tempMessage.replyMessage = {
          text: replyMessage.text,
          id: replyMessage._id,
          name: replyMessage.user._id !== +currentUserId ? (replyMessage.user.name as string) : 'Me'
        };
      }

      setMessages((previousMessages) => GiftedChat.append(previousMessages ?? [], [tempMessage]));

      const locationData = JSON.stringify({ lat: coords.latitude, lng: coords.longitude });
      const fileUri = FileSystem.documentDirectory + 'location.json';
      await FileSystem.writeAsStringAsync(fileUri, locationData);

      const locationFile = {
        uri: fileUri,
        type: 'application/json',
        name: 'location.json'
      };

      const messageData = {
        token,
        to_group_token: group_token,
        text: tempMessage.text,
        reply_to_id: replyMessage ? (replyMessage._id as number) : -1,
        attachment: locationFile
      };

      sendMessage(messageData, {
        onSuccess: async (res) => {
          const { attachment, message_id } = res;

          const newMessage = {
            _id: message_id,
            text: '',
            attachment,
            replyMessage: { ...tempMessage.replyMessage, sender: replyMessage?.user?._id }
          };

          setMessages((previousMessages) =>
            (previousMessages ?? []).map((msg) =>
              msg._id === tempMessage._id ? { ...msg, _id: res.message_id } : msg
            )
          );

          sendWebSocketMessage('new_message', newMessage as unknown as CustomMessage);
          await FileSystem.deleteAsync(fileUri);
        },
        onError: async (err) => {
          await FileSystem.deleteAsync(fileUri);
        }
      });

      clearReplyMessage();
    },
    [replyMessage]
  );

  const onSendFile = useCallback(
    (files: { uri: string; type: string; name?: string }[]) => {
      const newMsgs = files.map((file) => {
        const msg: CustomMessage = {
          _id: Date.now() + Math.random(),
          text: '',
          createdAt: new Date(),
          user: { _id: +currentUserId, name: 'Me', avatar: null as never },
          deleted: false,
          reactions: {},
          isSending: true,
          attachment: {
            id: -1,
            filename: file.name ?? 'File',
            filetype: file.type,
            attachment_link: file.uri
          }
        };

        if (replyMessage) {
          msg.replyMessage = {
            text: replyMessage.text,
            id: replyMessage._id,
            name:
              replyMessage.user._id !== +currentUserId ? (replyMessage.user.name as string) : 'Me'
          };
        }

        if (file.type.includes('image')) {
          msg.image = file.uri;
        } else if (file.type.includes('video')) {
          msg.video = file.uri;
        }

        setMessages((previousMessages) => GiftedChat.append(previousMessages ?? [], [msg]));

        const messageData = {
          token,
          to_group_token: group_token,
          text: '',
          reply_to_id: replyMessage ? (replyMessage._id as number) : -1,
          attachment: {
            uri: file.uri,
            type: file.type,
            name: file.name || file.uri.split('/').pop()
          }
        };

        sendMessage(messageData, {
          onSuccess: (res) => {
            const { attachment, message_id } = res;

            const newMessage = {
              _id: message_id,
              text: '',
              attachment,
              replyMessage: { ...msg.replyMessage, sender: replyMessage?.user?._id },
              image: file.type === 'image' ? API_HOST + attachment.attachment_full_url : undefined,
              video: file.type === 'video' ? file.uri : undefined
            };

            setMessages((previousMessages) =>
              (previousMessages ?? []).map((prevMsg) =>
                prevMsg._id === msg._id
                  ? {
                      ...prevMsg,
                      _id: res.message_id,
                      attachment: res.attachment,
                      isSending: false,
                      image:
                        res.attachment?.attachment_small_url && file.type?.startsWith('image')
                          ? API_HOST + res.attachment.attachment_small_url
                          : undefined,
                      video:
                        res.attachment?.attachment_link && file.type?.startsWith('video')
                          ? API_HOST + res.attachment.attachment_link
                          : undefined
                    }
                  : prevMsg
              )
            );

            sendWebSocketMessage('new_message', newMessage as unknown as CustomMessage);
          }
        });

        return msg;
      });

      clearReplyMessage();
    },
    [replyMessage]
  );

  async function openFileInApp(uri: string, fileName: string) {
    try {
      const dirExist = await FileSystem.getInfoAsync(CACHED_ATTACHMENTS_DIR);
      if (!dirExist.exists) {
        await FileSystem.makeDirectoryAsync(CACHED_ATTACHMENTS_DIR, { intermediates: true });
      }

      const fileUri = `${CACHED_ATTACHMENTS_DIR}${fileName}`;

      const fileExists = await FileSystem.getInfoAsync(fileUri);
      if (fileExists.exists && fileExists.size > 1024) {
        await FileViewer.open(fileUri, {
          showOpenWithDialog: true,
          showAppsSuggestions: true
        });

        return;
      }

      const { uri: localUri } = await FileSystem.downloadAsync(uri, fileUri, {
        headers: { Nmtoken: token, 'App-Version': APP_VERSION, Platform: Platform.OS }
      });

      await FileViewer.open(localUri, {
        showOpenWithDialog: true,
        showAppsSuggestions: true
      });
    } catch (err) {
      console.warn('openFileInApp error:', err);
      Alert.alert('Cannot open file', 'No application found to open this file.');
    }
  }

  async function downloadFileToDevice(currentMessage: CustomMessage) {
    if (!currentMessage.image && !currentMessage.video) {
      return;
    }

    const fileUrl = currentMessage.video
      ? currentMessage.video
      : API_HOST + currentMessage.attachment?.attachment_full_url;
    const fileType = currentMessage.attachment?.filetype || '';
    const fileExt = fileType.split('/').pop() || '';
    const fileName = currentMessage.attachment?.filename?.split('.')[0] || 'file';
    const fileUri = `${FileSystem.cacheDirectory}${fileName}.${fileExt}`;

    try {
      const { status } = await MediaLibrary.requestPermissionsAsync();
      if (status !== 'granted') {
        return;
      }

      const downloadOptions = currentMessage.video
        ? { headers: { Nmtoken: token, 'App-Version': APP_VERSION, Platform: Platform.OS } }
        : undefined;
      const { uri } = await FileSystem.downloadAsync(fileUrl, fileUri, downloadOptions);

      await MediaLibrary.createAssetAsync(uri);

      Alert.alert(
        'Success',
        `${fileType.startsWith('video') ? 'Video' : 'Image'} saved to gallery.`
      );
    } catch (error) {
      Alert.alert('Error', 'Failed to download the file.');
    }
  }
  const renderMessageFile = (props: BubbleProps<CustomMessage>) => {
    const { currentMessage } = props;
    const leftMessage = currentMessage?.user?._id !== +currentUserId;
    if (!currentMessage?.attachment) return null;

    const { attachment_link, filename } = currentMessage.attachment;
    const fileName = filename ?? 'Attachment';
    const uri = API_HOST + attachment_link;

    return (
      <TouchableOpacity
        style={[
          styles.fileContainer,
          { backgroundColor: leftMessage ? 'rgba(15, 63, 79, 0.2)' : 'rgba(244, 244, 244, 0.2)' }
        ]}
        onPress={() => {
          openFileInApp(uri, fileName);
        }}
        onLongPress={() => handleLongPress(currentMessage, props)}
        disabled={currentMessage?.isSending}
      >
        {currentMessage?.isSending ? (
          <ActivityIndicator
            size="small"
            color={leftMessage ? Colors.DARK_BLUE : Colors.FILL_LIGHT}
          />
        ) : (
          <MaterialCommunityIcons
            name="file"
            size={32}
            color={leftMessage ? Colors.DARK_BLUE : Colors.FILL_LIGHT}
          />
        )}
        <Text
          style={[
            styles.fileNameText,
            { color: leftMessage ? Colors.DARK_BLUE : Colors.FILL_LIGHT }
          ]}
        >
          {fileName}
        </Text>
      </TouchableOpacity>
    );
  };

  const renderMessageLocation = (props: BubbleProps<CustomMessage>) => {
    const { currentMessage } = props;
    if (!currentMessage?.attachment) return null;

    const { lat, lng } = currentMessage.attachment;
    if (!lat || !lng) return null;

    return (
      <View
        style={[
          {
            alignItems: 'center',
            borderRadius: 8,
            marginVertical: 6,
            marginHorizontal: 6,
            width: 220
          }
        ]}
      >
        <MessageLocation props={props} lat={lat} lng={lng} onLongPress={handleLongPress} />
      </View>
    );
  };

  const onShareLiveLocation = useCallback(() => {}, []);

  useEffect(() => {
    if (data && data.settings) {
      setCanSeeMembers(data.settings.members_can_see_members === 1 || data.settings.admin === 1);
    }
  }, [data]);

  useEffect(() => {
    let unsubscribe: any;

    const setupNotificationHandler = async () => {
      unsubscribe = await dismissChatNotifications(
        group_token,
        isSubscribed,
        setModalInfo,
        navigation
      );
    };

    setupNotificationHandler();

    return () => {
      if (unsubscribe) unsubscribe();
      updateUnreadMessagesCount();
    };
  }, [group_token]);

  useEffect(() => {
    socket.current = new WebSocket(WEBSOCKET_URL);

    socket.current.onopen = () => {
      socket.current?.send(JSON.stringify({ token }));
    };

    socket.current.onmessage = (event) => {
      const data = JSON.parse(event.data);
      handleWebSocketMessage(data);
    };

    socket.current.onclose = () => {
      console.log('WebSocket connection closed chat screen');
    };

    return () => {
      if (socket.current) {
        socket.current.close();
        socket.current = null;
      }
    };
  }, [token]);

  useEffect(() => {
    const handleAppStateChange = async (nextAppState: AppStateStatus) => {
      const prevState = appState.current;
      appState.current = nextAppState;

      if (prevState.match(/inactive|background/) && nextAppState === 'active') {
        refetch();

        if (!socket.current || socket.current.readyState === WebSocket.CLOSED) {
          socket.current = new WebSocket(WEBSOCKET_URL);
          socket.current.onopen = () => {
            socket.current?.send(JSON.stringify({ token }));
          };
          socket.current.onmessage = (event) => {
            const data = JSON.parse(event.data);
            handleWebSocketMessage(data);
          };
        }

        await dismissChatNotifications(group_token, isSubscribed, setModalInfo, navigation);
      }
    };

    const subscription = AppState.addEventListener('change', handleAppStateChange);

    return () => {
      subscription.remove();
      if (socket.current) {
        socket.current.close();
        socket.current = null;
      }
    };
  }, [token]);

  const handleWebSocketMessage = (data: any) => {
    switch (data.action) {
      case 'new_message':
        if (data.group_token === group_token && data.message) {
          const newMessage = mapApiMessageToGiftedMessage(data.message);
          setMessages((previousMessages) => {
            const messageExists =
              previousMessages && previousMessages.some((msg) => msg._id === newMessage._id);
            if (!messageExists) {
              return GiftedChat.append(previousMessages ?? [], [
                {
                  ...newMessage,
                  user: {
                    _id: data.uid,
                    name: data.name,
                    avatar: API_HOST + data.avatar
                  }
                }
              ]);
            }
            return previousMessages;
          });
        }
        break;

      case 'new_reaction':
        if (data.group_token === group_token && data.reaction) {
          // todo: name
          updateMessageWithReaction(data.reaction);
        }
        break;

      case 'unreact':
        if (data.group_token === group_token && data.unreacted_message_id) {
          // todo: name
          removeReactionFromMessage(data.unreacted_message_id);
        }
        break;

      case 'delete_message':
        if (data.group_token === group_token && data.deleted_message_id) {
          removeDeletedMessage(data.deleted_message_id);
        }
        break;

      case 'is_typing':
        if (data.group_token === group_token && data.uid !== +currentUserId) {
          setIsTyping(data.name);
        }
        break;

      case 'stopped_typing':
        if (data.group_token === group_token) {
          setIsTyping(null);
        }
        break;

      case 'messages_read':
        if (data.group_token === group_token && data.read_messages_ids) {
          setMessages(
            (prevMessages) =>
              prevMessages?.map((msg) => {
                if (data.read_messages_ids.includes(msg._id)) {
                  return { ...msg, received: true };
                }
                return msg;
              }) ?? []
          );
        }
        break;
      case 'messages_received':
        if (data.group_token === group_token && data.received_messages_ids) {
          setMessages(
            (prevMessages) =>
              prevMessages?.map((msg) => {
                if (data.received_messages_ids.includes(msg._id)) {
                  return { ...msg, sent: true };
                }
                return msg;
              }) ?? []
          );
        }
        break;

      default:
        break;
    }
  };

  const updateMessageWithReaction = (reactionData: any) => {
    setMessages(
      (prevMessages) =>
        prevMessages?.map((msg) => {
          if (msg._id === reactionData.message_id) {
            const updatedReactions = [
              ...(Array.isArray(msg.reactions)
                ? msg.reactions?.filter((r: any) => r.uid !== reactionData.uid)
                : []),
              reactionData
            ];
            return { ...msg, reactions: updatedReactions };
          }
          return msg;
        }) ?? []
    );
  };

  const removeReactionFromMessage = (messageId: number) => {
    setMessages(
      (prevMessages) =>
        prevMessages?.map((msg) => {
          if (msg._id === messageId) {
            const updatedReactions = Array.isArray(msg.reactions)
              ? msg.reactions?.filter((r: any) => r.uid === +currentUserId)
              : [];
            return { ...msg, reactions: updatedReactions };
          }
          return msg;
        }) ?? []
    );
  };

  const removeDeletedMessage = (messageId: number) => {
    setMessages(
      (prevMessages) =>
        prevMessages?.map((msg) => {
          if (msg._id === messageId) {
            return {
              ...msg,
              deleted: true,
              text: 'This message was deleted',
              pending: false,
              sent: false,
              received: false
            };
          }
          return msg;
        }) ?? []
    );
  };

  useEffect(() => {
    const pingInterval = setInterval(() => {
      if (socket.current && socket.current.readyState === WebSocket.OPEN) {
        socket.current.send(
          JSON.stringify({ action: 'ping', conversation_with_group: group_token })
        );
      } else {
        socket.current = new WebSocket(WEBSOCKET_URL);
        socket.current.onopen = () => {
          socket.current?.send(JSON.stringify({ token }));
        };
        socket.current.onmessage = (event) => {
          const data = JSON.parse(event.data);
          handleWebSocketMessage(data);
        };

        return () => {
          if (socket.current) {
            socket.current.close();
            socket.current = null;
          }
        };
      }
    }, 50000);

    return () => clearInterval(pingInterval);
  }, []);

  const sendWebSocketMessage = (
    action: string,
    message: CustomMessage | null = null,
    reaction: string | null = null,
    readMessagesIds: number[] | null = null
  ) => {
    if (socket.current && socket.current.readyState === WebSocket.OPEN) {
      const data: any = {
        action,
        conversation_with_group: group_token
      };

      if (action === 'new_message' && message) {
        data.message = {
          id: message._id,
          text: message.text,
          sender: +currentUserId,
          sent_datetime: new Date().toISOString().replace('T', ' ').substring(0, 19),
          reply_to_id: message.replyMessage?.id ?? -1,
          reply_to: message.replyMessage ?? null,
          reactions: message.reactions ?? '{}',
          status: 2,
          attachement: message.attachment ? message.attachment : -1
        };
      }

      if (action === 'new_reaction' && message && reaction) {
        data.reaction = {
          message_id: message._id,
          reaction,
          uid: +currentUserId,
          datetime: new Date().toISOString()
        };
      }

      if (action === 'unreact' && message) {
        data.message_id = message._id;
      }

      if (action === 'delete_message' && message) {
        data.message_id = message._id;
      }

      if (action === 'messages_read' && readMessagesIds) {
        data.messages_ids = readMessagesIds;
      }

      socket.current.send(JSON.stringify(data));
    }
  };

  const handleTyping = (isTyping: boolean) => {
    if (isTyping) {
      sendWebSocketMessage('is_typing');
    } else {
      sendWebSocketMessage('stopped_typing');
    }
  };

  const mapApiMessageToGiftedMessage = (message: GroupMessage): CustomMessage => {
    return {
      _id: message.id,
      text: message.text,
      createdAt: new Date(message.sent_datetime + 'Z'),
      user: {
        _id: message.sender,
        name: message.sender !== +currentUserId ? message.sender_name : 'Me',
        avatar:
          message.sender !== +currentUserId && message.sender_avatar
            ? API_HOST + message.sender_avatar
            : message.sender === +currentUserId
              ? (null as never)
              : undefined
      },
      replyMessage:
        message.reply_to_id && message.reply_to_id > 0
          ? {
              text: message.reply_to.text,
              id: message.reply_to.id,
              name:
                message.reply_to.sender !== +currentUserId ? message.reply_to?.sender_name : 'Me'
            }
          : null,
      reactions: JSON.parse(message.reactions || '{}'),
      attachment: message.attachement !== -1 ? message.attachement : null,
      pending: message.status === 1,
      sent: message.status === 2,
      received: message.status === 3,
      deleted: message.status === 4,
      isSending: false,
      video:
        message.attachement !== -1 && message.attachement?.filetype?.startsWith('video')
          ? API_HOST + message.attachement?.attachment_link
          : null,
      image:
        message.attachement !== -1 && message.attachement?.filetype?.startsWith('image')
          ? API_HOST + message.attachement?.attachment_small_url
          : null,
      system: message.sender === -1
    };
  };

  useFocusEffect(
    useCallback(() => {
      refetch();
    }, [])
  );

  useFocusEffect(
    useCallback(() => {
      if (chatData?.groupAvatar) {
        setGroupAvatar(API_HOST + chatData.groupAvatar);
      }

      if (chatData?.messages) {
        const mappedMessages = chatData.messages.map(mapApiMessageToGiftedMessage);

        if (unreadMessageIndex === null && !isFetching) {
          const firstUnreadIndex = mappedMessages.findLastIndex(
            (msg) => !msg.received && !msg?.deleted && msg.user._id !== +currentUserId
          );

          if (firstUnreadIndex !== -1) {
            setUnreadMessageIndex(firstUnreadIndex);

            const unreadMarker: any = {
              _id: 'unreadMarker',
              text: 'Unread messages',
              system: true
            };

            mappedMessages.splice(firstUnreadIndex + 1, 0, unreadMarker);
            setTimeout(() => {
              if (flatList.current) {
                flatList.current.scrollToIndex({
                  index: firstUnreadIndex,
                  animated: true,
                  viewPosition: 0.5
                });
              }
            }, 500);
          } else {
            setUnreadMessageIndex(0);
          }
        }

        setMessages((previousMessages) => {
          const newMessages = mappedMessages.filter(
            (newMsg) => !previousMessages?.some((oldMsg) => oldMsg._id === newMsg._id)
          );
          return prevThenMessageId !== -1 && previousMessages
            ? GiftedChat.prepend(previousMessages, newMessages)
            : mappedMessages;
        });

        if (mappedMessages.length < 50) {
          setHasMoreMessages(false);
        }

        if (mappedMessages.length === 0 && !modalInfo.visible) {
          setTimeout(() => {
            textInputRef.current?.focus();
          }, 500);
        }

        setIsLoadingEarlier(false);
      }
    }, [chatData])
  );

  useEffect(() => {
    if (messages) {
      if (isSearchingMessage) {
        const messageIndex = messages.findIndex((msg) => msg._id === isSearchingMessage);

        if (messageIndex !== -1 && flatList.current) {
          setIsSearchingMessage(null);
        }
        scrollToMessage(isSearchingMessage);
      }
    }
  }, [messages]);

  useEffect(() => {
    if (messages?.length === 0 && !modalInfo.visible) {
      setTimeout(() => {
        textInputRef.current?.focus();
      }, 500);
    }
  }, [modalInfo]);

  const loadEarlierMessages = async () => {
    if (!hasMoreMessages || (isLoadingEarlier && !isSearchingMessage) || !messages) return;

    setIsLoadingEarlier(true);

    const previousMessageId = messages[messages.length - 1]._id;

    setPrevThenMessageId(previousMessageId);
  };

  const sentToServer = useRef<Set<number>>(new Set());

  const handleViewableItemsChanged = ({ viewableItems }: { viewableItems: any[] }) => {
    const newViewableUnreadMessages = viewableItems
      .filter(
        (item) =>
          !item.item.received &&
          !item.item.deleted &&
          item.item._id !== 'unreadMarker' &&
          item.item.user._id !== +currentUserId &&
          !sentToServer.current.has(item.item._id)
      )
      .map((item) => item.item._id);

    if (newViewableUnreadMessages.length > 0) {
      markMessagesAsRead(
        {
          token,
          group_token,
          messages_id: newViewableUnreadMessages
        },
        {
          onSuccess: (res) => {
            newViewableUnreadMessages.forEach((id) => sentToServer.current.add(id));
            sendWebSocketMessage('messages_read', null, null, newViewableUnreadMessages);
          }
        }
      );
    }
  };

  const renderSystemMessage = (props: any) => {
    if (props.currentMessage._id === 'unreadMarker') {
      return (
        <View style={styles.unreadMessagesContainer}>
          <Text style={styles.unreadMessagesText}>{props.currentMessage.text}</Text>
        </View>
      );
    } else if (props.currentMessage.user._id === -1) {
      return (
        <SystemMessage
          currentMessage={props.currentMessage}
          containerStyle={{
            marginTop: 0,
            marginBottom: 0,
            paddingVertical: 2,
            paddingHorizontal: 12
          }}
          wrapperStyle={{
            backgroundColor: Colors.FILL_LIGHT,
            paddingHorizontal: 6,
            paddingVertical: 4,
            borderRadius: 10
          }}
          textStyle={{
            color: Colors.DARK_BLUE,
            fontStyle: 'italic',
            fontSize: 12,
            textAlign: 'center'
          }}
        />
      );
    }
    return null;
  };

  const clearReplyMessage = () => setReplyMessage(null);

  const handleLongPress = (message: CustomMessage, props: BubbleProps<CustomMessage>) => {
    const messageRef = messageRefs.current[message._id];

    setSelectedMessage(props);
    trigger('impactMedium', options);

    const isMine = message.user._id === +currentUserId;

    if (messageRef) {
      messageRef.measureInWindow((x: number, y: number, width: number, height: number) => {
        const screenHeight = Dimensions.get('window').height;
        const spaceAbove = y - insets.top;
        const spaceBelow = screenHeight - (y + height) - insets.bottom * 2;

        let finalY = y;
        scrollY.value = 0;

        if (isNaN(y) || isNaN(height)) {
          console.error("Invalid measurement values for 'y' or 'height'", { y, height });
          return;
        }

        if (spaceBelow < 220) {
          const extraShift = 220 - spaceBelow;
          finalY -= extraShift;
        }

        if (spaceAbove < 50) {
          const extraShift = 50 - spaceAbove;
          finalY += extraShift;
        }

        if (spaceBelow < 220 || spaceAbove < 50) {
          const targetY = screenHeight / 2 - height / 2;
          scrollY.value = withTiming(finalY - finalY);
        }

        if (height > Dimensions.get('window').height - 200) {
          finalY = 100;
        }

        finalY = isNaN(finalY) ? 0 : finalY;

        setMessagePosition({ x, y: finalY, width, height, isMine });
        setIsModalVisible(true);
      });
    }
  };

  const openEmojiSelector = () => {
    SheetManager.show('emoji-selector');
    trigger('impactLight', options);
  };

  const closeEmojiSelector = () => {
    SheetManager.hide('emoji-selector');
  };

  const handleReactionPress = (emoji: string, messageId: number) => {
    addReaction(messageId, emoji);
  };

  const handleDeleteMessage = (messageId: number) => {
    deleteMessage(
      {
        token,
        message_id: messageId,
        group_token
      },
      {
        onSuccess: () => {
          setMessages(
            (prevMessages) =>
              prevMessages?.map((msg) => {
                if (msg._id === messageId) {
                  return {
                    ...msg,
                    deleted: true,
                    text: 'This message was deleted',
                    pending: false,
                    sent: false,
                    received: false,
                    attachment: null,
                    image: undefined,
                    video: undefined
                  };
                }
                return msg;
              }) ?? []
          );
          const messageToDelete = messages?.find((msg) => msg._id === messageId);
          if (messageToDelete) {
            sendWebSocketMessage('delete_message', messageToDelete, null, null);
          }
        }
      }
    );
  };

  const handlePinMessage = (messageId: number, pin: 0 | 1) => {
    pinMessage(
      {
        token,
        message_id: messageId,
        group_token,
        pin
      },
      {
        onSuccess: () => {
          refetchPinned();
          if (pin === 0) {
            setPinned(null);
          }
        }
      }
    );
  };

  const handleOptionPress = (option: string) => {
    if (!selectedMessage) return;

    switch (option) {
      case 'reply':
        setReplyMessage(selectedMessage.currentMessage);
        setIsModalVisible(false);
        break;
      case 'copy':
        Clipboard.setString(selectedMessage?.currentMessage?.text ?? '');
        setIsModalVisible(false);
        Alert.alert('Copied');
        break;
      case 'delete':
        handleDeleteMessage(selectedMessage.currentMessage?._id);
        setIsModalVisible(false);
        break;
      case 'download':
        downloadFileToDevice(selectedMessage.currentMessage);
        setIsModalVisible(false);
        break;
      case 'info':
        SheetManager.show('group-status', {
          payload: {
            messageId: selectedMessage.currentMessage._id,
            groupToken: group_token,
            setInsetsColor
          } as any
        });
        setIsModalVisible(false);
        setInsetsColor(Colors.WHITE);
        break;
      case 'pin':
        handlePinMessage(selectedMessage.currentMessage?._id, 1);
        setIsModalVisible(false);
        break;
      default:
        break;
    }
    closeEmojiSelector();
  };

  const openReactionList = (
    reactions: { uid: number; name: string; reaction: string }[],
    messageId: number
  ) => {
    SheetManager.show('reactions-list-modal', {
      payload: {
        users: reactions,
        currentUserId: +currentUserId,
        token,
        messageId,
        conversation_with_user: group_token,
        setMessages,
        sendWebSocketMessage,
        isGroup: true,
        groupToken: group_token
      } as any
    });
  };

  const renderTimeContainer = (time: TimeProps<CustomMessage>) => {
    const createdAt = new Date(time.currentMessage.createdAt);

    const formattedTime = createdAt.toLocaleTimeString([], {
      hour: '2-digit',
      minute: '2-digit',
      hour12: true
    });

    const hasReactions =
      time.currentMessage.reactions &&
      Array.isArray(time.currentMessage.reactions) &&
      time.currentMessage.reactions.length > 0;

    return (
      <View
        style={[
          styles.bottomContainer,
          {
            justifyContent: hasReactions ? 'space-between' : 'flex-end'
          }
        ]}
      >
        {hasReactions && (
          <TouchableOpacity
            style={[
              styles.bottomCustomContainer,
              {
                backgroundColor:
                  time.position === 'left' ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.2)'
              }
            ]}
            onPress={() =>
              Array.isArray(time.currentMessage.reactions) &&
              openReactionList(
                time.currentMessage.reactions.map((reaction) => ({
                  ...reaction,
                  name: reaction.uid !== +currentUserId ? reaction?.name : 'Me'
                })),
                time.currentMessage._id
              )
            }
          >
            {Object.entries(
              (Array.isArray(time.currentMessage.reactions)
                ? time.currentMessage.reactions
                : []
              ).reduce(
                (acc: Record<string, { count: number }>, { reaction }: { reaction: string }) => {
                  if (!acc[reaction]) {
                    acc[reaction] = { count: 0 };
                  }
                  acc[reaction].count += 1;
                  return acc;
                },
                {}
              )
            ).map(([emoji, { count }]: any) => {
              return (
                <View key={emoji}>
                  <Text style={{}}>
                    {emoji}
                    {(count as number) > 1 ? ` ${count}` : ''}
                  </Text>
                </View>
              );
            })}
          </TouchableOpacity>
        )}
        <View style={styles.timeContainer}>
          <Text style={styles.timeText}>{formattedTime}</Text>
          {renderTicks(time.currentMessage)}
        </View>
      </View>
    );
  };

  const renderSelectedMessage = () =>
    selectedMessage && (
      <View
        style={{
          maxHeight: '80%',
          width: messagePosition?.width,
          position: 'absolute',
          top: messagePosition?.y,
          left: messagePosition?.x
        }}
      >
        <ScrollView>
          <Bubble
            {...selectedMessage}
            wrapperStyle={{
              right: { backgroundColor: Colors.DARK_BLUE },
              left: { backgroundColor: Colors.FILL_LIGHT }
            }}
            textStyle={{
              right: { color: Colors.WHITE },
              left: { color: Colors.DARK_BLUE }
            }}
            renderTicks={() => null}
            renderTime={renderTimeContainer}
            renderCustomView={() =>
              selectedMessage.currentMessage.attachment?.filetype === 'nomadmania/location'
                ? renderMessageLocation(selectedMessage)
                : selectedMessage.currentMessage.attachment &&
                    !selectedMessage.currentMessage.image &&
                    !selectedMessage.currentMessage.video
                  ? renderMessageFile(selectedMessage)
                  : renderReplyMessageView(selectedMessage)
            }
          />
        </ScrollView>
      </View>
    );

  const handleBackgroundPress = () => {
    setIsModalVisible(false);
    setSelectedMessage(null);
    closeEmojiSelector();
  };

  useFocusEffect(
    useCallback(() => {
      navigation?.getParent()?.setOptions({
        tabBarStyle: {
          display: 'none'
        }
      });
    }, [navigation])
  );

  const onSend = useCallback(
    (newMessages: CustomMessage[] = []) => {
      if (replyMessage) {
        newMessages[0].replyMessage = {
          text: transformMessageForServer(replyMessage.text),
          id: replyMessage._id,
          name: replyMessage.user._id !== +currentUserId ? (replyMessage.user.name as string) : 'Me'
        };
      }
      const user = {
        _id: +currentUserId,
        name: 'Me',
        avatar: null as never
      };
      const message = { ...newMessages[0], pending: true, isSending: true, user };

      setMessages((previousMessages) =>
        GiftedChat.append(previousMessages ?? [], [
          { ...message, text: transformMessageForServer(newMessages[0].text) }
        ])
      );

      sendMessage(
        {
          token,
          to_group_token: group_token,
          text: transformMessageForServer(message.text),
          reply_to_id: replyMessage ? (replyMessage._id as number) : -1
        },
        {
          onSuccess: (res) => {
            const newMessage = {
              _id: res.message_id,
              text: message.text,
              replyMessage: { ...message.replyMessage, sender: replyMessage?.user?._id }
            };

            setMessages((previousMessages) =>
              (previousMessages ?? []).map((msg) =>
                msg._id === message._id ? { ...msg, _id: res.message_id, isSending: false } : msg
              )
            );
            sendWebSocketMessage('new_message', newMessage as unknown as CustomMessage);
          }
        }
      );

      clearReplyMessage();
    },
    [replyMessage]
  );

  const addReaction = (messageId: number, reaction: string) => {
    if (!messages) return;

    const updatedMessages = messages.map((msg: any) => {
      if (msg._id === messageId) {
        const updatedReactions: Reaction[] = [
          ...(Array.isArray(msg.reactions)
            ? msg.reactions?.filter((r: Reaction) => r.uid !== +currentUserId)
            : []),
          { datetime: new Date().toISOString(), reaction: reaction, uid: +currentUserId }
        ];

        return {
          ...msg,
          reactions: updatedReactions
        };
      }
      return msg;
    });

    setMessages(updatedMessages);

    reactToMessage(
      { token, message_id: messageId, reaction: reaction, group_token: group_token },
      {
        onSuccess: () => {
          const message = messages.find((msg) => msg._id === messageId);
          if (message) {
            sendWebSocketMessage('new_reaction', message, reaction);
          }
        }
      }
    );

    setIsModalVisible(false);
  };

  const updateRowRef = useCallback(
    (ref: any) => {
      if (
        ref &&
        replyMessage &&
        ref.props.children.props.currentMessage?._id === replyMessage._id
      ) {
        swipeableRowRef.current = ref;
      }
    },
    [replyMessage]
  );

  const renderReplyMessageView = (props: BubbleProps<CustomMessage>) => {
    if (!props.currentMessage) {
      return null;
    }
    const { currentMessage } = props;

    if (!currentMessage || !currentMessage?.replyMessage) {
      return null;
    }

    return (
      <TouchableOpacity
        style={[
          styles.replyMessageContainer,
          {
            backgroundColor:
              currentMessage.user._id !== +currentUserId
                ? 'rgba(255, 255, 255, 0.7)'
                : 'rgba(0, 0, 0, 0.2)',
            borderColor:
              currentMessage.user._id !== +currentUserId ? Colors.DARK_BLUE : Colors.WHITE
          }
        ]}
        onPress={() => {
          if (currentMessage?.replyMessage?.id) {
            scrollToMessage(currentMessage.replyMessage.id);
          }
        }}
      >
        <View style={styles.replyContent}>
          <Text
            style={[
              styles.replyAuthorName,
              {
                color: currentMessage.user._id !== +currentUserId ? Colors.DARK_BLUE : Colors.WHITE
              }
            ]}
          >
            {currentMessage.replyMessage.name}
          </Text>

          <Text
            numberOfLines={1}
            style={[
              styles.replyMessageText,
              {
                color: currentMessage.user._id !== +currentUserId ? Colors.DARK_BLUE : Colors.WHITE
              }
            ]}
          >
            {currentMessage.replyMessage.text}
          </Text>
        </View>
      </TouchableOpacity>
    );
  };

  const scrollToMessage = (messageId: number) => {
    if (!messages) return;

    const messageIndex = messages.findIndex((message) => message._id === messageId);

    if (messageIndex !== -1 && flatList.current) {
      setIsSearchingMessage(null);

      flatList.current.scrollToIndex({
        index: messageIndex,
        animated: true,
        viewPosition: 0.5
      });

      setHighlightedMessageId(messageId);
      setMessages((previousMessages) =>
        (previousMessages ?? []).map((msg) =>
          msg._id === messageId
            ? {
                ...msg,
                isRendering: msg?.isRendering ? false : true
              }
            : msg
        )
      );
    }

    if (hasMoreMessages && messageIndex === -1) {
      setIsSearchingMessage(messageId);
      loadEarlierMessages();
    }
  };

  useEffect(() => {
    if (highlightedMessageId && isRerendering) {
      setTimeout(() => {
        setHighlightedMessageId(null);
        setIsRerendering(false);
      }, 1500);
    }
  }, [highlightedMessageId, isRerendering]);

  useEffect(() => {
    if (replyMessage && swipeableRowRef.current) {
      swipeableRowRef.current.close();
      swipeableRowRef.current = null;
    }
  }, [replyMessage]);

  const handleOpenImage = async (uri: string, fileName: string) => {
    const dirExist = await FileSystem.getInfoAsync(CACHED_ATTACHMENTS_DIR);
    if (!dirExist.exists) {
      await FileSystem.makeDirectoryAsync(CACHED_ATTACHMENTS_DIR, { intermediates: true });
    }

    const fileUri = `${CACHED_ATTACHMENTS_DIR}${fileName}`;

    const fileExists = await FileSystem.getInfoAsync(fileUri);
    if (fileExists.exists && fileExists.size > 1024) {
      setSelectedMedia(fileUri);

      return;
    }
    setSelectedMedia(uri);

    const { uri: localUri } = await FileSystem.downloadAsync(uri, fileUri, {
      headers: { Nmtoken: token, 'App-Version': APP_VERSION, Platform: Platform.OS }
    });
  };

  const renderMessageImage = (props: any) => {
    const { currentMessage } = props;
    const leftMessage = currentMessage?.user?._id !== +currentUserId;

    return (
      <TouchableOpacity
        onPress={() => {
          if (!currentMessage.attachment.attachment_full_url?.startsWith('/')) {
            setSelectedMedia(currentMessage.attachment.attachment_full_url);
            return;
          }
          handleOpenImage(
            API_HOST + currentMessage.attachment.attachment_full_url,
            currentMessage.attachment?.filename
          );
        }}
        onLongPress={() => handleLongPress(currentMessage, props)}
        style={styles.imageContainer}
        disabled={currentMessage.isSending}
      >
        <Image
          source={{
            uri: currentMessage.image,
            headers: { Nmtoken: token, 'App-Version': APP_VERSION, Platform: Platform.OS }
          }}
          style={styles.chatImage}
          resizeMode="cover"
        />
        {currentMessage.isSending && (
          <View
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              right: 0,
              bottom: 0,
              justifyContent: 'center',
              alignItems: 'center'
            }}
          >
            <ActivityIndicator
              size="large"
              color={leftMessage ? Colors.DARK_BLUE : Colors.FILL_LIGHT}
            />
          </View>
        )}
      </TouchableOpacity>
    );
  };

  const renderTicks = (message: CustomMessage) => {
    if (message.user._id !== +currentUserId) return null;

    if (message.isSending) {
      return (
        <View>
          <ActivityIndicator
            size={16}
            color={Colors.LIGHT_GRAY}
            style={{ transform: 'scale(0.8)' }}
          />
        </View>
      );
    }

    return message.received ? (
      <View>
        <MaterialCommunityIcons name="check-all" size={16} color={Colors.WHITE} />
      </View>
    ) : message.sent ? (
      <View>
        <MaterialCommunityIcons name="check" size={16} color={Colors.WHITE} />
      </View>
    ) : message.pending ? (
      <View>
        <MaterialCommunityIcons name="check" size={16} color={Colors.LIGHT_GRAY} />
      </View>
    ) : null;
  };

  const renderBubble = (props: BubbleProps<CustomMessage>) => {
    const { currentMessage } = props;

    if (currentMessage.deleted) {
      const text = currentMessage.text.length
        ? props.currentMessage.text
        : 'This message was deleted';

      return (
        <View>
          <Bubble
            {...props}
            renderTime={() => null}
            currentMessage={{
              ...props.currentMessage,
              text: text
            }}
            renderMessageText={() => (
              <View style={{ paddingHorizontal: 12, paddingVertical: 6 }}>
                <Text style={{ color: Colors.LIGHT_GRAY, fontStyle: 'italic', fontSize: 12 }}>
                  {text}
                </Text>
              </View>
            )}
            wrapperStyle={{
              right: {
                backgroundColor: Colors.DARK_BLUE
              },
              left: {
                backgroundColor: Colors.FILL_LIGHT
              }
            }}
            textStyle={{
              left: {
                color: Colors.DARK_BLUE
              },
              right: {
                color: Colors.WHITE
              }
            }}
          />
        </View>
      );
    }

    const isHighlighted = currentMessage._id === highlightedMessageId;
    const backgroundColor = isHighlighted
      ? Colors.ORANGE
      : currentMessage.user._id === +currentUserId
        ? Colors.DARK_BLUE
        : Colors.FILL_LIGHT;

    const messageToCompare = props.previousMessage;

    const showUserName =
      props.position === 'left' &&
      currentMessage &&
      messageToCompare &&
      (!isSameUser(currentMessage, messageToCompare) ||
        !isSameDay(currentMessage, messageToCompare));

    return (
      <View
        key={`${currentMessage._id}-${isHighlighted ? 'highlighted' : 'normal'}`}
        ref={(ref) => {
          if (ref && currentMessage) {
            messageRefs.current[currentMessage._id] = ref;
          }
        }}
        collapsable={false}
      >
        <Bubble
          {...props}
          wrapperStyle={{
            right: {
              backgroundColor: backgroundColor
            },
            left: {
              backgroundColor: backgroundColor
            }
          }}
          textStyle={{
            left: {
              color: Colors.DARK_BLUE
            },
            right: {
              color: Colors.FILL_LIGHT
            }
          }}
          onLongPress={() => handleLongPress(currentMessage, props)}
          renderTicks={() => null}
          renderTime={renderTimeContainer}
          renderCustomView={() => {
            return (
              <View>
                {showUserName ? (
                  <Text
                    style={{
                      color: Colors.BLACK,
                      fontWeight: '600',
                      fontSize: 13,
                      paddingHorizontal: 10,
                      paddingTop: 8,
                      paddingBottom: 2
                    }}
                  >
                    {/* {'~ '} */}
                    {props.currentMessage.user.name}
                  </Text>
                ) : null}
                {currentMessage.attachment?.filetype === 'nomadmania/location'
                  ? renderMessageLocation(props)
                  : currentMessage.attachment && !currentMessage.image && !currentMessage.video
                    ? renderMessageFile(props)
                    : renderReplyMessageView(props)}
              </View>
            );
          }}
        />
      </View>
    );
  };

  const openAttachmentsModal = () => {
    SheetManager.show('chat-attachments', {
      payload: {
        name: groupName,
        uid: group_token,
        setModalInfo,
        closeOptions: () => {},
        onSendMedia,
        onSendLocation,
        onShareLiveLocation,
        onSendFile,
        isGroup: true
      } as any
    });
  };

  const renderInputToolbar = (props: any) => {
    if (!chatData?.can_send_messages) return null;

    return (
      <>
        {showMentions && canSeeMembers ? (
          <MentionsList
            mentionList={mentionList}
            inputHeight={inputHeight}
            onMentionSelect={onMentionSelect}
          />
        ) : null}
        <View
          onLayout={(e) => {
            setInputHeight(e.nativeEvent.layout.height);
          }}
        >
          <InputToolbar
            {...props}
            renderActions={() =>
              userType === 'normal' ? (
                <Actions
                  icon={() => (
                    <MaterialCommunityIcons name="plus" size={28} color={Colors.DARK_BLUE} />
                  )}
                  onPressActionButton={openAttachmentsModal}
                />
              ) : null
            }
            containerStyle={{
              backgroundColor: Colors.FILL_LIGHT
            }}
          />
        </View>
      </>
    );
  };

  const renderScrollToBottom = () => {
    return (
      <TouchableOpacity
        style={styles.scrollToBottom}
        onPress={() => {
          if (flatList.current) {
            flatList.current.scrollToIndex({ index: 0, animated: true });
          }
        }}
      >
        <MaterialCommunityIcons name="chevron-down" size={24} color={Colors.WHITE} />
      </TouchableOpacity>
    );
  };

  const shouldUpdateMessage = (
    props: MessageProps<IMessage>,
    nextProps: MessageProps<IMessage>
  ) => {
    setIsRerendering(true);
    const currentId = nextProps.currentMessage._id;
    return currentId === highlightedMessageId;
  };

  const onInputTextChanged = (value: string) => {
    handleTyping(value.length > 0);

    setText(value);

    const mentionMatch = value.match(/(^|\s)(@\w*)$/);

    if (mentionMatch) {
      setShowMentions(true);
      const searchText = mentionMatch[2].slice(1).toLowerCase();
      setMentionList(
        (members?.settings ?? [])?.filter(
          (m) => m.name.toLowerCase().includes(searchText) && m.uid !== +currentUserId
        )
      );
    } else {
      setShowMentions(false);
    }
  };

  const onMentionSelect = (member: { uid: number; name: string }) => {
    const words = text.split(' ');
    words[words.length - 1] = `@${member.name} `;
    setText(words.join(' '));
    setShowMentions(false);
  };

  const transformMessageForServer = (text: string) => {
    let transformedText = text;
    members?.settings?.forEach((member) => {
      const mentionRegex = new RegExp(`@${member.name}\\b`, 'g');
      transformedText = transformedText.replace(mentionRegex, `@{${member.uid}}`);
    });
    return transformedText;
  };

  return (
    <SafeAreaView
      edges={['top']}
      style={{
        height: '100%'
      }}
    >
      <View style={{ paddingHorizontal: '5%' }}>
        <Header
          label={groupName}
          textColor={userType !== 'normal' ? Colors.RED : Colors.DARK_BLUE}
          rightElement={
            <TouchableOpacity
              onPress={() =>
                navigation.navigate(
                  ...([NAVIGATION_PAGES.GROUP_SETTINGS, { groupToken: group_token }] as never)
                )
              }
              disabled={userType !== 'normal'}
            >
              {groupAvatar && userType === 'normal' ? (
                <Image source={{ uri: groupAvatar, cache: 'reload' }} style={styles.avatar} />
              ) : userType === 'normal' ? (
                <GroupIcon fill={Colors.DARK_BLUE} width={30} height={30} />
              ) : (
                <BanIcon fill={Colors.RED} width={30} height={30} />
              )}
            </TouchableOpacity>
          }
        />
      </View>
      {pinned && (
        <TouchableOpacity
          style={{
            height: 38,
            flexDirection: 'row',
            backgroundColor: Colors.FILL_LIGHT,
            borderBottomWidth: 1,
            borderBottomColor: Colors.DARK_LIGHT
          }}
          onPress={() => scrollToMessage(pinned.id)}
        >
          <View
            style={{
              height: 50,
              width: 6,
              backgroundColor: Colors.DARK_BLUE
            }}
          ></View>
          <View
            style={{
              paddingLeft: 8,
              height: '100%',
              justifyContent: 'center'
            }}
          >
            <PinIcon fill={Colors.DARK_BLUE} height={18} />
          </View>

          <View style={{ flex: 1, justifyContent: 'center' }}>
            <Text style={{ color: Colors.DARK_BLUE, paddingLeft: 10 }} numberOfLines={1}>
              {pinned.text}
            </Text>
          </View>

          {data?.settings?.admin === 1 && (
            <View style={{ alignItems: 'flex-end', justifyContent: 'center' }}>
              <TouchableOpacity
                style={{ paddingRight: 10 }}
                onPress={() => handlePinMessage(pinned.id, 0)}
              >
                <MaterialCommunityIcons
                  name="close-circle-outline"
                  size={24}
                  color={Colors.DARK_BLUE}
                />
              </TouchableOpacity>
            </View>
          )}
        </TouchableOpacity>
      )}

      <GestureHandlerRootView style={styles.container}>
        {messages ? (
          <GiftedChat
            messages={messages as CustomMessage[]}
            text={text}
            listViewProps={{
              ref: flatList,
              showsVerticalScrollIndicator: false,
              initialNumToRender: messages.length,
              onViewableItemsChanged: handleViewableItemsChanged,
              viewabilityConfig: { itemVisiblePercentThreshold: 50 },
              onScrollToIndexFailed: (info: any) => {
                const wait = new Promise((resolve) => setTimeout(resolve, 300));
                wait.then(() => {
                  flatList.current?.scrollToIndex({
                    index: info.index,
                    animated: true,
                    viewPosition: 0.5
                  });
                });
              }
            }}
            renderSystemMessage={renderSystemMessage}
            onSend={(newMessages: CustomMessage[]) => onSend(newMessages)}
            user={{ _id: +currentUserId, name: 'Me' }}
            renderBubble={renderBubble}
            renderMessageImage={renderMessageImage}
            showUserAvatar={true}
            onPressAvatar={(user) => {
              navigation.navigate(
                ...([NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: user._id }] as never)
              );
            }}
            renderInputToolbar={renderInputToolbar}
            renderCustomView={renderReplyMessageView}
            isCustomViewBottom={false}
            messageContainerRef={messageContainerRef}
            minComposerHeight={34}
            onInputTextChanged={onInputTextChanged}
            textInputRef={textInputRef}
            isTyping={isTyping ? true : false}
            renderTypingIndicator={() => <TypingIndicator isTyping={isTyping} />}
            renderSend={(props) => (
              <View style={styles.sendBtn}>
                {props.text?.trim() && (
                  <Send
                    {...props}
                    containerStyle={{
                      justifyContent: 'center'
                    }}
                  >
                    <SendIcon fill={Colors.DARK_BLUE} />
                  </Send>
                )}
                {!props.text?.trim() && <SendIcon fill={Colors.LIGHT_GRAY} />}
              </View>
            )}
            renderMessageVideo={(props) => (
              <RenderMessageVideo
                props={props}
                token={token}
                currentUserId={+currentUserId}
                onLongPress={handleLongPress}
              />
            )}
            textInputProps={{
              ...styles.composer,
              selectionColor: Colors.LIGHT_GRAY
            }}
            placeholder=""
            renderMessage={(props) => (
              <ChatMessageBox
                {...(props as MessageProps<CustomMessage>)}
                updateRowRef={updateRowRef}
                setReplyOnSwipeOpen={setReplyMessage}
              />
            )}
            renderChatFooter={() => (
              <ReplyMessageBar clearReply={clearReplyMessage} message={replyMessage} />
            )}
            maxComposerHeight={100}
            renderComposer={(props) => <Composer {...props} />}
            keyboardShouldPersistTaps="handled"
            renderChatEmpty={() => (
              <View style={styles.emptyChat}>
                <Text
                  style={styles.emptyChatText}
                >{`No messages yet.\nFeel free to start the conversation.`}</Text>
              </View>
            )}
            shouldUpdateMessage={shouldUpdateMessage}
            scrollToBottom={true}
            scrollToBottomComponent={renderScrollToBottom}
            scrollToBottomStyle={{ backgroundColor: 'transparent' }}
            parsePatterns={(linkStyle) => [
              {
                type: 'url',
                style: { color: Colors.ORANGE, textDecorationLine: 'underline' },
                onPress: (url: string) => Linking.openURL(url),
                onLongPress: (url: string) => {
                  Clipboard.setString(url ?? '');
                  Alert.alert('Link copied');
                }
              },
              {
                pattern: /@\{(\d+)\}/g,
                renderText: (messageText: string) => {
                  const tagId = messageText.slice(2, messageText.length - 1);
                  const user = (members?.settings ?? [])?.find((m) => m.uid === +tagId);

                  if (user) {
                    return (
                      <Text
                        style={{ color: Colors.ORANGE }}
                        onPress={() =>
                          navigation.navigate(
                            ...([
                              NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW,
                              { userId: user.uid }
                            ] as never)
                          )
                        }
                      >
                        @{user.name}
                      </Text>
                    );
                  } else {
                    return messageText;
                  }
                }
              }
            ]}
            infiniteScroll={true}
            loadEarlier={hasMoreMessages}
            isLoadingEarlier={isLoadingEarlier}
            onLoadEarlier={loadEarlierMessages}
            renderLoadEarlier={() => (
              <View style={{ paddingVertical: 20 }}>
                <ActivityIndicator size="small" color={Colors.DARK_BLUE} />
              </View>
            )}
          />
        ) : (
          <ActivityIndicator size="large" color={Colors.DARK_BLUE} />
        )}

        <ImageView
          images={[
            {
              uri: selectedMedia,
              cache: 'force-cache',
              headers: { Nmtoken: token, 'App-Version': APP_VERSION, Platform: Platform.OS }
            }
          ]}
          imageIndex={0}
          visible={!!selectedMedia}
          onRequestClose={() => setSelectedMedia(null)}
          backgroundColor={Colors.DARK_BLUE}
        />

        <ReactModal
          isVisible={isModalVisible}
          onBackdropPress={handleBackgroundPress}
          style={styles.reactModalContainer}
          animationIn="fadeIn"
          animationOut="fadeOut"
          useNativeDriver
          backdropColor="transparent"
        >
          <BlurView
            intensity={80}
            style={styles.modalBackground}
            experimentalBlurMethod="dimezisBlurView"
          >
            <TouchableOpacity
              style={styles.modalBackground}
              activeOpacity={1}
              onPress={handleBackgroundPress}
            >
              <ReactionBar
                messagePosition={messagePosition}
                selectedMessage={selectedMessage}
                reactionEmojis={reactionEmojis}
                handleReactionPress={handleReactionPress}
                openEmojiSelector={openEmojiSelector}
              />
              {renderSelectedMessage()}
              <OptionsMenu
                selectedMessage={selectedMessage}
                handleOptionPress={handleOptionPress}
                messagePosition={messagePosition}
                isGroup={true}
                isAdmin={data?.settings?.admin == 1}
              />
              <EmojiSelectorModal
                visible={emojiSelectorVisible}
                selectedMessage={selectedMessage}
                addReaction={addReaction}
                closeEmojiSelector={closeEmojiSelector}
              />
            </TouchableOpacity>
          </BlurView>
        </ReactModal>

        <WarningModal
          isVisible={modalInfo.visible}
          onClose={closeModal}
          type={modalInfo.type}
          message={modalInfo.message}
          buttonTitle={modalInfo.buttonTitle}
          title={modalInfo.title}
          action={() => {
            modalInfo.action();
            closeModal();
          }}
        />
        <AttachmentsModal />
        <ReactionsListModal />
        <GroupStatusModal />
      </GestureHandlerRootView>
      <View
        style={{
          height: insets.bottom,
          backgroundColor: insetsColor
        }}
      />
    </SafeAreaView>
  );
};

export default GroupChatScreen;
