import {
  FlatList as FlatListType,
  NativeScrollEvent,
  NativeSyntheticEvent,
  View,
} from 'react-native';
import {
  Col,
  Container,
  Fab,
  FlatList,
  Row,
  SafeScreen,
  StyleSystemThemedProps,
  Visible,
  Heading,
  ChatInput,
} from 'components';
import { ChatStatus, Message, MessageDivider } from 'components/room';
import { metrics } from 'config';
import { useProfile, useResponsiveLayout, useRouting, useSettings, useTheme } from 'hooks';
import { FC, MutableRefObject, useCallback, useEffect, useRef, useState } from 'react';
import { isDateSame } from 'utils/dateTime';
import { DirectRoomAction, EmojisAction, MessageAction } from 'components/sheets';
import { Direct } from 'components/room/header';
import { FullScreen } from 'components/calling/full-screen';
import { TopBar } from 'components/calling/top-bar';
import { CloseThick } from 'icons';
import { useReactiveVar } from '@apollo/client';
import {
  isMessageThreadOpenVar,
  editingMessageIdVar,
  isEditingMessageVar,
  deletingMessageIdVar,
  directRoomActionPositionVar,
} from 'state-management/chat';
import {
  MessageFragment,
  MessageStatusEnum,
  RoomMember,
  RoomMemberRoleEnum,
  useDeleteMessageMutation,
  useMessageAddedOrEditedSubscription,
  useMultipleMessagesQuery,
  useOneRoomQuery,
} from 'expo-schema';
import { RootStackScreenProps } from 'navigation';
import { NavigationRoutes } from 'navigation/enum';
import { isDirectRoomActionOpenVar } from 'state-management/dialogs';

const { STYLES, Z_INDEX, LIMITS } = metrics;

