/* eslint-disable react-hooks/rules-of-hooks */
"use client";

import Container from "@/components/elements/Container";
import useAxios from "@/hooks/useAxios";
import { User } from "@/types/types";
import React, { useState, useEffect, useRef, useCallback } from "react";
import { io } from "socket.io-client";
import { SOCKET_URL } from "@/components/main/BaseUrl";
import MessageInput from "@/components/chat/MessageInput";
import MultiImageModal from "@/components/chat/MultiImageModal";
import ChatActionBar from "@/components/chat/ChatActionBar";
import ChatDeleteBar from "@/components/chat/ChatDeleteBar";
import { useUser } from "@/hooks/useUser";
import ChatMessageList from "@/components/chat/ChatMessageList";
import ChatHeader from "@/components/chat/ChatHeader";
import dynamic from "next/dynamic";
import type { ChatMessage } from "@/components/chat/ChatMessageCard";
import toast from "react-hot-toast";
import { optimizeMediaFile } from "@/lib/media";
import ForwardMessageModal from "@/components/chat/ForwardMessageModal";
import { AnimatePresence } from "framer-motion";
import { useQueryClient } from "@tanstack/react-query";
import { chatThreadQueryKey } from "@/lib/chat/queryKeys";
import { appendMessageToThreadCache, normalizeThreadId } from "@/lib/chat/threadCache";
import { useStableMessageKeys } from "@/hooks/useStableMessageKeys";
import { getStoredUserId } from "@/lib/auth/session";
import { buildExpiresAtIso } from "@/lib/chat/timedMessages";
import { isViewOnceMediaType } from "@/lib/chat/viewOnce";
import { filterResolvedPendingMessages } from "@/lib/chat/dedupeMessages";

interface ITicketChatProps {
  params: Promise<{ id: string; username: string }>;
}

function normalizeMessage(raw: ChatMessage & { data?: ChatMessage }): ChatMessage {
  const m = (raw as { data?: ChatMessage }).data ?? raw;
  return {
    ...m,
    replyTo: m.replyTo ?? undefined,
  };
}

