import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  Image,
  Platform,
  TouchableHighlight,
  AppState,
  AppStateStatus,
  Keyboard,
  Dimensions
} from 'react-native';
import { AvatarWithInitials, HorizontalTabView, Input, WarningModal } from 'src/components';
import { NAVIGATION_PAGES } from 'src/types';
import { useFocusEffect, useNavigation } from '@react-navigation/native';

import AddChatIcon from 'assets/icons/messages/chat-plus.svg';
import { API_HOST, WEBSOCKET_URL } from 'src/constants';
import { Colors } from 'src/theme';
import SwipeableRow from './Components/SwipeableRow';
import { FlashList } from '@shopify/flash-list';
import * as Notifications from 'expo-notifications';

import ReadIcon from 'assets/icons/messages/check-read.svg';
import UnreadIcon from 'assets/icons/messages/check-unread.svg';
import SearchModal from './Components/SearchUsersModal';
import { SheetManager } from 'react-native-actions-sheet';
import MoreModal from './Components/MoreModal';
import SearchIcon from 'assets/icons/search.svg';
import { storage, StoreType } from 'src/storage';
import { Blocked, Chat } from './types';

import PinIcon from 'assets/icons/messages/pin.svg';
import { formatDate } from './utils';
import { routes } from './constants';
import { styles } from './styles';
import { useChatStore } from 'src/stores/chatStore';
import BellSlashIcon from 'assets/icons/messages/bell-slash.svg';
import BanIcon from 'assets/icons/messages/ban.svg';
import SwipeableBlockedRow from './Components/SwipeableBlockedRow';
import { useMessagesStore } from 'src/stores/unreadMessagesStore';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import GroupIcon from 'assets/icons/messages/group-chat.svg';
import { usePushNotification } from 'src/contexts/PushNotificationContext';
import { useChatsListLive } from 'src/watermelondb/features/chat/hooks/useChatsList';
import { syncChatsIncremental } from 'src/watermelondb/features/chat/data/chat.sync';
import { useBlockedUsersLive } from 'src/watermelondb/features/chat/hooks/useBlockedUsersLive';
import { useMessageSearch } from 'src/watermelondb/features/chat/hooks/useMessagesSearch';

export function highlightText(text: string, query: string, highlightStyle: any) {
  if (!query.trim()) return <Text>{text}</Text>;

  const lowerText = text.toLowerCase();
  const lowerQuery = query.toLowerCase();

  const parts: React.ReactNode[] = [];
  let lastIndex = 0;

  let index = lowerText.indexOf(lowerQuery);

  while (index !== -1) {
    if (index > lastIndex) {
      parts.push(text.slice(lastIndex, index));
    }

    parts.push(
      <Text key={index} style={highlightStyle}>
        {text.slice(index, index + query.length)}
      </Text>
    );

    lastIndex = index + query.length;
    index = lowerText.indexOf(lowerQuery, lastIndex);
  }

  if (lastIndex < text.length) {
    parts.push(text.slice(lastIndex));
  }

  return <Text>{parts}</Text>;
}

