import { Server } from "socket.io";
import NameSpaceManager from "./Utils/NameSpaceManager/index.js";
import User from "./DB/Model/user.model.js";
import mongoose from "mongoose";
import ChatController from "./Controller/chat.controller.js";
import logger from "./Config/logger.js";
import fs from "fs";
import CustomError from "./Utils/ResponseHandler/CustomError.js";
import { joseJwtVerify } from "./Utils/AccessTokenManagement/Tokens.js";
// import Ride from "./DB/Model/ride.model.js";
import { fileURLToPath } from "url";
import path, { dirname } from "path";
import push_notification from "./Config/push_notification.js";
import Chat from "./DB/Model/chat.modal.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const newPath = __dirname.slice(0, __dirname.length - 4)
console.log(newPath)

let io;
export function initializeSocket(server) {


  io = new Server(server, {
    cors: {
      origin: "*",
      methods: ["GET", "POST"],
    },
    maxHttpBufferSize: 1e8,
  }).use(async (socket, next) => {
    if (socket.handshake.query && socket.handshake.query.token) {
      const decoded = await joseJwtVerify(socket.handshake.query.token);
      if (!decoded) {
        return next(CustomError.unauthorized());
      }
      const userData = await User.findOne({
        _id: decoded.payload.sub,
        is_verified: true,
      }).select("full_name email userType");
      if (!userData) {
        return next(CustomError.unauthorized());
      }
      socket.user = userData;
      return next();
    } else {
      next(CustomError.unauthorized());
    }
  });


  // Default 10
  // Set 1000
  // Reason : Memory leak

  io.setMaxListeners(1000000);
  const namespaceManager = new NameSpaceManager(io);

  io.on("connection", (socket) => {
    console.log(`Client connected: ${socket.user._id}`.magenta);

    // * Create a Namespace of user for personal channel
    socket.on("connect-user", async (data) => {
      const user_id = socket.user._id;
      const userType = socket.user.userType;
      const user = await User.findOne({ _id: user_id, userType });
      if (!user) return socket.emit("error", `Error! No User found!`);
      let namespace;
      if (userType == "user") {
        namespace = namespaceManager.createUserNamespace(user_id);
      }
      //  else if (userType == "service_provider") {
      //   namespace = namespaceManager.createserviceProviderNamespace(user_id);
      // }

      await User.updateOne(
        { _id: new mongoose.Types.ObjectId(user_id) },
        { $set: { status: "online" } },
        { new: true }
      );

      console.log(`Namespace Created: ${socket?.user?._id}`.magenta);

      namespace.on("connection", (userSocket) => {
        userSocket.on("disconnect", async () => {
          console.log(`Client disconnected: ${userSocket.id}`.red);

          await User.updateOne(
            { _id: new mongoose.Types.ObjectId(user_id) },
            { $set: { status: "offline" } },
            { new: true }
          );

          if (userType == "user") {
            return namespaceManager.disconnectUserNamespace(user_id);
          }
          // else if (userType == "service_provider") {
          //   return namespaceManager.disconnectserviceProviderNamespace(user_id);
          // }
        });
      });
    });
    // * Socket related to chat
    socket.on("search-user", async (data) => {
      try {
        if (!data.user_id)
          return socket.emit("error", `Error: Current user id required!`);
        const user = await User.findById(data.user_id);
        if (!user) return socket.emit("error", "Error: No User Found!");

        let userList = null;

        if (data.full_name == undefined || !data.full_name)
          return socket.emit("searched-user-list", userList);

        const fullNameRegex = new RegExp(data.full_name, "i");
        userList = await User.find({ full_name: fullNameRegex }).select(
          "full_name image email status"
        );

        if (!userList) return socket.emit("error", `Error! No user found!`);

        let namespace;
        if (user.userType == "user") {
          namespace = io.of(`/userNamespace/${data.user_id}`);
        }
        // else {
        //   namespace = io.of(`/serviceProviderNamespace/${data.user_id}`);
        // }
        return namespace.emit("searched-user-list", userList);
      } catch (error) {
        console.log("Error: Chat event/search-user", error);
      }
    });

    socket.on("chat-list", async (data, ack) => {
      try {
        if (!mongoose.isValidObjectId(data.user_id)) {
          return socket.emit("error", `Error! Invalid User ID!`);
        }
        const user = await User.findById(data.user_id);
        const chatList = await ChatController.index(data.user_id);
        console.log(chatList, "chatList");

        if (!chatList)
          return socket.emit("error", "Error: No chat record found.");
        // let namespace;
        // if (user.userType == "user") {
        //   namespace = io.of(`/userNamespace/${data.user_id}`);
        // }
        // else {
        //   namespace = io.of(`/serviceProviderNamespace/${data.user_id}`);
        // }
        // return namespace.emit("chat-list", chatList);
        ack(chatList)
      } catch (error) {
        console.log("Error: Chat event/chat-list", error);
      }
    });

    socket.on("join-chat-room", async (data, ack) => {
      try {
        const chat = await ChatController.createChat(data);

        console.log(chat, "chat");

        if (!chat || !chat.success) {
          return socket.emit("error", chat || { message: "Chat creation failed" });
        }

        socket.join(chat.data._id.toString());

        const chatNamespace = namespaceManager.createChatNamespace(
          chat.data._id.toString()
        );
        logger.info(`Join chat of id ${chat.data._id}`.cyan);

        const chatMessages = await ChatController.getChatMessages(chat.data._id);
        if (!chatMessages.success)
          return socket.emit("error", chatMessages);

        return ack({
          chat: chat.data,
          messages: chatMessages.data,
        });
      } catch (error) {
        console.log("Error: Chat event/join-chat-room", error);
      }
    });


    socket.on("leave-chat-room", async (data) => {
      try {
        return logger.info(`Left chat of id ${data.chat_id}`.red);
      } catch (error) {
        console.log("Error: Chat event/leave-chat-room", error);
      }
    });

    socket.on("send-message", async (data, ack) => {
      try {
        const chatType = await Chat.findById(data.chat_id);
        if (!mongoose.isValidObjectId(data.sender)) {
          return ack && ack({ success: false, message: "Invalid Sender ID!" });
        }
        if (data.chatType === "private") {
          if (!mongoose.isValidObjectId(data.receiver)) {
            return ack && ack({ success: false, message: "Invalid Receiver ID!" });
          }
        }
        let payload = {
          content: data.content,
          chat_id: data.chat_id,
          sender: data.sender,
          receiver: data.receiver,
        };
        if (data.image) {
          payload.image = data.image;
        }
        const sendMessageResponse = await ChatController.sendMessage(payload);
        if (!sendMessageResponse.success) {
          return ack && ack({ success: false, message: sendMessageResponse.message });
        }
        const chatNamespace = io.of(`/chatNamespace/${data.chat_id}`);
        chatNamespace.emit("receive-message", sendMessageResponse.data);

        let chatListSender, chatListReceiver;

        if (data.chatType === "private") {
          chatListSender = await ChatController.index(data.sender);
          chatListReceiver = await ChatController.index(data.receiver);
        } else if (data.chatType === "group") {
          chatListSender = await ChatController.index(data.sender);

          const groupMembers = await ChatController.getGroupMembers(data.chat_id);

          chatListReceiver = await Promise.all(
            groupMembers.map((member) => ChatController.index(member._id))
          );
        }

        ack &&
          ack({
            success: true,
            message: "Message sent successfully.",
            data: {
              chatListSender,
              chatListReceiver,
              message: sendMessageResponse.data,
            },
          });

        if (data.chatType === "private") {
          chatNamespace.emit("chat-list-update", {
            sender: data.sender,
            chatList: chatListSender,
          });

          chatNamespace.emit("chat-list-update", {
            receiver: data.receiver,
            chatList: chatListReceiver,
          });
        } else if (data.chatType === "group") {
          chatNamespace.emit("chat-list-update", {
            sender: data.sender,
            chatList: chatListSender,
          });

          chatNamespace.emit("chat-list-update", {
            groupMembers: chatListReceiver,
          });
        }
      } catch (error) {
        console.error("❌ Error: Chat event/send-message", error);
        ack && ack({ success: false, message: "Internal server error." });
      }
    });


    socket.on("send-media-message", async (data) => {
      try {
        // Step 1: Basic Validation
        if (
          !data?.image?.buffer ||
          !data?.image?.filename ||
          !data?.image?.fileType
        ) {
          return socket.emit("error", {
            message: "Buffer, filename, and fileType are required in image.",
            success: false,
          });
        }

        // Step 2: File Type Check
        const allowedTypes = ["image/jpeg", "image/jpg", "image/png"];
        if (!allowedTypes.includes(data.image.fileType)) {
          return socket.emit("error", {
            message: `Only filetypes ${allowedTypes.join(", ")} are allowed.`,
            success: false,
          });
        }

        // Step 3: Extract filename
        let actualFilename;
        if (typeof data.image.filename === "string") {
          actualFilename = data.image.filename;
        } else if (
          typeof data.image.filename === "object" &&
          typeof data.image.filename.name === "string"
        ) {
          actualFilename = data.image.filename.name;
        } else {
          actualFilename = `image_${Date.now()}.png`; // Default fallback
        }

        if (!actualFilename || /\x00/.test(actualFilename)) {
          return socket.emit("error", {
            message: "Filename is invalid or contains forbidden characters.",
            success: false,
          });
        }

        // Step 4: Convert base64 buffer to binary
        const base64Data = data.image.buffer.split(",")[1]; // Remove prefix like 'data:image/png;base64,'
        let imageBuffer;
        try {
          imageBuffer = Buffer.from(base64Data, "base64");
        } catch (err) {
          return socket.emit("error", {
            message: "Invalid image buffer format.",
            success: false,
          });
        }

        // Step 5: Write File
        const uploadsDir = path.join(newPath, "Uploads");
        if (!fs.existsSync(uploadsDir)) fs.mkdirSync(uploadsDir, { recursive: true });

        const filePath = path.join(uploadsDir, actualFilename);
        fs.writeFileSync(filePath, imageBuffer);

        console.log("✅ Image saved at:", filePath);

        // Step 6: Save to DB using ChatController
        const sendMessageResponse = await ChatController.sendMessage({
          content: data.content,
          chat_id: data.chat_id,
          sender: data.sender,
          image: {
            filename: actualFilename,
            fileType: data.image.fileType,
          },
        });
        console.log(sendMessageResponse, "sendMessageResponse");

        if (!sendMessageResponse.success) {
          return socket.emit("error", sendMessageResponse);
        }

        // Step 7: Emit message to chat namespace
        const chatNamespace = io.of(`/chatNamespace/${data.chat_id}`);
        chatNamespace.emit("receive-message", sendMessageResponse.data);

        // Step 8: Create chat if needed
        const chat = await ChatController.createChat({
          sender: data.sender,
          receiver: data.receiver,
        });

        if (!chat.success) return socket.emit("error", chat);

        // Step 9: Fetch updated chat lists
        const chatListReceiver = await ChatController.index(data.receiver);
        const chatListSender = await ChatController.index(data.sender);

        if (!chatListReceiver || !chatListSender) {
          return socket.emit("error", {
            message: "Chat lists could not be fetched.",
            success: false,
          });
        }

        chatNamespace.emit("chat-list", {
          [data.sender]: chatListSender,
          [data.receiver]: chatListReceiver,
        });

      } catch (error) {
        console.error("❌ Socket error [send-media-message]:", error);
        socket.emit("error", {
          message: "Unexpected server error.",
          error: error?.message || error,
          success: false,
        });
      }
    });

    socket.on("message-seen", async (data) => {
      try {
        const messagesSeenResponse = await ChatController.messageSeen(data);
        const chatNamespace = io.of(`/chatNamespace/${data.chat_id}`);
        chatNamespace.emit("message-seen", messagesSeenResponse);
      } catch (error) {
        console.log("Error: Chat event/message-seen", error);
      }
    });

    socket.on("disconnect", () => {
      socket.user = null;
      console.log(`Client disconnected: ${socket.id}`.red);
      // namespaceManager.destroyChatNamespace(user_id);
      // console.log(`User ${user_id} disconnected from their namespace`);
    });
  });
}

export async function sendMessageToNamespace(user, messageObject) {
  console.log(messageObject);
  return await new Promise((resolve, reject) => {
    try {
      if (io) {
        let namespace;
        if (user.userType == "user") {
          namespace = io.of(
            `/serviceProviderNamespace/${messageObject.payload.namespaces.serviceProvider_id.toString()}`
          );
        } else {
          namespace = io.of(
            `/userNamespace/${messageObject.payload.namespaces.user_id.toString()}`
          );
        }
        return namespace.emit(
          messageObject.event,
          messageObject.payload,
          (ack) => resolve(messageObject.payload)
        );
        // io.to(socketId).emit(messageObject.event, messageObject.data);
      } else {
        reject("Socket.io not initialized.");
        console.log("Socket.io not initialized.");
      }
    } catch (e) {
      console.log("e", e);
      reject(e);
    }
  });
}