function TicketChat({ params }: ITicketChatProps) {
  const resolvedParams = React.use(params);
  const { request } = useAxios();
  const { username, id: chatPartnerId } = resolvedParams;
  const [newMessage, setNewMessage] = useState("");
  const [userTwoDetail, setUserTwoDetail] = useState<User>();
  const [pendingImages, setPendingImages] = useState<File[]>([]);
  const [showMultiModal, setShowMultiModal] = useState(false);
  const [pendingMessages, setPendingMessages] = useState<ChatMessage[]>([]);
  const [replyingTo, setReplyingTo] = useState<ChatMessage | null>(null);
  const [forwardMessage, setForwardMessage] = useState<ChatMessage | null>(null);
  const [selectedMessage, setSelectedMessage] = useState<ChatMessage | null>(null);
  const [actionMode, setActionMode] = useState(false);
  const [deleteMode, setDeleteMode] = useState(false);
  const [selectedDeleteIds, setSelectedDeleteIds] = useState<string[]>([]);
  const [deleteLoading, setDeleteLoading] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const [selfDestructSeconds, setSelfDestructSeconds] = useState<number | null>(
    null
  );
  const [viewOnceMedia, setViewOnceMedia] = useState(false);
  const user = useUser();
  const queryClient = useQueryClient();
  const { linkIds, getStableKey } = useStableMessageKeys();
  const [receiverId, setReceiverId] = useState(chatPartnerId);
  const [isTyping, setIsTyping] = useState(false);
  const socketRef = useRef<ReturnType<typeof io> | null>(null);
  const typingTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const activeReplyRef = useRef<ChatMessage | null>(null);

  useEffect(() => {
    if (!user?._id || !receiverId) return;
    const socket = io(SOCKET_URL, {
      transports: ["websocket", "polling"],
      reconnection: true,
      reconnectionAttempts: Infinity,
      reconnectionDelay: 800,
    });
    socketRef.current = socket;

    // عضویت در روم‌ها روی هر اتصال/اتصال مجدد تا بعد از قطعی هم پیام‌ها برسند
    const joinRooms = () => {
      socket.emit("joinChat", { userId: user._id, receiverId });
      socket.emit("joinUser", { userId: user._id });
    };
    socket.on("connect", joinRooms);
    if (socket.connected) joinRooms();

    return () => {
      socket.off("connect", joinRooms);
      socket.disconnect();
      socketRef.current = null;
    };
  }, [user?._id, receiverId]);

  const handleTyping = useCallback(() => {
    if (!socketRef.current || !user?._id || !receiverId) return;
    socketRef.current.emit("typing", {
      senderId: user._id,
      receiverId,
    });
    if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
    typingTimeoutRef.current = setTimeout(() => {
      socketRef.current?.emit("stopTyping", {
        senderId: user._id,
        receiverId,
      });
    }, 2000);
  }, [user?._id, receiverId]);

  useEffect(() => {
    const fetchStates = async () => {
      try {
        const response = await request<{ user: User }>(
          "GET",
          `/users/get/web?user_name=${username}&_t=${Date.now()}`
        );
        setUserTwoDetail(response?.user);
      } catch (err) {
        console.error("Error fetching user:", err);
      }
    };
    fetchStates();
  }, [username, request]);

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

  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(`${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) {
      void processSendMessage(videos[0], "video");
      return;
    }

    if (others.length === 1 && images.length === 0 && videos.length === 0) {
      void processSendMessage(others[0], "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",
    });
    processSendMessage(voiceFile, "voice");
  };

  const handleLocationShare = () => {
    if (!navigator.geolocation) {
      toast.error("مرورگر از موقعیت مکانی پشتیبانی نمی‌کند.");
      return;
    }
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        const locationContent = JSON.stringify({
          lat: pos.coords.latitude,
          lng: pos.coords.longitude,
          label: "موقعیت من",
        });
        processSendMessage(null, "location", locationContent);
      },
      () => toast.error("دسترسی به موقعیت مکانی داده نشد.")
    );
  };

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

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

  const buildReplyPayload = (source: ChatMessage | null) => {
    if (!source) return undefined;
    return {
      _id: source._id,
      content: source.content?.slice(0, 120) || "پیام",
      senderName:
        source.senderId === user?._id
          ? "شما"
          : `${userTwoDetail?.first_name || ""} ${userTwoDetail?.last_name || ""}`.trim(),
    };
  };

  const handleIncomingMessage = useCallback((msg: ChatMessage) => {
    setPendingMessages((prev) => filterResolvedPendingMessages([msg], prev));
  }, []);

  const confirmSentMessage = useCallback(
    (tempId: string, serverMsg: ChatMessage, replyPayload?: ChatMessage["replyTo"]) => {
      const sentMsg: ChatMessage = {
        ...serverMsg,
        status: "sent",
        replyTo: serverMsg.replyTo ?? replyPayload,
      };

      const currentUserId = normalizeThreadId(user?._id ?? getStoredUserId());
      const targetReceiverId = normalizeThreadId(
        userTwoDetail?._id ?? receiverId ?? chatPartnerId
      );

      if (currentUserId && targetReceiverId) {
        linkIds(tempId, sentMsg._id);
        queryClient.setQueryData(
          chatThreadQueryKey(currentUserId, targetReceiverId),
          (oldData) => appendMessageToThreadCache(oldData, sentMsg)
        );
      }

      setPendingMessages((prev) => prev.filter((m) => m._id !== tempId));
    },
    [user?._id, userTwoDetail?._id, receiverId, chatPartnerId, linkIds, queryClient]
  );

  const processSendMessage = async (
    fileToUpload: File | null,
    fileType?: ChatMessage["fileType"],
    contentOverride?: string
  ) => {
    const replySource = activeReplyRef.current;
    let textContent = contentOverride ?? newMessage;
    if (textContent.trim() === "" && !fileToUpload) return;

    const tempId = `temp-${Date.now()}-${Math.random()}`;
    const replyPayload = buildReplyPayload(replySource);

    const currentUserId = normalizeThreadId(user?._id ?? getStoredUserId());
    const targetReceiverId = normalizeThreadId(
      userTwoDetail?._id ?? receiverId ?? chatPartnerId
    );

    const applyViewOnce =
      viewOnceMedia && fileType && isViewOnceMediaType(fileType);

    const optimisticMsg: ChatMessage = {
      _id: tempId,
      content: textContent,
      senderId: currentUserId,
      receiverId: targetReceiverId,
      createdAt: new Date().toISOString(),
      expiresAt: buildExpiresAtIso(selfDestructSeconds),
      status: "pending",
      file: fileToUpload ? URL.createObjectURL(fileToUpload) : "",
      fileType: fileType,
      replyTo: replyPayload,
      viewOnce: applyViewOnce || undefined,
    };

    setPendingMessages((prev) => [...prev, optimisticMsg]);
    setNewMessage("");

    let fileForUpload = fileToUpload;
    if (fileToUpload && fileType !== "location") {
      try {
        toast.loading("در حال بهینه‌سازی فایل…", { id: "media-opt" });
        fileForUpload = await optimizeMediaFile(fileToUpload);
        toast.dismiss("media-opt");
      } catch {
        toast.dismiss("media-opt");
      }
    }

    const isFileUpload = fileForUpload && fileType !== "location";

    try {
      let messagePayload: Record<string, unknown> = {
        content: textContent,
        receiverId: targetReceiverId,
        senderId: currentUserId,
      };

      if (replySource?._id && !replySource._id.startsWith("temp")) {
        messagePayload.replyToId = replySource._id;
      }
      if (selfDestructSeconds) {
        messagePayload.selfDestructSeconds = selfDestructSeconds;
      }
      if (fileType) {
        messagePayload.fileType = fileType;
      }
      if (applyViewOnce) {
        messagePayload.viewOnce = true;
      }

      let response: ChatMessage | { data: ChatMessage };

      if (isFileUpload) {
        const formData = new FormData();
        for (const key in messagePayload) {
          formData.append(key, String(messagePayload[key]));
        }
        formData.append("file", fileForUpload);

        response = await request<ChatMessage | { data: ChatMessage }>(
          "post",
          "/chat/file",
          formData
        );
      } else {
        response = await request<ChatMessage | { data: ChatMessage }>(
          "POST",
          "/chat",
          messagePayload
        );
      }

      const serverMsg = normalizeMessage(response as ChatMessage);
      confirmSentMessage(tempId, serverMsg, replyPayload);
    } catch (error) {
      console.error("Error sending message:", error);
      setPendingMessages((prev) => prev.filter((m) => m._id !== tempId));
      toast.error("ارسال پیام ناموفق بود.");
      return;
    }

    setReplyingTo(null);
    activeReplyRef.current = null;
    setSelfDestructSeconds(null);
    if (applyViewOnce) setViewOnceMedia(false);
  };

  const handleExpirePending = useCallback((ids: string[]) => {
    setPendingMessages((prev) => prev.filter((m) => !ids.includes(m._id)));
  }, []);

  const openActionFor = (msg: ChatMessage) => {
    setSelectedMessage(msg);
    setActionMode(true);
  };

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

  const startReplyFor = useCallback((msg: ChatMessage) => {
    setReplyingTo(msg);
    activeReplyRef.current = msg;
    closeActionMode();
  }, []);

  const startReply = () => {
    if (!selectedMessage) return;
    startReplyFor(selectedMessage);
  };

  const startForward = () => {
    if (!selectedMessage) return;
    setForwardMessage(selectedMessage);
    closeActionMode();
  };

  const handleBlockChange = (blocked?: boolean) => {
    if (typeof blocked === "boolean") {
      setUserTwoDetail((prev) =>
        prev ? { ...prev, is_blocked: blocked } : prev
      );
    }
    void request<{ user: User }>(
      "GET",
      `/users/get/web?user_name=${username}&_t=${Date.now()}`
    ).then((res) => {
      if (res?.user) setUserTwoDetail(res.user);
    });
  };

  const startDeleteMode = () => {
    closeActionMode();
    setSearchQuery("");
    setDeleteMode(true);
    setSelectedDeleteIds([]);
  };

  const cancelDeleteMode = () => {
    setDeleteMode(false);
    setSelectedDeleteIds([]);
  };

  const toggleDeleteSelect = (msg: ChatMessage) => {
    setSelectedDeleteIds((prev) =>
      prev.includes(msg._id)
        ? prev.filter((id) => id !== msg._id)
        : [...prev, msg._id]
    );
  };

  const confirmDeleteMessages = async () => {
    if (!selectedDeleteIds.length || !user?._id) return;
    setDeleteLoading(true);
    try {
      const response = await request<{ deletedIds: string[]; message?: string }>(
        "DELETE",
        "/chat",
        { messageIds: selectedDeleteIds }
      );
      const deleted = new Set(response?.deletedIds || []);
      setPendingMessages((prev) => prev.filter((m) => !deleted.has(m._id)));
      queryClient.setQueryData(
        chatThreadQueryKey(
          normalizeThreadId(user._id),
          normalizeThreadId(chatPartnerId)
        ),
        (oldData: { pages: { messages: ChatMessage[] }[] } | undefined) => {
          if (!oldData) return oldData;
          return {
            ...oldData,
            pages: oldData.pages.map((page) => ({
              ...page,
              messages: page.messages.filter((m) => !deleted.has(m._id)),
            })),
          };
        }
      );
      toast.success(response?.message || "پیام‌ها حذف شدند");
      cancelDeleteMode();
    } catch {
      toast.error("حذف پیام‌ها ناموفق بود");
    } finally {
      setDeleteLoading(false);
    }
  };

  return (
    <Container className="chat-page-bg flex h-[100dvh] max-h-[100dvh] flex-col overflow-hidden !px-0">
      <ChatHeader
        user={userTwoDetail}
        searchQuery={searchQuery}
        onSearchChange={setSearchQuery}
        onBlockChange={handleBlockChange}
        onStartDeleteMode={startDeleteMode}
        deleteMode={deleteMode}
      />
      <ChatMessageList
          chatPartnerId={chatPartnerId}
          getStableKey={getStableKey}
          pendingMessages={pendingMessages}
          userDetail={user as User}
          userTwoDetail={userTwoDetail}
          isTyping={isTyping}
          onTypingChange={setIsTyping}
          searchQuery={searchQuery}
          selectedMessageId={selectedMessage?._id}
          onBubbleClick={openActionFor}
          onSwipeReply={startReplyFor}
          actionMode={actionMode}
          onDismissAction={closeActionMode}
          deleteMode={deleteMode}
          selectedDeleteIds={selectedDeleteIds}
          onToggleDeleteSelect={toggleDeleteSelect}
          onExpirePending={handleExpirePending}
          onIncomingMessage={handleIncomingMessage}
          extraBottomRem={
            (replyingTo ? 2.75 : 0) +
            (selfDestructSeconds != null ? 2 : 0) +
            (viewOnceMedia ? 2 : 0)
          }
      />

      <AnimatePresence mode="wait">
        {deleteMode ? (
          <ChatDeleteBar
            key="delete"
            selectedCount={selectedDeleteIds.length}
            onDelete={confirmDeleteMessages}
            onCancel={cancelDeleteMode}
            loading={deleteLoading}
          />
        ) : actionMode ? (
          <ChatActionBar
            key="actions"
            onReply={startReply}
            onForward={startForward}
            onCancel={closeActionMode}
          />
        ) : (
          <MessageInput
            key="input"
            newMessage={newMessage}
            setNewMessage={setNewMessage}
            sendMessage={sendMessage}
            handleFileSelection={handleFileSelection}
            handleVoiceUpload={handleVoiceUpload}
            onLocationShare={handleLocationShare}
            onTyping={handleTyping}
            isChatThread
            blocked_you={userTwoDetail?.blocked_you}
            replyingTo={replyingTo}
            onCancelReply={() => {
              setReplyingTo(null);
              activeReplyRef.current = null;
            }}
            replyLabel={
              replyingTo
                ? replyingTo.content?.slice(0, 80) || "پیام"
                : undefined
            }
            selfDestructSeconds={selfDestructSeconds}
            onSelfDestructChange={setSelfDestructSeconds}
            viewOnceMedia={viewOnceMedia}
            onViewOnceChange={setViewOnceMedia}
          />
        )}
      </AnimatePresence>

      <MultiImageModal
        isOpen={showMultiModal}
        files={pendingImages}
        onRemove={(i) =>
          setPendingImages((prev) => {
            const next = prev.filter((_, idx) => idx !== i);
            if (next.length === 0) setShowMultiModal(false);
            return next;
          })
        }
        onCancel={() => {
          setPendingImages([]);
          setShowMultiModal(false);
        }}
        onConfirm={sendAllImages}
        viewOnceMedia={viewOnceMedia}
        onViewOnceChange={setViewOnceMedia}
        selfDestructSeconds={selfDestructSeconds}
        onSelfDestructChange={setSelfDestructSeconds}
      />
      {forwardMessage && user?._id && (
        <ForwardMessageModal
          open={!!forwardMessage}
          onClose={() => setForwardMessage(null)}
          message={forwardMessage}
          currentUserId={user._id}
          currentReceiverId={receiverId}
        />
      )}
    </Container>
  );
}

export default dynamic(() => Promise.resolve(TicketChat), { ssr: false });