const TypingIndicator = ({ name }: { name?: string }) => {
  const [dots, setDots] = useState('');

  useEffect(() => {
    const interval = setInterval(() => {
      setDots((prevDots) => {
        if (prevDots.length >= 3) {
          return '';
        }
        return prevDots + '.';
      });
    }, 500);

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

  return name ? (
    <Text style={styles.typingText}>
      {name} is typing{dots}
    </Text>
  ) : (
    <Text style={styles.typingText}>Typing{dots}</Text>
  );
};

const MessagesScreen = () => {
  const insets = useSafeAreaInsets();
  const navigation = useNavigation<any>();
  const token = storage.get('token', StoreType.STRING) as string;
  const [index, setIndex] = useState(0);
  const [contentIndex, setContentIndex] = useState(0);
  const [cacheKey, setCacheKey] = useState(Date.now());
  const updateUnreadMessagesCount = useMessagesStore((state) => state.updateUnreadMessagesCount);
  const { isSubscribed } = usePushNotification();
  const [isBannerVisible, setIsBannerVisible] = useState(false);

  const currentUserId = storage.get('uid', StoreType.STRING) as string;

  const [filteredChats, setFilteredChats] = useState<{
    all: Chat[];
    unread: Chat[];
    archived: Chat[];
    blocked: Blocked[];
  }>({ all: [], unread: [], archived: [], blocked: [] });
  const [search, setSearch] = useState('');
  const openRowRef = useRef<any>(null);
  const { isWarningModalVisible, setIsWarningModalVisible } = useChatStore();
  const [typingUsers, setTypingUsers] = useState<
    { [key: string]: boolean } | { [key: string]: { firstName: string; isTyping: boolean } }
  >({});

  const chats = useChatsListLive({ archived: index === 2 ? 1 : 0 });
  const blocked = useBlockedUsersLive();

  const appState = useRef(AppState.currentState);

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

  const [debouncedSearch, setDebouncedSearch] = useState('');

  useEffect(() => {
    const t = setTimeout(() => {
      setDebouncedSearch(search);
    }, 300);

    return () => clearTimeout(t);
  }, [search]);

  const initializeSocket = () => {
    if (socket.current) {
      socket.current.close();
    }

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

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

      socket.current.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);
          handleWebSocketMessage(data);
        } catch {
          console.log('Invalid WS message:', event.data);
        }
      };

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

  useFocusEffect(
    useCallback(() => {
      setCacheKey(Date.now());
    }, [navigation])
  );

  useFocusEffect(
    useCallback(() => {
      const checkNotificationPermission = async () => {
        const { status } = await Notifications.getPermissionsAsync();
        if (status !== 'granted' || !isSubscribed) {
          setIsBannerVisible(true);
        } else {
          setIsBannerVisible(false);
        }
      };

      checkNotificationPermission();
    }, [isSubscribed])
  );

  useEffect(() => {
    const handleAppStateChange = (nextAppState: AppStateStatus) => {
      if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
        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) => {
            try {
              const data = JSON.parse(event.data);
              handleWebSocketMessage(data);
            } catch {
              console.log('Invalid WS message:', event.data);
            }
          };
        }
      }
    };

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

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

  useEffect(() => {
    const pingInterval = setInterval(() => {
      if (socket.current && socket.current.readyState === WebSocket.OPEN) {
        socket.current.send(JSON.stringify({ action: 'ping', conversation_with: 0 }));
      } else {
        initializeSocket();

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

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

  const handleWebSocketMessage = async (data: any) => {
    switch (data.action) {
      case 'new_message':
      case 'messages_read':
        await syncChatsIncremental(token);
        break;
      case 'is_typing':
        if (data.conversation_with) {
          setTypingUsers((prev) => ({
            ...prev,
            [data.conversation_with]: true
          }));
        } else if (data.group_token) {
          setTypingUsers((prev) => ({
            ...prev,
            [data.group_token]: {
              isTyping: true,
              firstName: data.name?.split(' ')[0]
            }
          }));
        }
        break;
      case 'stopped_typing':
        if (data.conversation_with) {
          setTypingUsers((prev) => ({
            ...prev,
            [data.conversation_with]: false
          }));
        } else if (data.group_token) {
          setTypingUsers((prev) => ({
            ...prev,
            [data.group_token]: false
          }));
        }
        break;
      default:
        break;
    }
  };

  const handleRowOpen = (ref: any) => {
    if (openRowRef.current && openRowRef.current !== ref) {
      openRowRef.current.close();
    }
    openRowRef.current = ref;
  };

  useFocusEffect(
    useCallback(() => {
      const isKeyboardVisible = Keyboard.isVisible();
      if (isKeyboardVisible) {
        Keyboard.dismiss();
      }
      navigation.getParent()?.setOptions({
        tabBarStyle: {
          display: 'flex',
          ...Platform.select({
            android: {
              // height: 58
            }
          })
        }
      });
    }, [])
  );

  useFocusEffect(
    useCallback(() => {
      (async () => {
        await syncChatsIncremental(token);
      })();
      initializeSocket();
      updateUnreadMessagesCount();

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

  const getFilteredChats = (key: keyof typeof filteredChats) => {
    if (key === 'blocked') return blocked;

    let list = chats;

    if (key === 'unread') {
      list = list.filter((c) => c.unreadCount > 0);
    }

    if (key === 'archived') {
      list = list.filter((c) => c.archive === 1);
    }

    return list.slice().sort((a, b) => {
      if (b.pin - a.pin !== 0) return b.pin - a.pin;
      if (b.pinOrder - a.pinOrder !== 0) return b.pinOrder - a.pinOrder;
      return new Date(b.updated).getTime() - new Date(a.updated).getTime();
    });
  };

  const filteredChatsForCurrentTab = useMemo(() => {
    return getFilteredChats(routes[index].key);
  }, [index, chats]);

  type ChatSearchResult =
    | {
        type: 'chat';
        chat: Chat;
      }
    | {
        type: 'message';
        chat: Chat;
        messageId: number;
        messageText: string;
        messageTime: number;
      };

  const messageSearchResults = useMessageSearch(debouncedSearch);

  const searchedChats: ChatSearchResult[] = useMemo(() => {
    if (!debouncedSearch.trim()) {
      return filteredChatsForCurrentTab.map((chat) => ({
        type: 'chat',
        chat
      }));
    }

    const q = debouncedSearch.toLowerCase();
    const results: ChatSearchResult[] = [];

    for (const chat of filteredChatsForCurrentTab) {
      if (chat.name?.toLowerCase().includes(q) || chat.short?.toLowerCase().includes(q)) {
        results.push({
          type: 'chat',
          chat
        });
      }
    }

    for (const m of messageSearchResults) {
      const chat = filteredChatsForCurrentTab.find((c) => {
        const key = c.groupChatToken ? `g:${c.groupChatToken}` : `u:${c.chatUid}`;
        return key === m.chatKey;
      });

      if (!chat) continue;

      results.push({
        type: 'message',
        chat,
        messageId: m.messageId,
        messageText: m.text,
        messageTime: m.sentAt
      });
    }

    return results;
  }, [filteredChatsForCurrentTab, debouncedSearch, messageSearchResults]);

  const searchFilter = (text: string) => {
    if (text) {
      setSearch(text);
    } else {
      setSearch(text);
    }
  };

  const renderChatItem = ({ item }: { item: ChatSearchResult }) => {
    const name =
      item.chat.userType === 'blocked'
        ? 'Account is blocked'
        : item.chat.userType === 'not_exist'
          ? 'Account does not exist'
          : item.chat.name;

    const previewText = item.type === 'message' ? item.messageText : item.chat.short;

    const previewTime = item.type === 'message' ? item.messageTime : item.chat.updated;

    return (
      <SwipeableRow
        chat={{
          uid: item.chat.chatUid,
          groupToken: item.chat.groupChatToken,
          name: item.chat.name,
          avatar: item.chat.avatar,
          pin: item.chat.pin,
          archive: item.chat.archive,
          muted: item.chat.muted,
          userType: item.chat.userType ?? 'normal',
          announcement: item.chat.announcement
        }}
        token={token}
        onRowOpen={handleRowOpen}
      >
        <TouchableHighlight
          key={
            item.chat.chatUid
              ? `${item.chat.chatUid}-${typingUsers[item.chat.chatUid]}`
              : `${item.chat.groupChatToken}-${typingUsers[item.chat.groupChatToken ?? '']}`
          }
          activeOpacity={0.8}
          onPress={() => {
            navigation.navigate(
              item.chat.groupChatToken ? NAVIGATION_PAGES.GROUP_CHAT : NAVIGATION_PAGES.CHAT,
              {
                id: item.chat.chatUid,
                group_token: item.chat.groupChatToken,
                name: item.chat.name,
                avatar: item.chat.avatar,
                userType: item.chat.userType ?? 'normal',
                announcement: item.chat.announcement,
                canSendMessages: item.chat?.canSendMessages ?? 1,
                scrollToMessageId: item?.messageId ?? null,
              }
            );
          }}
          underlayColor={Colors.FILL_LIGHT}
        >
          <View style={styles.chatItem}>
            {item.chat.avatar && (item.chat.userType === 'normal' || !item.chat.userType) ? (
              <Image
                source={{
                  uri: item.chat.groupChatToken
                    ? `${API_HOST}${item.chat.avatar}?cacheBust=${cacheKey}`
                    : `${API_HOST}${item.chat.avatar}`
                }}
                style={styles.avatar}
                onError={(e) => console.warn('Image error', e.nativeEvent)}
              />
            ) : item.chat.chatUid && (item.chat.userType === 'normal' || !item.chat.userType) ? (
              <AvatarWithInitials
                text={
                  item.chat.name
                    ?.split(/ (.+)/)
                    .map((n) => n[0])
                    .join('') ?? ''
                }
                flag={API_HOST + item.chat?.flag}
                size={54}
              />
            ) : item.chat.userType === 'normal' || !item.chat.userType ? (
              <GroupIcon fill={Colors.DARK_BLUE} width={54} height={54} />
            ) : (
              <BanIcon fill={Colors.RED} width={54} height={54} />
            )}

            <View style={{ flex: 1, gap: 6 }}>
              <View style={[styles.rowContainer, { alignItems: 'center' }]}>
                <Text
                  style={[
                    styles.chatName,
                    item.chat.userType === 'not_exist' || item.chat.userType === 'blocked'
                      ? { color: Colors.RED }
                      : {}
                  ]}
                >
                  {highlightText(name, debouncedSearch, { backgroundColor: '#FFE58A' })}
                </Text>

                <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
                  {item.chat.pin === 1 ? <PinIcon height={12} fill={Colors.DARK_BLUE} /> : null}
                  {item.chat.muted === 1 ? (
                    <BellSlashIcon height={12} fill={Colors.DARK_BLUE} />
                  ) : null}

                  {item.chat.sentBy === +currentUserId && item.chat.status === 3 ? (
                    <ReadIcon fill={Colors.DARK_BLUE} />
                  ) : item.chat.sentBy === +currentUserId &&
                    (item.chat.status === 2 || item.chat.status === 1) ? (
                    <UnreadIcon fill={Colors.LIGHT_GRAY} />
                  ) : null}
                  <Text style={styles.chatTime}>{formatDate(previewTime)}</Text>
                </View>
              </View>

              <View style={[styles.rowContainer, { flex: 1, gap: 6 }]}>
                {item.chat.chatUid && typingUsers[item.chat.chatUid] ? (
                  <TypingIndicator />
                ) : item.chat.groupChatToken &&
                  typingUsers[item.chat.groupChatToken] &&
                  (typingUsers[item.chat.groupChatToken] as any)?.firstName ? (
                  <TypingIndicator
                    name={(typingUsers[item.chat.groupChatToken] as any).firstName}
                  />
                ) : (
                  <Text numberOfLines={2} style={styles.chatMessage}>
                    {item.chat.attachementName && item.chat.attachementName.length
                      ? item.chat.attachementName
                      : highlightText(previewText, debouncedSearch, { backgroundColor: '#FFE58A' })}
                  </Text>
                )}

                {item.chat.unreadCount > 0 ? (
                  <View style={styles.unreadBadge}>
                    <Text style={styles.unreadText}>
                      {item.chat.unreadCount > 99 ? '99+' : item.chat.unreadCount}
                    </Text>
                  </View>
                ) : null}
              </View>
            </View>
          </View>
        </TouchableHighlight>
      </SwipeableRow>
    );
  };

  const renderBlockedItem = ({ item }: { item: Blocked }) => {
    return (
      <SwipeableBlockedRow
        data={{
          userId: item.userId,
          firstName: item.firstName,
          lastName: item.lastName,
          avatar: item.avatar
        }}
        token={token}
        onRowOpen={handleRowOpen}
      >
        <TouchableHighlight
          activeOpacity={0.8}
          onPress={() =>
            navigation.navigate(NAVIGATION_PAGES.PUBLIC_PROFILE_VIEW, { userId: item.userId })
          }
          underlayColor={Colors.FILL_LIGHT}
        >
          <View style={[styles.chatItem, { alignItems: 'center' }]}>
            {item.avatar ? (
              <Image
                source={{ uri: API_HOST + item.avatar }}
                style={[styles.avatar, { width: 30, height: 30, borderRadius: 15 }]}
              />
            ) : (
              <AvatarWithInitials
                text={item.firstName[0] + item.lastName[0]}
                flag={API_HOST + item?.flag}
                size={32}
                fontSize={12}
              />
            )}

            <View style={{ flex: 1, gap: 6 }}>
              <View style={[styles.rowContainer, { alignItems: 'center' }]}>
                <Text style={styles.chatName}>{item.firstName + ' ' + item.lastName}</Text>

                <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
                  <BanIcon height={12} fill={Colors.RED} />
                </View>
              </View>
            </View>
          </View>
        </TouchableHighlight>
      </SwipeableBlockedRow>
    );
  };

  const SEARCH_HEIGHT = 38;
  const TABS_HEIGHT = 50;
  const TOP_HEIGHT = SEARCH_HEIGHT + TABS_HEIGHT;

  const SearchHeader = useMemo(
    () => (
      <View style={{ backgroundColor: Colors.WHITE, height: TOP_HEIGHT, gap: 12 }}>
        <View
          style={{
            height: SEARCH_HEIGHT,
            justifyContent: 'center',
            paddingHorizontal: '4%'
          }}
        >
          <Input
            inputMode="search"
            placeholder="Search"
            value={search}
            onChange={setSearch}
            icon={<SearchIcon fill="#C8C8C8" width={14} height={14} />}
            height={38}
          />
        </View>

        <View style={{ backgroundColor: Colors.RED }}>
          <HorizontalTabView
            index={index}
            setIndex={() => {}}
            onTabPress={(i: number) => {
              setIndex(i);
              setContentIndex(i);
            }}
            routes={routes}
            renderScene={({ route }: { route: any }) => <View style={{ height: 0 }} />}
          />
        </View>
      </View>
    ),
    [search, index]
  );

  const activeRouteKey = routes[contentIndex].key;

  const listData = useMemo(() => {
    switch (activeRouteKey) {
      case 'blocked':
        return blocked;

      default:
        return searchedChats;
    }
  }, [activeRouteKey, blocked, searchedChats]);

  const renderItem = useCallback(
    ({ item }: any) => {
      if (activeRouteKey === 'blocked') {
        return renderBlockedItem({ item });
      }

      return renderChatItem({ item });
    },
    [activeRouteKey, typingUsers, debouncedSearch]
  );

  const extraData = useMemo(() => ({ typingUsers }), [typingUsers]);

  return (
    <View style={{ paddingTop: insets.top, flex: 1, marginLeft: 0, marginRight: 0, gap: 12 }}>
      <View style={styles.header}>
        <View style={{ width: 30 }} />
        <Text style={styles.title}>Messages</Text>
        <TouchableOpacity
          onPress={() => SheetManager.show('search-modal')}
          style={{ width: 30, alignItems: 'flex-end' }}
        >
          <AddChatIcon />
        </TouchableOpacity>
      </View>
      {isBannerVisible && (
        <View
          style={{
            flexDirection: 'row',
            alignItems: 'center',
            justifyContent: 'space-between',
            paddingHorizontal: '5%',
            paddingVertical: 6,
            backgroundColor: Colors.DARK_BLUE
          }}
        >
          <Text style={{ flex: 1, color: Colors.WHITE, fontSize: 12, fontWeight: '500' }}>
            Enable notifications not to miss any messages
          </Text>
          <TouchableOpacity
            style={{
              paddingHorizontal: 8,
              paddingVertical: 6,
              backgroundColor: Colors.ORANGE,
              borderRadius: 12
            }}
            onPress={() => navigation.navigate(NAVIGATION_PAGES.NOTIFICATIONS)}
          >
            <Text style={{ color: Colors.WHITE, fontSize: 12, fontWeight: '600' }}>Enable</Text>
          </TouchableOpacity>
        </View>
      )}
      <FlashList
        ListHeaderComponent={SearchHeader}
        contentContainerStyle={{
          minHeight: Dimensions.get('window').height
        }}
        contentOffset={Platform.OS === 'ios' ? { x: 0, y: TOP_HEIGHT } : undefined}
        viewabilityConfig={{
          waitForInteraction: true,
          itemVisiblePercentThreshold: 50,
          minimumViewTime: 1000
        }}
        data={listData}
        renderItem={renderItem}
        keyExtractor={(item: any, i) => {
          if (activeRouteKey === 'blocked') {
            return `${item.userId}-${i}`;
          }
          return `${item.chat.chatUid}-${item.chat.groupChatToken}-${i}`;
        }}
        extraData={extraData}
      />

      <SearchModal />
      <MoreModal />
      <WarningModal
        type={'delete'}
        buttonTitle={isWarningModalVisible?.buttonTitle ?? 'Delete'}
        isVisible={!!isWarningModalVisible?.visible}
        onClose={() =>
          setIsWarningModalVisible({
            ...isWarningModalVisible,
            visible: false,
            title: isWarningModalVisible?.title ?? '',
            buttonTitle: isWarningModalVisible?.buttonTitle ?? 'Delete',
            message: isWarningModalVisible?.message ?? '',
            action: isWarningModalVisible?.action ?? (() => {})
          })
        }
        title={isWarningModalVisible?.title ?? ''}
        message={isWarningModalVisible?.message ?? ''}
        action={isWarningModalVisible?.action}
      />
    </View>
  );
};

export default MessagesScreen;
