"use client";

import React, { useEffect, useState, useRef, useMemo } from "react";
import { BASE_URL, SOCKET_URL } from "../main/BaseUrl";
import axios from "axios";
import ChatMessageCard, { ChatMessage } from "./ChatMessageCard";
import ChatDateSeparator from "./ChatDateSeparator";
import { groupMessagesByDate } from "@/lib/chat/groupMessagesByDate";
import { dedupeChatMessages } from "@/lib/chat/dedupeMessages";
import { formatBubbleTime } from "@/lib/chat/formatMessageTime";
import { io } from "socket.io-client";
import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query";
import { User } from "@/types/types";
import { ChatMessagesSkeleton } from "@/components/ui/ChatSkeletons";
import TypingIndicator from "./TypingIndicator";
import IOSSpinner from "@/components/ui/IOSSpinner";
import { AnimatePresence } from "framer-motion";

function belongsToThread(
  msg: ChatMessage,
  userId: string,
  otherId: string
): boolean {
  const s = String(msg.senderId);
  const r = String(msg.receiverId);
  return (
    (s === userId && r === otherId) || (s === otherId && r === userId)
  );
}

interface ChatMessageListProps {
  userDetail: User | undefined;
  userTwoDetail: User | undefined;
  pendingMessages: ChatMessage[];
  isTyping?: boolean;
  onTypingChange?: (typing: boolean) => void;
  searchQuery?: string;
  selectedMessageId?: string;
  onBubbleClick?: (message: ChatMessage) => void;
}

