import { gql, useApolloClient, useQuery, useSubscription } from "@apollo/client";
import axios from "axios";
import React, { useCallback, useContext, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import SocketContext from "../../contexts/SocketIO/socketContext";
import { GET_CONVERSATION_DETAILS, CONVERSATION_SUBSCRIPTION_BY_PK } from "../../graphql/conversations.queries";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ChatConversationComponent from "./chat-conversation.component";

const mapStateToProps = createStructuredSelector({
  selectedConversation: selectSelectedConversation,
  bodyshop: selectBodyshop
});

function ChatConversationContainer({ bodyshop, selectedConversation }) {
  const client = useApolloClient();
  const { socket } = useContext(SocketContext);
  const [markingAsReadInProgress, setMarkingAsReadInProgress] = useState(false);

  // Fetch conversation details
  const {
    loading: convoLoading,
    error: convoError,
    data: convoData
  } = useQuery(GET_CONVERSATION_DETAILS, {
    variables: { conversationId: selectedConversation },
    fetchPolicy: "network-only",
    nextFetchPolicy: "network-only"
  });

  // Subscription for conversation updates
  useSubscription(CONVERSATION_SUBSCRIPTION_BY_PK, {
    skip: socket?.connected,
    variables: { conversationId: selectedConversation },
    onData: ({ data: subscriptionResult, client }) => {
      // Extract the messages array from the result
      const messages = subscriptionResult?.data?.messages;
      if (!messages || messages.length === 0) {
        console.warn("No messages found in subscription result.");
        return;
      }

      messages.forEach((message) => {
        const messageRef = client.cache.identify(message);
        // Write the new message to the cache
        client.cache.writeFragment({
          id: messageRef,
          fragment: gql`
            fragment NewMessage on messages {
              id
              status
              text
              isoutbound
              image
              image_path
              userid
              created_at
              read
            }
          `,
          data: message
        });

        // Update the conversation cache to include the new message
        client.cache.modify({
          id: client.cache.identify({ __typename: "conversations", id: selectedConversation }),
          fields: {
            messages(existingMessages = []) {
              const alreadyExists = existingMessages.some((msg) => msg.__ref === messageRef);
              if (alreadyExists) return existingMessages;
              return [...existingMessages, { __ref: messageRef }];
            },
            updated_at() {
              return message.created_at;
            }
          }
        });
      });
    }
  });

  const updateCacheWithReadMessages = useCallback(
    (conversationId, messageIds) => {
      if (!conversationId || !messageIds?.length) return;

      messageIds.forEach((messageId) => {
        client.cache.modify({
          id: client.cache.identify({ __typename: "messages", id: messageId }),
          fields: {
            read: () => true
          }
        });
      });
    },
    [client.cache]
  );

  // WebSocket event handlers
  useEffect(() => {
    if (!socket?.connected) return;

    const handleConversationChange = (data) => {
      if (data.type === "conversation-marked-read") {
        const { conversationId, messageIds } = data;
        updateCacheWithReadMessages(conversationId, messageIds);
      }
    };

    socket.on("conversation-changed", handleConversationChange);

    return () => {
      socket.off("conversation-changed", handleConversationChange);
    };
  }, [socket, updateCacheWithReadMessages]);

  // Join and leave conversation via WebSocket
  useEffect(() => {
    if (!socket?.connected || !selectedConversation || !bodyshop?.id) return;

    socket.emit("join-bodyshop-conversation", {
      bodyshopId: bodyshop.id,
      conversationId: selectedConversation
    });

    return () => {
      socket.emit("leave-bodyshop-conversation", {
        bodyshopId: bodyshop.id,
        conversationId: selectedConversation
      });
    };
  }, [socket, bodyshop, selectedConversation]);

  // Mark conversation as read
  const handleMarkConversationAsRead = async () => {
    if (!convoData || markingAsReadInProgress) return;

    const conversation = convoData.conversations_by_pk;
    if (!conversation) return;

    const unreadMessageIds = conversation.messages
      ?.filter((message) => !message.read && !message.isoutbound)
      .map((message) => message.id);

    if (unreadMessageIds?.length > 0) {
      setMarkingAsReadInProgress(true);
      try {
        await axios.post("/sms/markConversationRead", {
          conversation,
          imexshopid: bodyshop?.imexshopid,
          bodyshopid: bodyshop?.id
        });

        updateCacheWithReadMessages(selectedConversation, unreadMessageIds);
      } catch (error) {
        console.error("Error marking conversation as read:", error.message);
      } finally {
        setMarkingAsReadInProgress(false);
      }
    }
  };

  return (
    <ChatConversationComponent
      subState={[convoLoading, convoError]}
      conversation={convoData?.conversations_by_pk || {}}
      messages={convoData?.conversations_by_pk?.messages || []}
      handleMarkConversationAsRead={handleMarkConversationAsRead}
    />
  );
}

export default connect(mapStateToProps)(ChatConversationContainer);
