import { useCallback, useEffect, useRef, useState } from "react";
import type { Socket } from "socket.io-client";
import type { ChatMessage } from "@/types/chat";
import { getOrCreateChatSocket } from "@/utils/chatSocketClient";

export interface ChatSocketHandlers {
  onNewMessage?: (payload: {
    conversationId: string;
    message: ChatMessage;
  }) => void;
  onInboxUpdated?: (payload: { conversationId: string }) => void;
  onMessagesRead?: (payload: {
    userId: string;
    conversationId: string;
    readCount: number;
  }) => void;
  onMessageDeleted?: (payload: {
    conversationId: string;
    messageId: string;
  }) => void;
  onMessageUpdated?: (payload: {
    conversationId: string;
    messageId: string;
    updates: Partial<ChatMessage>;
  }) => void;
  onUserTyping?: (payload: {
    userId: string;
    conversationId: string;
    isTyping: boolean;
  }) => void;
  onUserStatusChanged?: (payload: {
    userId: string;
    status: "online" | "offline";
    lastSeen?: string;
  }) => void;
}

export function useChatSocket(
  token: string | undefined,
  activeConversationId: string | null,
  handlers: ChatSocketHandlers
) {
  const [socketReady, setSocketReady] = useState(false);
  const handlersRef = useRef(handlers);
  const activeConversationIdRef = useRef(activeConversationId);
  const joinedConversationIdRef = useRef<string | null>(null);

  handlersRef.current = handlers;
  activeConversationIdRef.current = activeConversationId;

  const syncActiveConversationRoom = useCallback((socket: Socket) => {
    const targetId = activeConversationIdRef.current;
    const joinedId = joinedConversationIdRef.current;

    if (joinedId && joinedId !== targetId) {
      socket.emit("leave-conversation", { conversationId: joinedId });
      joinedConversationIdRef.current = null;
    }

    if (targetId && joinedConversationIdRef.current !== targetId) {
      socket.emit("join-conversation", { conversationId: targetId });
      joinedConversationIdRef.current = targetId;
    }
  }, []);

  const leaveConversation = useCallback(
    (conversationId: string) => {
      const socket = getOrCreateChatSocket(token);
      if (!socket || !conversationId) return;
      if (joinedConversationIdRef.current === conversationId) {
        socket.emit("leave-conversation", { conversationId });
        joinedConversationIdRef.current = null;
      }
      if (activeConversationIdRef.current === conversationId) {
        activeConversationIdRef.current = null;
      }
    },
    [token]
  );

  const emitTypingStart = useCallback(
    (conversationId: string) => {
      getOrCreateChatSocket(token)?.emit("typing-start", { conversationId });
    },
    [token]
  );

  const emitTypingStop = useCallback(
    (conversationId: string) => {
      getOrCreateChatSocket(token)?.emit("typing-stop", { conversationId });
    },
    [token]
  );

  const emitMarkRead = useCallback(
    (conversationId: string) => {
      const socket = getOrCreateChatSocket(token);
      if (socket?.connected) {
        socket.emit("mark-read", { conversationId });
      }
    },
    [token]
  );

  useEffect(() => {
    if (!token) {
      setSocketReady(false);
      return;
    }

    const socket = getOrCreateChatSocket(token);
    if (!socket) return;

    const onConnect = () => {
      setSocketReady(true);
      joinedConversationIdRef.current = null;
      syncActiveConversationRoom(socket);
    };

    const onDisconnect = () => {
      setSocketReady(false);
      joinedConversationIdRef.current = null;
    };

    const onNewMessage = (payload: {
      conversationId: string;
      message: ChatMessage;
    }) => handlersRef.current.onNewMessage?.(payload);

    const onInboxUpdated = (payload: { conversationId: string }) =>
      handlersRef.current.onInboxUpdated?.(payload);

    const onMessagesRead = (payload: {
      userId: string;
      conversationId: string;
      readCount: number;
    }) => handlersRef.current.onMessagesRead?.(payload);

    const onMessageDeleted = (payload: {
      conversationId: string;
      messageId: string;
    }) => handlersRef.current.onMessageDeleted?.(payload);

    const onMessageUpdated = (payload: {
      conversationId: string;
      messageId: string;
      updates: Partial<ChatMessage>;
    }) => handlersRef.current.onMessageUpdated?.(payload);

    const onUserTyping = (payload: {
      userId: string;
      conversationId: string;
      isTyping: boolean;
    }) => handlersRef.current.onUserTyping?.(payload);

    const onUserStatusChanged = (payload: {
      userId: string;
      status: "online" | "offline";
      lastSeen?: string;
    }) => handlersRef.current.onUserStatusChanged?.(payload);

    socket.on("connect", onConnect);
    socket.on("disconnect", onDisconnect);
    socket.on("new-message", onNewMessage);
    socket.on("inbox-updated", onInboxUpdated);
    socket.on("messages-read", onMessagesRead);
    socket.on("message-deleted", onMessageDeleted);
    socket.on("message-updated", onMessageUpdated);
    socket.on("user-typing", onUserTyping);
    socket.on("user-status-changed", onUserStatusChanged);

    if (socket.connected) onConnect();

    return () => {
      const joinedId = joinedConversationIdRef.current;
      if (socket.connected && joinedId) {
        socket.emit("leave-conversation", { conversationId: joinedId });
      }
      joinedConversationIdRef.current = null;

      socket.off("connect", onConnect);
      socket.off("disconnect", onDisconnect);
      socket.off("new-message", onNewMessage);
      socket.off("inbox-updated", onInboxUpdated);
      socket.off("messages-read", onMessagesRead);
      socket.off("message-deleted", onMessageDeleted);
      socket.off("message-updated", onMessageUpdated);
      socket.off("user-typing", onUserTyping);
      socket.off("user-status-changed", onUserStatusChanged);
    };
  }, [token, syncActiveConversationRoom]);

  useEffect(() => {
    if (!token || !socketReady) return;
    const socket = getOrCreateChatSocket(token);
    if (!socket?.connected) return;
    syncActiveConversationRoom(socket);
  }, [token, socketReady, activeConversationId, syncActiveConversationRoom]);

  return {
    socketReady,
    leaveConversation,
    emitTypingStart,
    emitTypingStop,
    emitMarkRead,
  };
}