const ChatMessageList = ({
  userDetail,
  userTwoDetail,
  pendingMessages,
  isTyping = false,
  onTypingChange,
  searchQuery = "",
  selectedMessageId,
  onBubbleClick,
}: ChatMessageListProps) => {
  const [receiverId, setReceiverId] = useState("");
  const scrollRef = useRef<HTMLDivElement | null>(null);
  const prevScrollHeight = useRef(0);
  const [toEnd, setToEnd] = useState(true);
  const queryClient = useQueryClient();

  useEffect(() => {
    setReceiverId(userTwoDetail?._id || "");
  }, [userDetail, userTwoDetail]);

  const fetchMessages = async ({ pageParam = 1 }) => {
    if (!userDetail?._id || !receiverId)
      return { messages: [], nextPage: undefined };

    try {
      const response = await axios.get(
        `${BASE_URL}/chat?senderId=${userDetail?._id}&receiverId=${receiverId}&page=${pageParam}&limit=50&_t=${Date.now()}`
      );
      const msgs = (response.data.messages || []).map((m: ChatMessage) => ({
        ...m,
        createdAt: formatBubbleTime(m.createdAt),
      }));
      return {
        messages: msgs,
        nextPage:
          pageParam < response.data.totalPages ? pageParam + 1 : undefined,
      };
    } catch (error) {
      console.error("Error fetching messages:", error);
      return { messages: [], nextPage: undefined };
    }
  };

  const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } =
    useInfiniteQuery({
      queryKey: ["messages", userDetail?._id, receiverId],
      queryFn: fetchMessages,
      initialPageParam: 1,
      getNextPageParam: (lastPage) => lastPage.nextPage,
      staleTime: 0,
      gcTime: 0,
    });

  useEffect(() => {
    if (toEnd && scrollRef.current) {
      setTimeout(() => {
        scrollRef.current?.scrollTo({
          top: scrollRef.current.scrollHeight,
          behavior: "smooth",
        });
      }, 100);
    }
  }, [data, toEnd, pendingMessages, isTyping]);

  const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
    if (!scrollRef.current) return;

    const isAtTopOfScroll = e.currentTarget.scrollTop === 0;
    const isAtBottomOfScroll =
      Math.abs(
        e.currentTarget.scrollHeight -
          (e.currentTarget.scrollTop + e.currentTarget.clientHeight)
      ) < 48;

    if (isAtTopOfScroll && hasNextPage && !isFetchingNextPage) {
      prevScrollHeight.current = e.currentTarget.scrollHeight;
      fetchNextPage().then(() => {
        if (scrollRef.current) {
          scrollRef.current.scrollTop =
            scrollRef.current.scrollHeight - prevScrollHeight.current;
        }
      });
    }

    setToEnd(isAtBottomOfScroll);
  };

  useEffect(() => {
    if (!userDetail?._id || !receiverId) return;
    const socket = io(SOCKET_URL);

    socket.emit("joinChat", { userId: userDetail._id, receiverId });

    const belongs = (msg: ChatMessage) =>
      belongsToThread(msg, userDetail._id, receiverId) ||
      (msg.senderId === userDetail._id && msg.receiverId === receiverId) ||
      (msg.senderId === receiverId && msg.receiverId === userDetail._id);

    socket.on("newMessage", (message: ChatMessage) => {
      if (!belongs(message)) return;

      const formatted = {
        ...message,
        createdAt: formatBubbleTime(message.createdAt),
      };

      queryClient.setQueryData(
        ["messages", userDetail._id, receiverId],
        (oldData: { pages: { messages: ChatMessage[] }[] } | undefined) => {
          if (!oldData?.pages?.length) return oldData;
          const exists = oldData.pages.some((p) =>
            p.messages.some((m) => m._id === formatted._id)
          );
          if (exists) return oldData;
          const newPages = [...oldData.pages];
          newPages[0] = {
            ...newPages[0],
            messages: [formatted, ...newPages[0].messages],
          };
          return { ...oldData, pages: newPages };
        }
      );
      setToEnd(true);

      if (message.senderId === receiverId) {
        socket.emit("messageSeen", {
          messageId: message._id,
          senderId: message.senderId,
          receiverId: userDetail._id,
        });
      }
    });

    socket.on(
      "messageStatusUpdate",
      ({ messageId, status }: { messageId: string; status: ChatMessage["status"] }) => {
        queryClient.setQueryData(
          ["messages", userDetail._id, receiverId],
          (oldData: { pages: { messages: ChatMessage[] }[] } | undefined) => {
            if (!oldData) return oldData;
            return {
              ...oldData,
              pages: oldData.pages.map((page) => ({
                ...page,
                messages: page.messages.map((m) =>
                  m._id === messageId ? { ...m, status } : m
                ),
              })),
            };
          }
        );
      }
    );

    socket.on("userTyping", ({ userId }: { userId: string }) => {
      if (userId === receiverId) {
        onTypingChange?.(true);
        setTimeout(() => onTypingChange?.(false), 3000);
      }
    });

    return () => {
      socket.off("newMessage");
      socket.off("messageStatusUpdate");
      socket.off("userTyping");
      socket.disconnect();
    };
  }, [userDetail, receiverId, queryClient, onTypingChange]);

  const allMessages = useMemo(() => {
    const server =
      data?.pages.flatMap((page) => page.messages).reverse() || [];
    const serverIds = new Set(server.map((m) => m._id));
    const pendingOnly = pendingMessages.filter((p) => !serverIds.has(p._id));
    const merged = dedupeChatMessages([...server, ...pendingOnly]);
    const q = searchQuery.trim().toLowerCase();
    if (!q) return merged;
    return merged.filter((m) =>
      (m.content || "").toLowerCase().includes(q)
    );
  }, [data, pendingMessages, searchQuery]);

  const grouped = useMemo(
    () => groupMessagesByDate(allMessages),
    [allMessages]
  );

  return (
    <div
      dir="rtl"
      className="custom-scrollbar chat-page-bg flex min-h-0 flex-1 flex-col overflow-y-auto px-2 pb-[calc(5.5rem+env(safe-area-inset-bottom))] pt-2 sm:px-3"
      onScroll={handleScroll}
      ref={scrollRef}
    >
      {isFetchingNextPage && (
        <div className="flex justify-center py-2">
          <IOSSpinner size={18} color="#8e8e93" />
        </div>
      )}

      {isLoading ? (
        <ChatMessagesSkeleton />
      ) : (
        <div className="flex flex-col">
          {grouped.map((item, index) =>
            item.type === "date" ? (
              <ChatDateSeparator key={`date-${item.label}-${index}`} label={item.label} />
            ) : (
              <ChatMessageCard
                key={item.message._id || `msg-${index}`}
                message={item.message}
                userDetail={userDetail}
                selected={selectedMessageId === item.message._id}
                onBubbleClick={onBubbleClick}
              />
            )
          )}
        </div>
      )}

      <AnimatePresence>
        {isTyping && !isLoading && (
          <TypingIndicator userName={userTwoDetail?.first_name} />
        )}
      </AnimatePresence>
    </div>
  );
};

export default ChatMessageList;
