"use client";

import React, { useCallback, useEffect, useRef, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import Container from "@/components/elements/Container";
import ProfileAvatar from "@/components/main/ProfileAvatar";
import VerificationBadge from "@/components/main/VerificationBadge";
import useAxios from "@/hooks/useAxios";
import { useUser } from "@/hooks/useUser";
import {
  acquireChatSocket,
  joinGroupRoom,
  joinUserRoom,
  leaveGroupRoom,
  releaseChatSocket,
} from "@/lib/chat/socketClient";
import MessageInput from "@/components/chat/MessageInput";
import ChatActionBar from "@/components/chat/ChatActionBar";
import AddChatRoomMembersModal from "@/components/chat/AddChatRoomMembersModal";
import MultiImageModal from "@/components/chat/MultiImageModal";
import MediaPreviewModal from "@/components/chat/MediaPreviewModal";
import VoiceMessagePlayer from "@/components/chat/VoiceMessagePlayer";
import { groupReactions } from "@/lib/chat/reactions";
import { useLongPress } from "@/hooks/useLongPress";
import { useSwipeToReply } from "@/hooks/useSwipeToReply";
import ChatBoldIcon from "@/components/chat/ChatBoldIcon";
import BoldIcon from "@/components/ui/BoldIcon";
import { motion } from "framer-motion";
import IOSSpinner from "@/components/ui/IOSSpinner";
import toast from "react-hot-toast";
import { cn } from "@/lib/utils";
import { getStoredUserId } from "@/lib/auth/session";
import {
  copyTextToClipboard,
  getCopyableRoomMessageText,
} from "@/lib/chat/getCopyableMessageText";
import { useTranslation } from "react-i18next";
import LocalePageShell from "@/components/i18n/LocalePageShell";
import { optimizeMediaFile } from "@/lib/media";
import { buildStorageUrl } from "@/components/main/BaseUrl";
import {
  decryptRoomContent,
  decryptRoomMessageList,
  encryptRoomContent,
  ensureIdentityKeys,
  ensureRoomAesKey,
} from "@/lib/e2ee";

type RoomMember = {
  _id: string;
  user_name?: string;
  first_name?: string;
  last_name?: string;
  profile_image?: string;
  is_verified?: string;
  verify_badge?: string;
};

type RoomMessage = {
  _id: string;
  roomId: string;
  senderId: string;
  content: string;
  createdAt: string;
  file?: string;
  fileType?: "image" | "video" | "file" | "voice" | "location";
  sender?: RoomMember;
  reactions?: Array<{ userId: string; emoji: string }>;
  replyTo?: {
    _id: string;
    content: string;
    senderName?: string;
  };
};

type RoomDetail = {
  _id: string;
  title: string;
  description?: string;
  image?: string;
  onlineCount?: number;
  visibility?: "public" | "private";
  createdBy?: string | null;
  members?: RoomMember[];
};

function mergeRoomMessages(
  prev: RoomMessage[],
  incoming: RoomMessage[]
): RoomMessage[] {
  const pending = prev.filter((m) => String(m._id).startsWith("temp-"));
  const byId = new Map<string, RoomMessage>();

  for (const msg of incoming) {
    byId.set(msg._id, msg);
  }

  for (const msg of pending) {
    const matched = incoming.some(
      (item) =>
        item.content === msg.content &&
        String(item.senderId) === String(msg.senderId)
    );
    if (!matched) {
      byId.set(msg._id, msg);
    }
  }

  const order = new Map(incoming.map((msg, index) => [msg._id, index]));
  return Array.from(byId.values()).sort((a, b) => {
    const ai = order.get(a._id);
    const bi = order.get(b._id);
    if (ai != null && bi != null) return ai - bi;
    if (ai != null) return -1;
    if (bi != null) return 1;
    return 0;
  });
}

function RoomMessageRow({
  msg,
  isMine,
  senderName,
  reactionGroups,
  messageRefs,
  onOpenAction,
  onSwipeReply,
  onScrollToReply,
}: {
  msg: RoomMessage;
  isMine: boolean;
  senderName: string;
  reactionGroups: Array<{ emoji: string; count: number }>;
  messageRefs: React.MutableRefObject<Map<string, HTMLDivElement>>;
  onOpenAction: (msg: RoomMessage) => void;
  onSwipeReply: (msg: RoomMessage) => void;
  onScrollToReply: () => void;
}) {
  const handleOpenActions = useCallback(() => {
    onOpenAction(msg);
  }, [msg, onOpenAction]);

  const handleSwipeReply = useCallback(() => {
    onSwipeReply(msg);
  }, [msg, onSwipeReply]);

  const { handlers: longPressHandlers, shouldBlockClick } = useLongPress(
    handleOpenActions,
    { delay: 2000 }
  );

  const { dragProps, replyOpacity, replyScale } = useSwipeToReply(
    handleSwipeReply,
    true,
    "left"
  );

  const swipeDragStart =
    "onDragStart" in dragProps ? dragProps.onDragStart : undefined;
  const { onDragStart: _, ...restDragProps } = dragProps;

  return (
    <div
      ref={(el) => {
        if (el) messageRefs.current.set(msg._id, el);
        else messageRefs.current.delete(msg._id);
      }}
      className={cn("flex w-full", isMine ? "justify-end" : "justify-start")}
    >
      <div
        className={cn(
          "max-w-[min(85%,320px)]",
          isMine ? "items-end" : "items-start"
        )}
      >
        {!isMine ? (
          <Link
            href={`/users/${msg.sender?.user_name ?? ""}`}
            className="mb-1 flex items-center gap-1.5 text-[11px] font-semibold text-[#0095f6]"
          >
            <ProfileAvatar
              src={msg.sender?.profile_image}
              alt={senderName}
              size="xxs"
              rounded="full"
              className="h-5 w-5 shrink-0"
            />
            <span>{senderName}</span>
            <VerificationBadge verifyBadge={msg.sender?.verify_badge} />
          </Link>
        ) : null}
        {msg.replyTo ? (
          <button
            type="button"
            onClick={onScrollToReply}
            className="mb-1 w-full rounded-lg border-r-2 border-[#0095f6] bg-black/5 px-2 py-1 text-right text-[11px] dark:bg-white/10"
          >
            <span className="font-semibold text-[#0095f6]">
              {msg.replyTo.senderName}
            </span>
            <p className="truncate">{msg.replyTo.content}</p>
          </button>
        ) : null}
        <div className="relative w-full overflow-hidden">
          <motion.div
            aria-hidden
            className={cn(
              "pointer-events-none absolute inset-y-0 flex w-11 items-center justify-center text-[#0095f6]",
              isMine ? "right-0" : "left-0"
            )}
            style={{ opacity: replyOpacity, scale: replyScale }}
          >
            <ChatBoldIcon
              name="reply"
              size={20}
              className={cn("text-current", !isMine && "-scale-x-100")}
            />
          </motion.div>
          <motion.div
            {...restDragProps}
            onDragStart={swipeDragStart}
            className="relative z-[1] w-full"
          >
            <div
              data-chat-bubble
              {...longPressHandlers}
              onClick={() => {
                if (shouldBlockClick()) return;
              }}
              className={cn(
                "rounded-[18px] px-3 py-2 text-[15px] leading-snug select-none touch-manipulation",
                isMine ? "chat-bubble-out" : "chat-bubble-in"
              )}
            >
              {msg.file && msg.fileType === "image" ? (
                // eslint-disable-next-line @next/next/no-img-element
                <img
                  src={
                    msg.file.startsWith("blob:")
                      ? msg.file
                      : buildStorageUrl(msg.file)
                  }
                  alt=""
                  className="mb-1 max-h-64 w-full rounded-xl object-cover"
                />
              ) : null}
              {msg.file && msg.fileType === "video" ? (
                <video
                  src={
                    msg.file.startsWith("blob:")
                      ? msg.file
                      : buildStorageUrl(msg.file)
                  }
                  controls
                  playsInline
                  className="mb-1 max-h-64 w-full rounded-xl"
                />
              ) : null}
              {msg.file && msg.fileType === "voice" ? (
                <div className="mb-1 min-w-[200px]">
                  <VoiceMessagePlayer
                    src={
                      msg.file.startsWith("blob:")
                        ? msg.file
                        : buildStorageUrl(msg.file)
                    }
                    isOutgoing={isMine}
                  />
                </div>
              ) : null}
              {msg.file && msg.fileType === "file" ? (
                <a
                  href={
                    msg.file.startsWith("blob:")
                      ? msg.file
                      : buildStorageUrl(msg.file)
                  }
                  target="_blank"
                  rel="noopener noreferrer"
                  className="mb-1 flex items-center gap-2 text-sm underline"
                >
                  <BoldIcon name="document-download" size={18} />
                  <span>File</span>
                </a>
              ) : null}
              {msg.content ? (
                <p className="chat-message-text select-text whitespace-pre-wrap break-words">
                  {msg.content}
                </p>
              ) : null}
              <span className="mt-1 block text-[10px] opacity-70">
                {msg.createdAt}
              </span>
            </div>
          </motion.div>
        </div>
        {reactionGroups.length > 0 ? (
          <div className="mt-1 flex flex-wrap gap-1">
            {reactionGroups.map(({ emoji, count }) => (
              <span
                key={emoji}
                className="rounded-full bg-black/10 px-2 py-0.5 text-xs dark:bg-white/10"
              >
                {emoji}
                {count > 1 ? ` ${count}` : ""}
              </span>
            ))}
          </div>
        ) : null}
      </div>
    </div>
  );
}

export default function ChatRoomThreadPage({
  params,
}: {
  params: Promise<{ roomId: string }>;
}) {
  const { t } = useTranslation("common");
  const resolvedParams = React.use(params);
  const { roomId } = resolvedParams;
  const { request } = useAxios();
  const router = useRouter();
  const user = useUser();
  const [room, setRoom] = useState<RoomDetail | null>(null);
  const [messages, setMessages] = useState<RoomMessage[]>([]);
  const [newMessage, setNewMessage] = useState("");
  const [loading, setLoading] = useState(true);
  const [sending, setSending] = useState(false);
  const [replyingTo, setReplyingTo] = useState<RoomMessage | null>(null);
  const [selectedMessage, setSelectedMessage] = useState<RoomMessage | null>(null);
  const [actionMode, setActionMode] = useState(false);
  const [showAddMembers, setShowAddMembers] = useState(false);
  const [pendingImages, setPendingImages] = useState<File[]>([]);
  const [showMultiModal, setShowMultiModal] = useState(false);
  const [pendingMediaPreview, setPendingMediaPreview] = useState<{
    file: File;
    kind: "video" | "file";
  } | null>(null);
  const scrollRef = useRef<HTMLDivElement | null>(null);
  const headerRef = useRef<HTMLElement | null>(null);
  const messageRefs = useRef(new Map<string, HTMLDivElement>());
  const [headerOffset, setHeaderOffset] = useState(120);

  const scrollToBottom = useCallback(() => {
    const el = scrollRef.current;
    if (!el) return;
    el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
  }, []);

  const scrollToMessage = useCallback((targetId: string) => {
    const el = messageRefs.current.get(targetId);
    const container = scrollRef.current;
    if (!el || !container) {
      toast.error(t("chats.toast.originalMessageNotFound"));
      return;
    }
    const offset =
      el.getBoundingClientRect().top -
      container.getBoundingClientRect().top +
      container.scrollTop -
      container.clientHeight / 2;
    container.scrollTo({ top: offset, behavior: "smooth" });
  }, [t]);

  const loadRoom = useCallback(async () => {
    await request("POST", `/chat-rooms/${roomId}/join`, {}, { noToast: true });
    const detail = await request<{ room: RoomDetail }>(
      "GET",
      `/chat-rooms/${roomId}`,
      null,
      { noToast: true }
    );
    setRoom(detail.room);
  }, [request, roomId]);

  const loadMessages = useCallback(async () => {
    const res = await request<{ messages?: RoomMessage[] }>(
      "GET",
      `/chat-rooms/${roomId}/messages?limit=80`,
      null,
      { noToast: true }
    );
    const incoming = res?.messages ?? [];
    const uid = String(user?._id ?? getStoredUserId());
    let decrypted = incoming;
    if (uid) {
      try {
        await ensureIdentityKeys(uid);
        await ensureRoomAesKey(uid, roomId);
        decrypted = await decryptRoomMessageList(uid, roomId, incoming);
      } catch {
        decrypted = incoming;
      }
    }
    setMessages((prev) => mergeRoomMessages(prev, decrypted));
  }, [request, roomId, user?._id]);

  useEffect(() => {
    (async () => {
      setLoading(true);
      try {
        await loadRoom();
        await loadMessages();
      } catch {
        toast.error(t("chats.toast.loadRoomFailed"));
      } finally {
        setLoading(false);
      }
    })();
  }, [loadRoom, loadMessages]);

  useEffect(() => {
    const headerEl = headerRef.current;
    if (!headerEl) return;

    const updateOffset = () => {
      setHeaderOffset(headerEl.offsetHeight + 8);
    };

    updateOffset();
    const observer = new ResizeObserver(updateOffset);
    observer.observe(headerEl);
    return () => observer.disconnect();
  }, [room?.members?.length, room?.title]);

  useEffect(() => {
    const userId = String(user?._id ?? getStoredUserId()).trim();
    if (!userId) return;

    const socket = acquireChatSocket();
    const syncRooms = () => {
      joinUserRoom(userId);
      joinGroupRoom(roomId, userId);
    };

    syncRooms();
    socket.on("connect", syncRooms);
    if (socket.connected) syncRooms();

    const onNew = (msg: RoomMessage) => {
      if (String(msg.roomId) !== String(roomId)) return;
      void (async () => {
        let next = msg;
        try {
          const uid = String(user?._id ?? getStoredUserId());
          if (uid && msg.content) {
            const content = await decryptRoomContent(uid, roomId, msg.content);
            next = { ...msg, content };
            if (msg.replyTo?.content) {
              const replyContent = await decryptRoomContent(
                uid,
                roomId,
                msg.replyTo.content
              );
              next = {
                ...next,
                replyTo: { ...msg.replyTo, content: replyContent },
              };
            }
          }
        } catch {
          /* keep as-is */
        }
        setMessages((prev) => {
          if (prev.some((m) => m._id === next._id)) return prev;
          const cleaned = prev.filter(
            (m) =>
              !String(m._id).startsWith("temp-") ||
              m.content !== next.content ||
              String(m.senderId) !== String(next.senderId)
          );
          return [...cleaned, next];
        });
      })();
    };

    socket.on("newGroupMessage", onNew);
    const onDeleted = ({ roomId: deletedId }: { roomId: string }) => {
      if (String(deletedId) === String(roomId)) {
        toast.error(t("chats.toast.roomDeleted"));
        router.replace("/settings/chats/rooms");
      }
    };
    socket.on("chatRoomDeleted", onDeleted);

    const onGroupReaction = ({
      roomId: reactionRoomId,
      messageId,
      reactions,
    }: {
      roomId: string;
      messageId: string;
      reactions: RoomMessage["reactions"];
    }) => {
      if (String(reactionRoomId) !== String(roomId)) return;
      setMessages((prev) =>
        prev.map((m) => (m._id === messageId ? { ...m, reactions } : m))
      );
    };
    socket.on("groupMessageReaction", onGroupReaction);

    return () => {
      socket.off("connect", syncRooms);
      socket.off("newGroupMessage", onNew);
      socket.off("chatRoomDeleted", onDeleted);
      socket.off("groupMessageReaction", onGroupReaction);
      leaveGroupRoom(roomId);
      releaseChatSocket();
    };
  }, [roomId, user?._id, router]);

  useEffect(() => {
    if (loading) return;
    const poll = window.setInterval(() => {
      void loadMessages();
      void loadRoom();
    }, 8000);
    return () => window.clearInterval(poll);
  }, [loading, loadMessages, loadRoom]);

  useEffect(() => {
    scrollToBottom();
  }, [messages.length, scrollToBottom]);

  const processSendMessage = async (
    fileToUpload: File | null = null,
    fileType?: RoomMessage["fileType"]
  ) => {
    const text = newMessage.trim();
    if ((!text && !fileToUpload) || sending) return;

    const senderId = String(user?._id ?? getStoredUserId());
    const tempId = `temp-${Date.now()}-${Math.random()}`;
    const savedReply = replyingTo;
    const replyPayload = savedReply
      ? {
          _id: savedReply._id,
          content: savedReply.content,
          senderName:
            [savedReply.sender?.first_name, savedReply.sender?.last_name]
              .filter(Boolean)
              .join(" ") ||
            savedReply.sender?.user_name ||
            t("chats.user"),
        }
      : undefined;

    const optimistic: RoomMessage = {
      _id: tempId,
      roomId,
      senderId,
      content: text,
      createdAt: new Date().toLocaleTimeString("fa-IR"),
      file: fileToUpload ? URL.createObjectURL(fileToUpload) : undefined,
      fileType,
      sender: user
        ? {
            _id: senderId,
            first_name: user.first_name,
            last_name: user.last_name,
            user_name: user.user_name,
            profile_image: user.profile_image,
            is_verified: user.is_verified,
          }
        : undefined,
      replyTo: replyPayload,
    };

    setMessages((prev) => [...prev, optimistic]);
    setNewMessage("");
    setReplyingTo(null);
    setSending(true);

    let fileForUpload = fileToUpload;
    if (fileToUpload) {
      try {
        toast.loading(t("chats.toast.optimizingFile"), { id: "room-media-opt" });
        fileForUpload = await optimizeMediaFile(fileToUpload);
        toast.dismiss("room-media-opt");
      } catch {
        toast.dismiss("room-media-opt");
      }
    }

    try {
      let sendContent = text;
      try {
        await ensureIdentityKeys(senderId);
        const encrypted = await encryptRoomContent(senderId, roomId, text);
        if (encrypted) sendContent = encrypted;
      } catch {
        /* fallback plaintext */
      }

      let res: { message?: RoomMessage } | null = null;
      if (fileForUpload) {
        const formData = new FormData();
        formData.append("content", sendContent);
        if (replyPayload) formData.append("replyToId", replyPayload._id);
        if (fileType) formData.append("fileType", fileType);
        formData.append("file", fileForUpload);
        res = await request<{ message?: RoomMessage }>(
          "POST",
          `/chat-rooms/${roomId}/messages`,
          formData
        );
      } else {
        res = await request<{ message?: RoomMessage }>(
          "POST",
          `/chat-rooms/${roomId}/messages`,
          {
            content: sendContent,
            ...(replyPayload ? { replyToId: replyPayload._id } : {}),
          }
        );
      }

      if (res?.message?._id) {
        let serverMsg = res.message;
        try {
          const plain = await decryptRoomContent(
            senderId,
            roomId,
            serverMsg.content || text
          );
          serverMsg = { ...serverMsg, content: plain || text };
        } catch {
          serverMsg = { ...serverMsg, content: text };
        }
        setMessages((prev) =>
          prev.map((m) => (m._id === tempId ? serverMsg : m))
        );
      } else {
        await loadMessages();
      }
    } catch (err: unknown) {
      setMessages((prev) => prev.filter((m) => m._id !== tempId));
      setNewMessage(text);
      if (replyPayload && savedReply) {
        setReplyingTo(savedReply);
      }
      const message =
        (err as { response?: { data?: { error?: string; message?: string } } })
          ?.response?.data?.error ||
        (err as { response?: { data?: { message?: string } } })?.response?.data
          ?.message;
      toast.error(message || t("chats.toast.sendFailed"));
    } finally {
      setSending(false);
    }
  };

  const handleFileSelection = (e: React.ChangeEvent<HTMLInputElement>) => {
    const picked = Array.from(e.target.files || []);
    if (!picked.length) return;
    const MAX_SIZE = 200 * 1024 * 1024;
    const valid = picked.filter((f) => {
      if (f.size > MAX_SIZE) {
        toast.error(t("chats.toast.fileTooLargeNamed", { name: f.name }));
        return false;
      }
      return true;
    });
    if (!valid.length) return;

    const videos = valid.filter((f) => f.type.startsWith("video/"));
    const images = valid.filter((f) => f.type.startsWith("image/"));
    const others = valid.filter(
      (f) => !f.type.startsWith("video/") && !f.type.startsWith("image/")
    );

    if (videos.length === 1 && images.length === 0 && others.length === 0) {
      setPendingMediaPreview({ file: videos[0], kind: "video" });
      return;
    }

    if (others.length === 1 && images.length === 0 && videos.length === 0) {
      setPendingMediaPreview({ file: others[0], kind: "file" });
      return;
    }

    if (images.length) {
      setPendingImages((prev) => [...prev, ...images].slice(0, 10));
      setShowMultiModal(true);
    }
  };

  const handleVoiceUpload = (blob: Blob) => {
    const voiceFile = new File([blob], "voice-message.webm", {
      type: "audio/webm",
    });
    void processSendMessage(voiceFile, "voice");
  };

  const sendAllImages = async () => {
    const batch = [...pendingImages];
    setShowMultiModal(false);
    setPendingImages([]);
    for (const file of batch) {
      await processSendMessage(file, "image");
    }
  };

  const sendMessage = () => {
    if (pendingImages.length > 0) {
      void sendAllImages();
      return;
    }
    void processSendMessage(null);
  };

  const canAddMembers =
    room?.visibility === "private" &&
    room?.createdBy &&
    String(room.createdBy) === String(user?._id);

  const openActionFor = (msg: RoomMessage) => {
    if (String(msg._id).startsWith("temp-")) return;
    setSelectedMessage(msg);
    setActionMode(true);
  };

  const closeActionMode = () => {
    setActionMode(false);
    setSelectedMessage(null);
  };

  const copySelectedMessage = async () => {
    if (!selectedMessage) return;
    const text = getCopyableRoomMessageText(selectedMessage.content);
    if (!text) {
      toast.error(t("chats.toast.copyEmpty"));
      return;
    }

    const copied = await copyTextToClipboard(text);
    if (copied) {
      toast.success(t("chats.toast.copySuccess"));
      closeActionMode();
      return;
    }

    toast.error(t("chats.toast.copyFailed"));
  };

  const reactToMessage = async (emoji: string) => {
    if (!selectedMessage?._id || selectedMessage._id.startsWith("temp-")) return;
    try {
      const res = await request<{ message?: RoomMessage }>(
        "POST",
        `/chat-rooms/${roomId}/messages/reaction`,
        { messageId: selectedMessage._id, emoji }
      );
      const updated = res?.message;
      if (updated?._id) {
        setMessages((prev) =>
          prev.map((m) => (m._id === updated._id ? { ...m, ...updated } : m))
        );
      }
      closeActionMode();
    } catch {
      toast.error(t("chats.toast.reactFailed"));
    }
  };

  return (
    <LocalePageShell>
    <Container className="chat-page-bg flex h-[100dvh] max-h-[100dvh] flex-col overflow-hidden !px-0">
      <header
        ref={headerRef}
        className="fixed left-0 right-0 top-0 z-40 border-b border-white/10 bg-black/20 px-3 pb-3 pt-[calc(0.75rem+env(safe-area-inset-top))] backdrop-blur-md"
      >
        <div className="mx-auto flex w-full max-w-lg items-center gap-3">
          <Link
            href="/settings/chats/rooms"
            className="gentle-transition flex h-10 w-10 items-center justify-center rounded-full bg-white/10 active:scale-95"
            aria-label={t("chats.actions.back")}
          >
            <BoldIcon name="arrow-right-2" size={20} tinted className="text-white" />
          </Link>
          <div className="min-w-0 flex flex-1 items-center gap-3">
            <ProfileAvatar
              src={room?.image}
              alt={room?.title ?? t("chats.chatRoom")}
              size="sm"
              rounded="full"
              className="h-10 w-10 shrink-0"
            />
            <div className="min-w-0 flex-1">
              <h1 className="truncate text-base font-bold text-white">
                {room?.title ?? t("chats.chatRoom")}
              </h1>
              {room?.description ? (
                <p className="truncate text-[11px] text-white/70">
                  {room.description}
                </p>
              ) : null}
              {room?.onlineCount != null ? (
                <p className="text-[10px] text-[#22c55e]">
                  {t("chats.onlineCount", { count: room.onlineCount })}
                </p>
              ) : null}
            </div>
          </div>
          {canAddMembers ? (
            <button
              type="button"
              onClick={() => setShowAddMembers(true)}
              className="gentle-transition flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-white/10 active:scale-95"
              aria-label={t("chats.aria.addUser")}
            >
              <BoldIcon name="user-add" size={20} tinted className="text-white" />
            </button>
          ) : null}
        </div>
        {room?.members?.length ? (
          <div className="mx-auto mt-3 flex w-full max-w-lg gap-2 overflow-x-auto pb-1">
            {room.members.slice(0, 12).map((member) => (
              <Link
                key={member._id}
                href={`/users/${member.user_name}`}
                className="flex shrink-0 flex-col items-center gap-1"
                title={member.user_name}
              >
                <ProfileAvatar
                  src={member.profile_image}
                  alt={member.user_name || ""}
                  size="sm"
                  rounded="full"
                  className="h-10 w-10"
                />
                <span className="max-w-[56px] truncate text-[10px] text-white/80">
                  {member.user_name}
                </span>
              </Link>
            ))}
          </div>
        ) : null}
      </header>

      <div
        ref={scrollRef}
        dir="rtl"
        className="chat-thread-messages custom-scrollbar relative mx-auto flex min-h-0 w-full max-w-lg flex-1 flex-col overflow-y-auto px-3"
        style={{
          paddingTop: headerOffset,
          paddingBottom: `calc(5.5rem + env(safe-area-inset-bottom, 0px))`,
        }}
      >
        {loading ? (
          <div className="flex flex-1 items-center justify-center py-20">
            <IOSSpinner size={28} />
          </div>
        ) : messages.length === 0 ? (
          <p className="py-16 text-center text-sm text-neutral-500">
            {t("chats.threadEmpty")}
          </p>
        ) : (
          <div className="flex flex-col gap-2 pb-2">
            {messages.map((msg) => {
              const isMine = String(msg.senderId) === String(user?._id);
              const senderName =
                [msg.sender?.first_name, msg.sender?.last_name]
                  .filter(Boolean)
                  .join(" ") ||
                msg.sender?.user_name ||
                t("chats.user");
              const reactionGroups = groupReactions(msg.reactions);

              return (
                <RoomMessageRow
                  key={msg._id}
                  msg={msg}
                  isMine={isMine}
                  senderName={senderName}
                  reactionGroups={reactionGroups}
                  messageRefs={messageRefs}
                  onOpenAction={openActionFor}
                  onSwipeReply={(target) => setReplyingTo(target)}
                  onScrollToReply={() => scrollToMessage(msg.replyTo!._id)}
                />
              );
            })}
          </div>
        )}
      </div>

      {actionMode ? (
        <ChatActionBar
          hideForward
          onReply={() => {
            if (selectedMessage) setReplyingTo(selectedMessage);
            closeActionMode();
          }}
          onCopy={copySelectedMessage}
          onCancel={closeActionMode}
          onReact={reactToMessage}
        />
      ) : (
        <MessageInput
          newMessage={newMessage}
          setNewMessage={setNewMessage}
          sendMessage={sendMessage}
          handleFileSelection={handleFileSelection}
          handleVoiceUpload={handleVoiceUpload}
          isChatThread
          replyingTo={replyingTo}
          onCancelReply={() => setReplyingTo(null)}
          onReplyPreviewClick={() => {
            if (replyingTo?._id) scrollToMessage(replyingTo._id);
          }}
          replyLabel={
            replyingTo
              ? replyingTo.content?.slice(0, 80) || t("chats.message")
              : undefined
          }
        />
      )}

      <MultiImageModal
        isOpen={showMultiModal}
        files={pendingImages}
        onCancel={() => {
          setShowMultiModal(false);
          setPendingImages([]);
        }}
        onConfirm={() => void sendAllImages()}
        onRemove={(index) =>
          setPendingImages((prev) => {
            const next = prev.filter((_, i) => i !== index);
            if (next.length === 0) setShowMultiModal(false);
            return next;
          })
        }
      />

      <MediaPreviewModal
        isOpen={!!pendingMediaPreview}
        file={pendingMediaPreview?.file ?? null}
        kind={pendingMediaPreview?.kind ?? "file"}
        onCancel={() => setPendingMediaPreview(null)}
        onConfirm={() => {
          if (!pendingMediaPreview) return;
          const { file, kind } = pendingMediaPreview;
          setPendingMediaPreview(null);
          void processSendMessage(file, kind);
        }}
      />

      <AddChatRoomMembersModal
        open={showAddMembers}
        onClose={() => setShowAddMembers(false)}
        roomId={roomId}
        existingMemberIds={room?.members?.map((member) => member._id) ?? []}
        onAdded={() => void loadRoom()}
      />
    </Container>
    </LocalePageShell>
  );
}
