import { prisma } from "@/config/database.js";
import { checkMessageRateLimit } from "@/middleware/rateLimiter.js";
import { ChatService } from "@/services/chatService.js";
import { MessageService } from "@/services/messageService.js";
import { AuthenticatedSocket, CreateChatRoomRequest, ReactToMessageRequest, SendMessageRequest } from "@/types/index.js";
import { Server } from "socket.io";

export const handleConnection = async (io: Server, socket: AuthenticatedSocket) => {
  console.log(`User connected: ${socket.user.username} (${socket.user.id})`);

  socket.join(socket.userId);

  await updateUserOnlineStatus(socket.userId, true);

  const onlineUsers = await prisma.user.findMany({
    where: { isOnline: true },
    select: { id: true, username: true },
  });

  socket.emit("online_users", onlineUsers);

  await joinUserRooms(socket);

  socket.on("send_message", (data: SendMessageRequest) => handleSendMessage(io, socket, data));

  socket.on("delete_message", (data) => handleDeleteMessage(io, socket, data));

  socket.on("react_to_message", (data: ReactToMessageRequest) => handleReactToMessage(io, socket, data));

  socket.on("typing_start", (data: { roomId: string; username: string }) => handleTypingStart(io, socket, data));
  socket.on("typing_stop", (data: { roomId: string; username: string }) => handleTypingStop(io, socket, data));

  socket.on("join_room", (roomId: string) => handleJoinRoom(socket, roomId));
  socket.on("leave_room", (roomId: string) => handleLeaveRoom(socket, roomId));

  socket.on("messages_seen", (data: { roomId: string; messageIds: string[] }) => handleMessagesSeen(io, socket, data));

  socket.on("create_chat_room", (data: CreateChatRoomRequest) => handleCreateChatRoom(io, socket, data));

  socket.on("disconnect", () => handleDisconnect(socket));
};

const handleTypingStart = async (io: Server, socket: AuthenticatedSocket, data: { roomId: string; username: string }) => {
  try {
    const isMember = await ChatService.checkRoomMembership(socket.userId, data.roomId);
    if (!isMember) return;

    io.to(data.roomId).emit("user_typing", {
      roomId: data.roomId,
      username: data.username,
    });
  } catch (error) {
    console.error(`Error handling typing start: ${error}`);
  }
};

const handleTypingStop = async (io: Server, socket: AuthenticatedSocket, data: { roomId: string; username: string }) => {
  try {
    const isMember = await ChatService.checkRoomMembership(socket.userId, data.roomId);
    if (!isMember) return;

    io.to(data.roomId).emit("user_stopped_typing", {
      roomId: data.roomId,
      username: data.username,
    });
  } catch (error) {
    console.error(`Error handling typing stop: ${error}`);
  }
};

const updateUserOnlineStatus = async (userId: string, isOnline: boolean) => {
  try {
    await prisma.user.update({
      where: { id: userId },
      data: {
        isOnline,
        lastSeen: new Date(),
      },
    });
  } catch (error) {
    console.error(`Error updating user online status: ${error}`);
  }
};

const joinUserRooms = async (socket: AuthenticatedSocket) => {
  try {
    const userRooms = await prisma.chatRoomMember.findMany({
      where: { userId: socket.userId },
      include: { room: true },
    });

    userRooms.forEach(async (member) => {
      await socket.join(member.roomId);
      socket.to(member.roomId).emit("user_online", {
        userId: socket.userId,
        username: socket.user.username,
      });
    });
  } catch (error) {
    console.error(`Error joining user rooms: ${error}`);
  }
};

const handleSendMessage = async (io: Server, socket: AuthenticatedSocket, data: SendMessageRequest) => {
  try {
    if (!data.roomId) {
      socket.emit("error", { message: "Room ID is required to send a message" });
      return;
    }

    if (data.image && data.image.length > 2 * 1024 * 1024) {
      socket.emit("error", { message: "Image size exceeds 2MB limit" });
      return;
    }

    if (!data.content && !data.image) {
      socket.emit("error", { message: "Message cannot be empty" });
      return;
    }

    if (!checkMessageRateLimit(socket)) return;

    const isMember = await ChatService.checkRoomMembership(socket.userId, data.roomId);

    if (!isMember) {
      socket.emit("error", { message: "Not authorized to send messages to this room" });
      return;
    }

    const result = await MessageService.sendMessage(socket.userId, data);

    if (result.success) {
      io.to(data.roomId).emit("new_message", result.data);

      io.to(data.roomId).emit("user_stopped_typing", {
        roomId: data.roomId,
        username: socket.user.username,
      });
    } else {
      socket.emit("error", { message: "Failed to send message" });
    }
  } catch (error) {
    console.error(`Error handling send message: ${error}`);
    socket.emit("error", { message: "Failed to send message" });
  }
};

