/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useLayoutEffect, useRef } from "react";
import Message from "./Message";
import { useChatContext } from "../../providers/ChatProvider";
import useChatUIStore from "../../stores/Zustant";
import { useInfiniteQuery } from "@tanstack/react-query";
import { fetchMessages } from "../../service/messageService";
import { useInView } from "react-intersection-observer";
import Loader from "../Loader";
import SystemNotice from "../common/SystemNotice";

const Messages = () => {
  const { selectedConversation, setMessages, messages } = useChatUIStore();
  const { userId, socket } = useChatContext();
  const { ref, inView } = useInView();
  const chatContainerRef = useRef<HTMLDivElement>(null);
  const prevScrollHeightRef = useRef(0);
  const prevIsFetchingRef = useRef(false);

  const { data, fetchNextPage, isFetchingNextPage } = useInfiniteQuery({
    queryKey: ["messages", selectedConversation?._id, userId],
    queryFn: ({ pageParam = 1 }) =>
      fetchMessages(selectedConversation?._id, userId, pageParam),
    getNextPageParam: (lastPage) => lastPage.nextPage,
    initialPageParam: 1,
    enabled: !!selectedConversation?._id, // Prevent fetching if no conversation is selected
  });

  // Handle infinite scroll
  useEffect(() => {
    if (inView) fetchNextPage();
  }, [fetchNextPage, inView]);

  // Save scroll height before older messages are fetched
  useEffect(() => {
    if (isFetchingNextPage && !prevIsFetchingRef.current) {
      prevScrollHeightRef.current =
        chatContainerRef.current?.scrollHeight ?? 0;
    }
    prevIsFetchingRef.current = isFetchingNextPage;
  }, [isFetchingNextPage]);

  // Restore scroll position after older messages are rendered
  useLayoutEffect(() => {
    if (!isFetchingNextPage && prevScrollHeightRef.current > 0) {
      const container = chatContainerRef.current;
      if (container && container.scrollHeight > prevScrollHeightRef.current) {
        container.scrollTop =
          container.scrollHeight - prevScrollHeightRef.current;
        prevScrollHeightRef.current = 0;
      }
    }
  }, [messages, isFetchingNextPage]);

  // Update messages when data changes
  useEffect(() => {
    if (data) {
      const allMessages = data.pages.flatMap((page) => page.messages).reverse();
      setMessages(allMessages);
    }
  }, [data, setMessages]);

  // Listen for WebSocket messages
  useEffect(() => {
    if (!socket || !selectedConversation?._id) return;

    const handleMessage = (event: MessageEvent) => {
      try {
        const parsed = JSON.parse(event.data);
        // if (parsed.type === "newMessage" || parsed.event === "newMessage") {
        //   const newMessage = parsed.data;
        //   if (!newMessage) {
        //     // console.warn(
        //     //   "Received newMessage event without a message payload",
        //     //   parsed
        //     // );
        //     return;
        //   }

        //   newMessage.shouldShake = true;

        //   setMessages((prevMessages) => {
        //     const isDuplicate = prevMessages.some(
        //       (msg) =>
        //         msg._id === newMessage._id ||
        //         (msg.isOptimistic &&
        //           msg.sender.senderId === userId &&
        //           msg.message === newMessage.message)
        //     );

        //     return isDuplicate ? prevMessages : [...prevMessages, newMessage];
        //   });
        // }

        if (parsed.event === "newMessage" || parsed.type === "newMessage") {
          const newMessage = parsed?.data?.data;
          if (!newMessage) return;

          const incomingConversationId = newMessage.conversationId;
          const currentConversationId = selectedConversation?._id;

          // ✅ if message is for another conversation, DO NOT append here
          if (
            !currentConversationId ||
            incomingConversationId !== currentConversationId
          ) {
            return;
          }

          newMessage.shouldShake = true;

          setMessages((prevMessages) => {
            const isDuplicate = prevMessages.some(
              (msg: any) =>
                msg._id === newMessage._id ||
                (msg.isOptimistic &&
                  msg.sender?.senderId === userId &&
                  msg.message === newMessage.message),
            );

            return isDuplicate ? prevMessages : [...prevMessages, newMessage];
          });

          return;
        }

        const statusOrder = ["sent", "delivered", "read", "edited", "deleted"];
        if (parsed.event === "messageStatusUpdated") {
          const { messageId, status } = parsed.data || {};
          if (!messageId) {
            // console.error("Missing messageId in status update", parsed);
            return;
          }

          setMessages((prev) =>
            prev.map((msg) => {
              if (msg._id !== messageId) return msg;

              const currentIdx = statusOrder.indexOf(msg.status);
              const newIdx = statusOrder.indexOf(status);
              if (newIdx === -1 || currentIdx === -1 || newIdx <= currentIdx)
                return msg;

              if (newIdx > currentIdx) {
                // console.log(`Updating status for ${messageId} to ${status}`);
                return { ...msg, status };
              }

              return msg; // No update if new status isn't higher
            }),
          );
        }

        if (parsed.event === "messageEdited") {
          // console.log("Received messageEdited event:", parsed);

          const updatedMessage = parsed.data;
          if (!updatedMessage || !updatedMessage.messageId) return;

          setMessages((prevMessages) =>
            prevMessages.map((msg) =>
              msg._id === updatedMessage.messageId
                ? { ...msg, message: updatedMessage.message, status: "edited" }
                : msg,
            ),
          );
        }

        if (parsed.event === "messageDeleted") {
          // console.log("Received messageDeleted event:", parsed);

          const { messageId } = parsed.data || {};
          if (!messageId) return;

          setMessages((prevMessages) =>
            prevMessages.map((msg) =>
              msg._id === messageId
                ? {
                    ...msg,
                    message: "This message was deleted",
                    status: "deleted",
                  }
                : msg,
            ),
          );
        }
      } catch (error) {
        console.error("Error parsing WebSocket message:", error);
      }
    };

    socket.addEventListener("message", handleMessage);

    return () => {
      socket.removeEventListener("message", handleMessage);
    };
  }, [socket, selectedConversation?._id, setMessages, userId]);

  // Scroll to bottom when a new message is appended (sender or receiver)
  const lastMessageId = messages[messages.length - 1]?._id;
  useEffect(() => {
    if (!lastMessageId) return;
    const timer = setTimeout(() => {
      const container = chatContainerRef.current;
      if (container) {
        container.scrollTo({
          top: container.scrollHeight,
          behavior: "smooth",
        });
      }
    }, 100);
    return () => clearTimeout(timer);
  }, [lastMessageId]);

  // Track message visibility for read receipts
  // useEffect(() => {
  //   if (!socket || !messages.length) return;

  //   const observer = new IntersectionObserver(
  //     (entries) => {
  //       entries.forEach((entry) => {
  //         if (entry.isIntersecting) {
  //           const messageId = entry.target.getAttribute("data-message-id");
  //           if (messageId) {
  //             sendDeliveryConfirmation(messageId);
  //           }
  //         }
  //       });
  //     },
  //     { threshold: 0.5 }
  //   );

  //   const messageElements = document.querySelectorAll("[data-message-id]");
  //   messageElements.forEach((el) => observer.observe(el));

  //   return () => observer.disconnect();
  // }, [messages, socket]);

  return (
    <div ref={chatContainerRef} className="chat-messages">
      <div className="chat-messages__inner">
        <SystemNotice />
        <div ref={ref} className="chat-messages__sentinel">
          {isFetchingNextPage ? <Loader /> : null}
        </div>
        {messages?.length > 0 ? (
          messages.map((message: any, idx: number) => {
            if (!message) return null;
            const divider = dividerLabel(message, messages[idx - 1]);
            return (
              <div key={message._id} data-message-id={message._id}>
                {divider && <div className="chat-date-divider">{divider}</div>}
                <Message message={message} />
              </div>
            );
          })
        ) : (
          <p className="chat-messages__hint">
            Send a message to start the conversation
          </p>
        )}
      </div>
    </div>
  );
};

const isSameDay = (a: Date, b: Date) =>
  a.getFullYear() === b.getFullYear() &&
  a.getMonth() === b.getMonth() &&
  a.getDate() === b.getDate();

/**
 * Label for the divider above a message, or null when it shares a day with
 * the message before it.
 */
const dividerLabel = (message: any, previous: any): string | null => {
  const at = new Date(message?.createdAt);
  if (Number.isNaN(at.getTime())) return null;

  const prevAt = previous ? new Date(previous.createdAt) : null;
  if (prevAt && !Number.isNaN(prevAt.getTime()) && isSameDay(at, prevAt)) return null;

  const now = new Date();
  if (isSameDay(at, now)) return "Today";

  const yesterday = new Date(now);
  yesterday.setDate(now.getDate() - 1);
  if (isSameDay(at, yesterday)) return "Yesterday";

  return at.toLocaleDateString([], { month: "short", day: "numeric", year: "numeric" });
};

export default Messages;