export const Chat: FC<ChatType> = ({ route, isPopUp, ...props }) => {
  const chatListRef = useRef() as MutableRefObject<FlatListType>;
  const chatInputRef = useRef() as MutableRefObject<View>;
  const roomActionRef = useRef(null);

  const { profile } = useProfile();
  const { activeThemeMode } = useTheme();
  const { params } = useRouting();
  const { isChatPopUpOpen, isOnCallPopUp } = useSettings();
  const { height, bottom, top, isMobile, isHandHeld } = useResponsiveLayout();

  const [offset, setOffset] = useState(0);
  const [isActionOpen, setIsActionOpen] = useState(false);
  const [isRoomAction, setIsRoomAction] = useState(false);
  const [isEmojisOpen, setIsEmojisOpen] = useState(false);
  const [messages, setMessages] = useState<MessageFragment[]>([]);
  const [messageForEdit, setMessageForEdit] = useState<MessageFragment>();

  const isMessageThreadOpen = useReactiveVar(isMessageThreadOpenVar);
  const editingMessageId = useReactiveVar(editingMessageIdVar);
  const isEditingMessage = useReactiveVar(isEditingMessageVar);
  const deletingMessageId = useReactiveVar(deletingMessageIdVar);

  const paddingBottom = bottom + STYLES.screenPaddingBottom;
  const marginTop = top + STYLES.topBarHeight + STYLES.headerHeight;
  const backgroundColor = activeThemeMode === 'dark' ? 'base' : 'base-01';
  const px = isMobile ? 0 : 's';

  const isCallActiveOnPopUp = isPopUp && isOnCallPopUp;
  const isSmall = isMobile || isChatPopUpOpen;
  const editingMessage = editingMessageId && isEditingMessage;
  const roomId = isHandHeld ? route.params.roomId : params.id;

  const position = !isHandHeld
    ? roomActionRef?.current &&
      ((roomActionRef?.current as unknown as HTMLDivElement).getBoundingClientRect() as DOMRect)
    : null;

  const chatInputPosition = !isHandHeld
    ? chatInputRef?.current &&
      ((chatInputRef?.current as unknown as HTMLDivElement).getBoundingClientRect() as DOMRect)
    : null;

  const inputHeight = (chatInputPosition && chatInputPosition.height) as number;
  const chatHeight = height - marginTop - inputHeight;

  const { data, refetch } = useMultipleMessagesQuery({
    variables: {
      input: {
        roomId,
        limit: LIMITS.messages,
        offset,
        status: MessageStatusEnum.Sent,
      },
    },
  });

  useMessageAddedOrEditedSubscription({
    variables: {
      roomId,
    },
    onSubscriptionData: (sub) => {
      handleSubscription(sub.subscriptionData.data?.messageAddedOrEdited as MessageFragment);
    },
  });

  const { data: roomData } = useOneRoomQuery({
    variables: {
      roomId,
    },
  });

  const participant = roomData?.oneRoom?.members?.find(
    (member) => member.role === RoomMemberRoleEnum.Participant
  );

  useEffect(() => {
    if (chatListRef.current) {
      // chatListRef.current.scrollToEnd({ animated: false });
    }
  }, [messages.length]);

  useEffect(() => {
    if (data?.multipleMessages) {
      const key = 'id';
      setMessages((state) => [
        ...new Map([...state, ...data?.multipleMessages].map((msg) => [msg[key], msg])).values(),
      ]);
    }
  }, [data?.multipleMessages]);

  useEffect(() => {
    setMessages([]);
  }, [roomId]);

  const isMessageExist = useCallback(
    (messageId: string) => {
      const messageResponse = messages.find((message) => message.id === messageId);
      return messageResponse;
    },
    [messages]
  );

  const updateMessage = useCallback(
    (message: MessageFragment) => {
      const index = messages.findIndex((msg) => msg.id === message.id);
      const updatedMessages = [...messages];
      updatedMessages.splice(index, 1, message);
      setMessages(updatedMessages);
    },
    [messages]
  );
  const addMessage = useCallback(
    (message: MessageFragment) => {
      setMessages([...messages, message]);
    },
    [messages]
  );

  const handleSubscription = useCallback(
    (messageAddedOrEdited: MessageFragment) => {
      if (messageAddedOrEdited) {
        const isSent = messageAddedOrEdited.status === MessageStatusEnum.Sent;
        if (isSent) {
          if (isMessageExist(messageAddedOrEdited.id)) {
            updateMessage(messageAddedOrEdited);
          } else {
            if (messageAddedOrEdited?.createdBy.id !== profile?.id) {
              addMessage(messageAddedOrEdited);
            }
          }
        }
      }
    },
    [addMessage, isMessageExist, profile?.id, updateMessage]
  );

  const onDeleteComplete = useCallback(() => {
    deletingMessageIdVar('');
  }, []);

  const [deleteMessage] = useDeleteMessageMutation({
    onCompleted: onDeleteComplete,
  });

  useEffect(() => {
    if (deletingMessageId) {
      deleteMessage({
        variables: {
          id: deletingMessageId,
        },
      });
    }
  }, [deleteMessage, deletingMessageId]);

  useEffect(() => {
    if (editingMessage) {
      const foundMessage = messages?.find((message) => message.id === editingMessageId);
      if (foundMessage) {
        setMessageForEdit(foundMessage);
      }
    }
  }, [editingMessageId, editingMessage, messages]);

  const closeThread = useCallback(() => {
    isMessageThreadOpenVar(false);
  }, []);

  const renderDivider = useCallback(
    (index: number) => {
      if (messages) {
        if (messages[index - 1]) {
          if (!isDateSame(messages[index - 1].createdAt, messages[index].createdAt)) {
            return <MessageDivider createdAt={messages[index].createdAt} />;
          }
          return null;
        }
      }

      return null;
    },
    [messages]
  );

  const openAction = useCallback(() => {
    setIsActionOpen(true);
  }, []);

  const openRoomAction = useCallback(() => {
    if (isHandHeld) {
      setIsRoomAction(!isRoomAction);
    } else {
      isDirectRoomActionOpenVar(true);
    }
  }, [isHandHeld, isRoomAction]);

  const openEmojisSheet = useCallback(() => {
    setIsActionOpen(false);
    setIsEmojisOpen(true);
  }, []);

  useEffect(() => {
    if (position) {
      const roomActionPosition = position as unknown as DOMRect;
      directRoomActionPositionVar({
        top: roomActionPosition.top,
        bottom: undefined,
        x: roomActionPosition.x,
      });
    }
  }, [position]);

  const handleScroll = useCallback(
    (event: NativeSyntheticEvent<NativeScrollEvent>) => {
      const offsetY = event.nativeEvent.contentOffset.y;
      const lastMessagesLength = data?.multipleMessages.length === LIMITS.messages;
      const isRefetch = lastMessagesLength && offsetY === 0;
      if (isRefetch) {
        refetch({
          input: {
            limit: LIMITS.messages,
            offset: offset + LIMITS.messages,
            roomId,
            status: MessageStatusEnum.Sent,
          },
        });
        setOffset((state) => state + LIMITS.messages);
      }
    },
    [data?.multipleMessages, offset, refetch, roomId]
  );

  return (
    <SafeScreen
      backgroundColor={backgroundColor}
      edges={['top', 'bottom', 'left', 'right']}
      {...props}
    >
      <Container zIndex={Z_INDEX.content}>
        <Container ref={roomActionRef}>
          <Direct
            participant={participant as RoomMember}
            backgroundColor={backgroundColor}
            isOpen={isRoomAction}
            isPopUp={isPopUp}
            onPress={openRoomAction}
            pr="xs"
          />
        </Container>

        <Visible if={isCallActiveOnPopUp}>
          <TopBar isPopUp={isPopUp} />
        </Visible>
      </Container>
      <Row flex={1}>
        <Col flex={1}>
          <Visible if={isCallActiveOnPopUp}>
            <FullScreen isPopUp={isPopUp} variant="full" />
          </Visible>
          <FlatList
            ref={chatListRef}
            onScroll={handleScroll}
            onEndReachedThreshold={0.5}
            style={{
              height: chatHeight,
            }}
            contentContainerStyle={{
              flexGrow: 1,
            }}
            data={messages}
            keyExtractor={(item: any) => item.id}
            renderItem={({ item, index }) => (
              <Col>
                {renderDivider(index)}
                <Container>
                  <Message
                    message={item as MessageFragment}
                    onLongPress={openAction}
                    onHovered={setIsActionOpen}
                  />
                </Container>
              </Col>
            )}
          />
          <Container ref={chatInputRef} backgroundColor={backgroundColor} pb="xs">
            <Visible if={false}>
              <Row mb="xs">
                <ChatStatus name="Sarmad" variant="video-recording" />
              </Row>
            </Visible>
            <Container px={px}>
              <ChatInput
                roomId={roomId}
                variant="message"
                message={messageForEdit as MessageFragment}
              />
            </Container>
          </Container>
          <Visible if={isSmall}>
            <EmojisAction setIsSheetOpen={setIsEmojisOpen} isSheetOpen={isEmojisOpen} />
          </Visible>
          <Visible if={isSmall}>
            <MessageAction
              position={null}
              messageId="none"
              setIsSheetOpen={setIsActionOpen}
              openEmojisSheet={openEmojisSheet}
              isSheetOpen={isActionOpen}
            />
          </Visible>
          <DirectRoomAction setIsSheetOpen={setIsRoomAction} isSheetOpen={isRoomAction} />
        </Col>

        <Visible if={isMessageThreadOpen}>
          <Col width={270}>
            <Container borderLeftWidth={1} borderLeftColor={'base-02'} flex={1}>
              <Heading
                borderBottomColor="base-02"
                borderBottomWidth={1}
                px="xs"
                py="xs"
                isAction={true}
                action={
                  <Fab
                    onPress={closeThread}
                    variant="plain"
                    colorVariant="transparent"
                    centerElement={<CloseThick colorVariant="body" />}
                  />
                }
                title="Thread"
                caption="General / PSY 345 Summer 2021-22"
              />

              <FlatList
                style={{
                  height: chatHeight,
                }}
                contentContainerStyle={{
                  flexGrow: 1,
                  paddingBottom: paddingBottom,
                }}
                data={messages}
                keyExtractor={(item: any) => item.id}
                renderItem={({ item, index }) => (
                  <Col>
                    {renderDivider(index)}
                    <Container>
                      <Message
                        isThread={true}
                        message={item as MessageFragment}
                        onLongPress={openAction}
                        onHovered={setIsActionOpen}
                      />
                    </Container>
                  </Col>
                )}
              />
            </Container>
          </Col>
        </Visible>
      </Row>
    </SafeScreen>
  );
};

type ChatType = StyleSystemThemedProps & ChatProps & RootStackScreenProps<NavigationRoutes.Chat>;

interface ChatProps {
  isPopUp: boolean;
}