const handleDeleteMessage = async (io: Server, socket: AuthenticatedSocket, data: { messageId: string; roomId: string }) => {
  try {
    const result = await MessageService.deleteMessage(socket.userId, data.messageId, data.roomId);

    if (result.success) {
      io.to(data.roomId).emit("message_deleted", {
        messageId: data.messageId,
        roomId: data.roomId,
      });
    } else {
      socket.emit("error", { message: result.error });
    }
  } catch (error) {
    console.error(`Error handling delete message: ${error}`);
    socket.emit("error", { message: "Failed to delete message" });
  }
};

const handleReactToMessage = async (io: Server, socket: AuthenticatedSocket, data: ReactToMessageRequest) => {
  try {
    const result = await MessageService.reactToMessage(socket.userId, data);

    if (result.success) {
      const message = await prisma.message.findUnique({
        where: { id: data.messageId },
        select: { roomId: true },
      });

      if (message) {
        io.to(message.roomId).emit("message_reaction_updated", result.data);
      }
    } else {
      socket.emit("error", { message: result.error });
    }
  } catch (error) {
    console.error(`Error handling react to message: ${error}`);
    socket.emit("error", { message: "Failed to react to message" });
  }
};

const handleMessagesSeen = async (io: Server, socket: AuthenticatedSocket, data: { roomId: string; messageIds: string[] }) => {
  try {
    const isMember = await ChatService.checkRoomMembership(socket.userId, data.roomId);
    if (!isMember) {
      socket.emit("error", { message: "Not authorized to access this room" });
      return;
    }

    const messagesInRoom = await prisma.message.findMany({
      where: {
        id: { in: data.messageIds },
        roomId: data.roomId,
      },
    });

    const validMessageIds = messagesInRoom.map((msg) => msg.id);

    if (validMessageIds.length === 0) return;

    const result = await MessageService.markMessagesAsSeen(socket.userId, validMessageIds);

    if (result.success && result.data) {
      for (const item of result.data) {
        io.to(data.roomId).emit("message_seen_update", {
          messageId: item.messageId,
          seenBy: item.seenBy,
        });
      }
    }
  } catch (error) {
    console.error(`Error handling messages seen: ${error}`);
    socket.emit("error", { message: "Failed to mark messages as seen" });
  }
};

const handleJoinRoom = async (socket: AuthenticatedSocket, roomId: string) => {
  await socket.join(roomId);
};

const handleCreateChatRoom = async (io: Server, socket: AuthenticatedSocket, data: CreateChatRoomRequest) => {
  try {
    const result = await ChatService.createChatRoom(socket.userId, data);

    if (result.success) {
      const room = result.data;

      room.members.forEach((member: { userId: string | string[] }) => {
        io.to(member.userId).emit("chat_room_created", room);
      });

      socket.emit("chat_room_created", room);
    } else {
      socket.emit("error", { message: result.error });
    }
  } catch (error) {
    console.error(`Error creating chat room: ${error}`);
    socket.emit("error", { message: "Failed to create chat room" });
  }
};

const handleLeaveRoom = async (socket: AuthenticatedSocket, roomId: string) => {
  await socket.leave(roomId);
};

const handleDisconnect = async (socket: AuthenticatedSocket) => {
  console.log(`User disconnected: ${socket.user.username}`);

  await updateUserOnlineStatus(socket.userId, false);

  try {
    const userRooms = await prisma.chatRoomMember.findMany({
      where: {
        userId: socket.userId,
      },
    });

    userRooms.forEach((member) => {
      socket.to(member.roomId).emit("user_offline", {
        userId: socket.userId,
        username: socket.user.username,
      });

      socket.to(member.roomId).emit("user_stopped_typing", {
        roomId: member.roomId,
        username: socket.user.username,
      });
    });
  } catch (error) {
    console.error(`Error handling user disconnect: ${error}`);
  }
};
