import { WebSocketGateway, SubscribeMessage, MessageBody, WebSocketServer, ConnectedSocket } from '@nestjs/websockets';
import { Socket, Server } from "socket.io";
import { ChatService } from './chat.service';
import { ChatAuthGuard } from 'src/auth/auth.guard';
import { ChatDisappearingDto, CreateConnection, EditMessageDto, ForwardMessageDto, JoinConnection, ReadMessage, SendChatEmoji, SendMessageDto, TypingDto, UserOnlineStateDto } from './dto/socket.dto';
import { ModelsService } from 'src/models/models.service';
import { Types } from 'mongoose';
import * as moment from 'moment';
import { CommonService } from 'src/common/common.service';
import { log } from 'node:console';
import { HttpException, HttpStatus, Query, Inject, forwardRef, Global } from '@nestjs/common';
import { chatDisappearing } from './schema/connections.schema';

interface CustomSocket extends Socket {
    user_data: any; // Add your desired type for user_data property
}
@WebSocketGateway({
    cors: {
        origin: "*",
        credentials: true
    },
})
export class ChatGateway {
    private projection = { __v: 0 } as const;
    private lean_options = { lean: true };
    private new_options = { new: true };
    @WebSocketServer() server: Server;
    constructor(
        @Inject(forwardRef(() => ChatService)) private readonly chatService: ChatService,
        private readonly chatAuthService: ChatAuthGuard,
        private readonly common: CommonService,
        private readonly model: ModelsService,
    ) { }


    //You can handle their multiple operations like user online status
    // in this function we are just verify user is valid or not 
    // @WebSocketServer() server: Server;
    async handleConnection(socket: Socket) {
        try {
            let data = await this.chatAuthService.validateSocket(socket)
            socket['user_data'] = data

            console.log("hitting handleConnection")
            // Emit online state to all user's connections
            await this.emitUserOnlineState(data, true);

            socket.to(socket?.id).emit("listening_event", { data })
        }
        catch (error) {
            console.log(error, "error will be occured while connecting socket..........");
            this.handleDisconnect(socket)
            socket.to(socket?.id?.toString()).emit("listening_event", {
                event_type: "CONNECTING_SOCKET_ERROR",
                data: error
            })
        }
    }

    @SubscribeMessage("create_connection")
    async handleSendMessage(
        @ConnectedSocket() socket: CustomSocket,
        @MessageBody() payload: CreateConnection
    ) {
        try {
            let user_id = socket?.user_data;
            let create_connection = await this.chatService.createConnections(user_id, payload)
            if (create_connection) {
                
                socket.join(create_connection?.data?._id.toString());
                this.server.to(create_connection?.data?._id?.toString()).emit("listening_event", {
                    event_type: "CREATE_CONNECTION_LISTENER",
                    data: create_connection
                })
            }
            else {
                socket.to(socket?.id?.toString()).emit("listening_event", {
                    event_type: "CREATE_CONNECTION_ERROR",
                    data: "Please provide a valid creds"
                })
            }
        } catch (error) {
            socket.to(socket?.id?.toString()).emit("listening_event", {
                event_type: "CREATE_CONNECTION_ERROR",
                data: error
            })
        }
    }

    @SubscribeMessage("join_connection")
    async joinConnection(
        @ConnectedSocket() socket: CustomSocket,
        @MessageBody() payload: JoinConnection
    ) {
        try {
            let { connection_id } = payload;
            socket.join(connection_id);
            socket.emit("listening_event", { event_type: "JOIN_CONNECTION", data: { connection_id } });
        } catch (error) {
            socket.emit("listening_event", { event_type: "JOIN_CONNECTION", data: error });
        }
    }


    @SubscribeMessage("leave_connection")
    async leaveConnection(
        @ConnectedSocket() socket: CustomSocket,
        @MessageBody() payload: JoinConnection
    ) {
        try {
            let { connection_id } = payload;
            socket.leave(connection_id);
            socket.to(socket?.id).emit("listening_event", { event_type: "LEAVE_CONNECTION", data: { connection_id } });
        } catch (error) {
            socket.emit("listening_event", { evnet_type: "JOIN_CONNECTION", error: error });
        }
    }

    @SubscribeMessage("user_online_state")
    async userOnlineState(@ConnectedSocket() socket: CustomSocket, @MessageBody() payload: UserOnlineStateDto) {
        try {
            let { _id: user_id, name } = socket?.user_data;
            let { is_online } = payload;

            // Update user online status in database
            await this.model.UserModel.findOneAndUpdate(
                { _id: user_id },
                {
                    is_online: is_online,
                    socket_id: is_online ? socket?.id : null
                }
            );

            // Get all connections where this user is a member
            let userConnections = await this.model.Connections.find({
                $or: [
                    { sent_by: user_id },
                    { sent_to: user_id }
                ]
            });

            // Also get group connections where user is a member
            let groupConnections = await this.model.GroupMembers.find({
                user_id: user_id,
                is_exit_from_group: false
            }).populate('connection_id');

            // Combine all connection IDs
            let allConnectionIds = [
                ...userConnections.map(conn => conn._id.toString()),
                ...groupConnections.map((member: any) => member.connection_id?._id?.toString()).filter(Boolean)
            ];

            // Remove duplicates
            allConnectionIds = [...new Set(allConnectionIds)];

            // Emit to all connections where user is joined
            const onlineStateData = {
                name: name,
                date: {
                    is_online: is_online
                },
                user_id: user_id
            };

            allConnectionIds.forEach(connectionId => {
                this.server.to(connectionId).emit("listening_event", {
                    event_type: "USER_ONLINE_STATE_UPDATE",
                    connection_id: connectionId,
                    data: onlineStateData
                });
            });

            // socket.emit("listening_event", {
            //     event_type: "USER_ONLINE_STATE_UPDATED",
            //     data: onlineStateData
            // });

        } catch (error) {
            console.log(error, "error occurred in user online state");
            socket.emit("listening_event", {
                event_type: "USER_ONLINE_STATE_ERROR",
                data: error
            });
        }
    }

    @SubscribeMessage("typing")
    async typing(@ConnectedSocket() socket: CustomSocket, @MessageBody() payload: TypingDto) {
        try {
            let user_id = socket?.user_data;
            let { is_typing, connection_id } = payload;
            console.log("payload" ,payload)
            const projection = { socket_id: 1, name: 1 };
            const user_detail: any = await this.chatService.getUser(
                user_id,
                projection
            );
            const response = {
                user_data: user_detail,
                user_name: user_detail?.name,
                is_typing: is_typing,
            };

            console.log("hitting typing" , response)
            socket.to(connection_id?.toString()).emit("listening_event", { event_type: "TYPING_EVENT_LISTEN", data: response });
        }
        catch (error) {
            console.log("typing error" , error)
            socket.emit("listening_event", { event_type: "TYPING_EVENT_ERROR", data: error });
        }
    }

    @SubscribeMessage("send_message")
    async sendMessage(@ConnectedSocket() socket: CustomSocket, @MessageBody() payload: SendMessageDto) {
        try {
            console.log("hitting send_message" , payload)
            let { _id: user_id } = socket?.user_data;
            user_id ??= new Types.ObjectId(payload.sent_by);

            let { message, sent_to, reply_msg_id, media, connection_id } = payload;
            socket.join(connection_id);
            let firstCheckConnections = await this.model.Connections.findOne({ _id: new Types.ObjectId(connection_id) });

            console.log(firstCheckConnections, "firstCheckConnections");
            const from_user: any = await this.chatService.getUser(user_id, this.projection);
            
            let is_another_user_blocket_me = false;
            if (firstCheckConnections?.connection_type === "NORMAL") {
                let checkBlocked = await this.model.blockUsers.find({ blocked_by: user_id, blocked_to: sent_to });
                if (checkBlocked.length) throw new HttpException({ message: "Please first unblock the user" }, HttpStatus.BAD_REQUEST);
                let checkAnotherUseerBlocked = await this.model.blockUsers.countDocuments({ blocked_by: sent_to, blocked_to: user_id });
                is_another_user_blocket_me = checkAnotherUseerBlocked > 0;
            }
            
            let response_data: any;
            payload.connection_id = connection_id;
            
            if (media?.length) {
                await this.mediaMessageSave(payload, user_id, is_another_user_blocket_me, from_user, connection_id, socket)
            }
            else {
                await this.singleMessageSave(user_id, payload, connection_id, from_user, socket)
            }
        }
        catch (error) {
            console.log(error);
            socket.emit("listening_event", { event_type: "SEND_MESSAGE_ERROR", error: error });
        }
    }

    @SubscribeMessage("send_chat_emoji")
    async sendChatEmoji(@ConnectedSocket() socket: CustomSocket, @MessageBody() payload: SendChatEmoji) {
        try {
            let { _id: user_id, name } = socket?.user_data;
            let { emoji, message_id } = payload;
            console.log(message_id, "message _id 0", user_id, "user id 0");
            message_id &&= new Types.ObjectId(message_id) as any;
            let from_user = await this.model.UserModel.findOne({ _id: user_id })
            let fetchMessage = await this.model.Messages.findOne({ _id: message_id });
            if (!fetchMessage) throw new HttpException({ message: "Please provide valid message id" }, HttpStatus.BAD_REQUEST);
            
            let connection: any = await this.model.Connections.findOne({ _id: fetchMessage?.connection_id }, this.projection, this.lean_options);
            let fetchEmoji: any = await this.model.MsgReactions.findOne({ message_id: message_id, user_id: { $in: user_id }, emoji }, this.projection, this.lean_options);
            let sent_to = fetchMessage.sent_by.toString() == user_id.toString() ? fetchMessage.sent_to.toString() : fetchMessage.sent_by.toString();
            let data;
            socket.join(String(connection._id));
            if (fetchEmoji) {
                // console.log("fetch foundiiiiiiii imogiiiiiiiiiiiiii");
                if (fetchEmoji.user_id.length == 1)
                    await this.model.MsgReactions.deleteOne({ _id: fetchEmoji._id });
                if (fetchEmoji.user_id.length > 1)
                    await this.model.MsgReactions.updateOne({ _id: fetchEmoji._id }, { $pull: { user_id: user_id } });
                let response_data = await this.chatService.makeMsgResponse(message_id);
                this.server.to(String(connection._id)).emit("listening_event", {
                    event_type: "DELETE_MSG_REACTION_EMOJI",
                    connection_id: fetchMessage.connection_id,
                    data: response_data,
                });
            } else {
                let fetchExistedEmoji = await this.model.MsgReactions.findOne({ message_id: message_id, emoji });
                if (fetchExistedEmoji) {
                    data = await this.model.MsgReactions.findOneAndUpdate({ _id: fetchEmoji?._id }, { $push: { user_id: user_id } }, { new: true });
                } else {
                    let dataToSave = { user_id: [user_id], emoji, message_id: message_id };
                    data = await this.model.MsgReactions.create(dataToSave);
                }
                if (fetchMessage?.sent_by?.toString() !== user_id?.toString()) {
                    let is_muted = await this.chatService.checkMuteOrUnmute(payload?.connection_id, sent_to);
                    let fetchConnectionType = await this.model.Connections.findOne({ _id: new Types.ObjectId(fetchMessage.connection_id) });
                    if (fetchConnectionType?.connection_type == "NORMAL") {

                        let notification_data: any = {
                            type: "EMOJI_MESSAGE",
                            title: 'New Message',
                            message_id: payload?.message_id,
                            message: `${name ?? "User"} reacted "${emoji}" to ${fetchMessage?.message ?? fetchMessage?.message_type?.toLowerCase()?.replace('_', ' ')}`,
                            sent_by: user_id,
                            sent_to: sent_to,
                            sent_by_name: from_user?.name,
                            sent_by_profile_pic: from_user?.profile_pic,
                            sent_by_user: JSON.stringify({
                                name: from_user?.name,
                                profile_pic: from_user?.profile_pic
                            }),
                            connection_id: fetchMessage.connection_id,
                        };
                        this.chatService.sendMessageNotification(sent_to, notification_data, is_muted, false)
                    } else {
                        let groupMembers = await this.model.GroupMembers.distinct('user_id', { connection_id: fetchConnectionType?._id, is_exit_from_group: true, user_id: { $ne: user_id } });
                        groupMembers.forEach(async (member) => {
                            let notification_data: any = {
                                type: "EMOJI_MESSAGE",
                                title: 'New Message',
                                message_id: payload?.message_id,
                                message: `Reacted to your message "${emoji}"`,
                                sent_by: user_id,
                                sent_to: member,
                                sent_by_name: from_user?.name,
                                sent_by_profile_pic: from_user?.profile_pic,
                                sent_by_user: JSON.stringify({
                                    name: from_user?.name,
                                    profile_pic: from_user?.profile_pic
                                }),
                                connection_id: fetchMessage.connection_id,
                            };
                            this.chatService.sendMessageNotification(sent_to, notification_data, is_muted, false)
                        })
                    }

                }
                let response_data = await this.chatService.makeMsgResponse(message_id);

                this.server.to(String(connection._id)).emit("listening_event", {
                    event_type: "MSG_REACTION_EMOJI",
                    connection_id: fetchMessage.connection_id,
                    data: response_data,
                });
            }
        } catch (error) {
            console.log(error, "error will ocucre in send chat emoji functions");

            socket.emit("listening_event", { event_type: "MSG_REACTION_EMOJI_ERROR", error: error });
        }
    }

    @SubscribeMessage("read_messages")
    async readMessage(@ConnectedSocket() socket: CustomSocket, @MessageBody() payload: ReadMessage) {
        console.log("payload" , payload)
        let { message_id, connection_id } = payload
        try {
            let { _id: user_id } = socket?.user_data;
            
            if (message_id) {
                return await this.readSingleMessages(message_id, user_id);
            }
            else {
                return await this.readAllMessages(connection_id, user_id);
            }
        }
        catch (error) {
            this.server.to(String(connection_id)).emit("listening_event", {
                event_type: "READ_MESSAGE_ERROR",
                data: error
            });
        }
    }

    @SubscribeMessage("edit_message")
    async editMessage(socket: CustomSocket, payload: EditMessageDto) {
        try {
            let { _id: user_id } = socket.user_data;
            let message_detail: any = await this.chatService.editMessage(
                payload,
                user_id
            );
            socket.join(String(message_detail.connection_id));
            this.server.to(String(message_detail.connection_id)).emit("listening_event", {
                event_type: "EDIT_MESSAGE",
                connection_id: message_detail.connection_id,
                data: message_detail,
            });
        } catch (error) {
            socket.emit("edit_message", { error: error });
        }
    }

    @SubscribeMessage("delete_message")
    async EditMessage(socket: CustomSocket, payload: any) {
        try {
            let { _id: user_id } = socket.user_data;
            let { type, message_ids } = payload;
            let response: any;
            if (!message_ids?.length) throw new HttpException({ message: "To Delete messages message_ids are required" }, HttpStatus.BAD_REQUEST);
            if (Number(type) == 1) {
                response = await this.chatService.deleteMessageForEveryOne(user_id, message_ids);
                socket.join(String(response.connection_id));
                this.server.to(String(response.connection_id)).emit("listening_event", {
                    event_type: "DELETE_MESSAGES",
                    connection_id: response.connection_id,
                    data: response,
                    message_ids
                });
            } else {
                response = await this.chatService.deleteMessageForMe(user_id, message_ids);
                this.server.to(socket?.id?.toString()).emit("listening_event", {
                    event_type: "DELETE_MESSAGES",
                    connection_id: response.connection_id,
                    data: response,
                    message_ids
                });
            }

        } catch (error) {
            socket.emit("message_edit_delete", { error: error });
        }
    }

    @SubscribeMessage("forword_messages")
    async forwardMessage(socket: CustomSocket, payload: ForwardMessageDto) {
        try {
            const { _id: user_id, name } = socket.user_data;
            const { message_ids, connection_ids } = payload;

            // ------------------- Fetch Data in Single Queries -------------------
            const connections = await this.model.Connections.find({ _id: { $in: connection_ids } });
            if (connections.length !== connection_ids.length) {
                throw new HttpException({ message: "Connections not found" }, HttpStatus.BAD_REQUEST);
            }

            const messages = await this.model.Messages.find({ _id: { $in: message_ids } });
            if (messages.length !== message_ids.length) {
                throw new HttpException({ message: "Messages not found" }, HttpStatus.BAD_REQUEST);
            }

            const username = name ? `${name}` : "Someone";

            // ------------------- Loop Over Each Connection -------------------
            for (const connection of connections) {

                // Check block only once per connection
                let is_user_blocked = false;
                if (connection.connection_type === "NORMAL") {
                    const isBlocked = await this.model.blockUsers.countDocuments({
                        blocked_by: connection.sent_to,
                        blocked_to: user_id,
                    });

                    is_user_blocked = isBlocked > 0;
                }

                // ------------------- Forward Each Message -------------------
                for (const message of messages) {
                    const savedMessage = await this.chatService.saveForwardMessage(
                        connection._id.toString(),
                        user_id,
                        message,
                        is_user_blocked
                    );

                    // --------- Fetch last message (single doc), faster & cleaner ----------
                    const lastMsg = await this.model.Messages.findOne(
                        { connection_id: connection._id.toString() },
                        { message_type: 1 },
                        { sort: { _id: -1 } }
                    ).lean();

                    let lastType = lastMsg?.message_type?.toLowerCase() || "message";
                    if (lastType.includes("_")) lastType = lastType.replace("_", " ");

                    const startsWithVowel = /^[aeiou]/.test(lastType);
                    const article = startsWithVowel ? "an" : "a";

                    // ---------------- Notifications ----------------
                    if (connection.connection_type === "NORMAL") {
                        const msg = `${username} has forward ${article} ${lastType}`;

                        this.chatService.messageNotifications(
                            msg,
                            { message_id: savedMessage?.message_id.toString() },
                            name,
                            connection._id.toString(),
                            user_id,
                            savedMessage.sent_to,
                            savedMessage.message,
                            connection,
                            savedMessage
                        );
                    } else {
                        const msg = `${username} has sent you ${article} ${lastType} in ${connection.group_name}`;

                        const groupMembers = await this.model.GroupMembers.distinct("user_id", {
                            connection_id: connection._id.toString(),
                            is_exit_from_group: true,
                            user_id: { $ne: user_id },
                        });

                        for (const member of groupMembers) {
                            this.chatService.messageNotifications(
                                msg,
                                payload,
                                name,
                                connection._id.toString(),
                                user_id,
                                member.toString(),
                                savedMessage.message,
                                connection,
                                savedMessage
                            );
                        }
                    }

                    // ---------------- Send Socket Event ----------------
                    socket.to(connection._id.toString()).emit("listening_event", {
                        event_type: "FORWARD_MESSAGE",
                        connection_id: connection._id,
                        data: savedMessage,
                    });
                }
            }
        } catch (error) {
            socket.emit("forward_messages", { error });
        }
    }

    /**
  * -------------------use this socket to make your chat disappear----------------
  * @param socket 
  * @param payload
  * @returns  
  */
    @SubscribeMessage("chat_disapearing")
    async chatDisappearing(socket: CustomSocket, payload: ChatDisappearingDto) {
        try {
            let { _id: user_id } = socket.user_data;
            let { connection_id, type, chat_disapear_for } = payload;
            socket.join(connection_id);
            let connection = await this.model.Connections.findOne({ _id: new Types.ObjectId(connection_id) });
            if (!connection) return;
            let update = {};
            let is_another_user_blocket_me = false;
            let deleted_for = [];
            let otherUserId = connection?.sent_by?.toString() == user_id?.toString() ? connection?.sent_to : connection?.sent_by;
            let fetchAllMembers = await this.allMembers(connection_id);
            if (chat_disapear_for == "EVERYONE") {

                await this.disappearChatForEveryOne(type, connection, user_id, otherUserId, fetchAllMembers, deleted_for);
            }

            if (chat_disapear_for == "ONLY_ME") {
                await this.disappearChatForMe(type, connection, user_id, otherUserId, deleted_for, socket);
            }
        } catch (error) {
            socket.emit("chat_disapear", { error: error });
        }
    }

    singleMessageSave = async (user_id: string, payload: any, connection_id: string, from_user: any, socket?: Socket) => {
        try {
            /// ------------------This is used to sent single message----------------------
            let response_data = await this.chatService.saveSingleMessage(user_id, payload);
            console.log(response_data, "+++++++++response_data");

            let username = (from_user?.name && from_user?.name != undefined) ? `${from_user?.name}` : "Someone";
            console.log(username, "+++++++++username");

            let fetch_last_message = await this.model.Messages.find({ connection_id: connection_id }, { message: 1, message_type: 1 }, { sort: { _id: -1 }, limit: 1, lean: true, })
            console.log(fetch_last_message, "+++++++++fetch_last_message");

            let last_message_type = fetch_last_message?.[0]?.message_type?.toLowerCase();
            let vowels = ['a', 'e', 'i', 'o', 'u'];
            let startsWithVowel = vowels.includes(last_message_type[0].toLowerCase());
            let article = startsWithVowel ? "an" : "a";
            if (last_message_type?.includes("_")) {
                last_message_type = last_message_type?.replace("_", " ");
            }
            let msg = `${username} has sent you ${article} ${last_message_type}`;
            console.log(msg, "+++++++++msg");

            // if (last_message_type == "text" || last_message_type == "link") {
            //     message = this.decodeBase64(message);
            // }
            let fetchConnectionType = await this.model.Connections.findOne({ _id: new Types.ObjectId(connection_id) });
            if (fetchConnectionType?.connection_type == "NORMAL") {
                let msg = `${username} has sent you ${article} ${last_message_type}`;
                console.log(msg, "+++++++++msg");
                this.chatService.messageNotifications(msg, payload, from_user, connection_id, user_id, payload.sent_to, payload.message, fetchConnectionType, response_data)
            } else {
                let msg = `${username} has sent you ${article} ${last_message_type} in ${fetchConnectionType?.group_name}`;
                console.log(msg, "+++++++++msg");
                let groupMembers = await this.model.GroupMembers.distinct('user_id', { connection_id: connection_id, is_exit_from_group: true, user_id: { $ne: user_id } });
                groupMembers.forEach(async (member) => {
                    this.chatService.messageNotifications(msg, payload, from_user, connection_id, user_id, member.toString(), payload.message, fetchConnectionType, response_data)
                })
            }
            console.log(connection_id, "connection_id", typeof connection_id);
            console.log(response_data, "response_data");

            const io: any = this.server ?? (socket as any)?.server;
            if (!io) {
                console.error("Socket server instance not available to emit message");
                return;
            }
            io.to(String(connection_id)).emit("listening_event", {
                event_type: "SEND_MESSAGE",
                connection_id: connection_id,
                data: response_data,
            });
            // return { data: response_data, connection_id, message: "message sent successfully" }
        } catch (error) {
            console.log(error, "error will be occured while sending message");
            throw error
        }
    }

    mediaMessageSave = async (payload: any, user_id: string, is_another_user_blocket_me: boolean, from_user: any, connection_id: string, socket: any) => {
        try {
            let { media, message, sent_to } = payload;
            for (let i = 0; i < media?.length; i++) {
                let response_data = await this.chatService.saveMediasMessage(user_id, payload, media[i], is_another_user_blocket_me);
                let username =
                    from_user?.name != undefined ? `${from_user?.name}` : "Someone";
                let fetch_last_message = await this.model.Messages.find({ connection_id: connection_id }, { message: 1, message_type: 1 }, { sort: { _id: -1 }, limit: 1, lean: true, })
                let last_message_type = fetch_last_message?.[0]?.message_type?.toLowerCase()?.replace("_", " ");
                let vowels = ['a', 'e', 'i', 'o', 'u'];
                let startsWithVowel = vowels.includes(last_message_type[0].toLowerCase());
                let article = startsWithVowel ? "an" : "a";
                if (is_another_user_blocket_me == true) {
                    this.server.to(String(socket.id)).emit("listening_event", {
                        event_type: "SEND_MESSAGE",
                        connection_id: connection_id,
                        data: response_data,
                    });
                } else {
                    let fetchConnectionType = await this.model.Connections.findOne({ _id: new Types.ObjectId(connection_id) });
                    if (fetchConnectionType?.connection_type == "NORMAL") {
                        let msg = `${username} has sent you ${article} ${last_message_type}`;
                        console.log(msg, "+++++++++msg");
                        this.chatService.messageNotifications(msg, payload, from_user, connection_id, user_id, sent_to, message, fetchConnectionType, response_data)
                    } else {
                        let msg = `${username} has sent you ${article} ${last_message_type} in ${fetchConnectionType?.group_name}`;
                        console.log(msg, "+++++++++msg");
                        let groupMembers = await this.model.GroupMembers.distinct('user_id', { connection_id: connection_id, is_exit_from_group: true, user_id: { $ne: user_id } });
                        groupMembers.forEach(async (member) => {
                            this.chatService.messageNotifications(msg, payload, from_user, connection_id, user_id, member.toString(), message, fetchConnectionType, response_data)
                        })
                    }
                    // this.server.to(String(connection_id)).emit("listening_event", {
                    //     event_type: "SEND_MESSAGE",
                    //     connection_id: connection_id,
                    //     data: response_data,
                    // });
                    const io: any = this.server ?? (socket as any)?.server;
                    if (!io) {
                        console.error("Socket server instance not available to emit message");
                        return;
                    }
                    io.to(String(connection_id)).emit("listening_event", {
                        event_type: "SEND_MESSAGE",
                        connection_id: connection_id,
                        data: response_data,
                    });
                }
            }
        }
        catch (error) {
            throw error;
        }
    }

    async emitUserOnlineState(userData: any, isOnline: boolean) {
        try {
            let { _id: user_id, name } = userData;

            // Get all connections where this user is a member
            let userConnections = await this.model.Connections.find({
                $or: [
                    { sent_by: user_id },
                    { sent_to: user_id }
                ]
            });

            // Also get group connections where user is a member
            let groupConnections = await this.model.GroupMembers.find({
                user_id: user_id,
                is_exit_from_group: false
            }).populate('connection_id');

            // Combine all connection IDs
            let allConnectionIds = [
                ...userConnections.map(conn => conn._id.toString()),
                ...groupConnections.map((member: any) => member.connection_id?._id?.toString()).filter(Boolean)
            ];

            // Remove duplicates
            allConnectionIds = [...new Set(allConnectionIds)];

            // Emit to all connections where user is joined
            const onlineStateData = {
                name: name,
                date: {
                    is_online: isOnline
                },
                user_id: user_id
            };

            allConnectionIds.forEach(connectionId => {
                this.server.to(connectionId).emit("listening_event", {
                    event_type: "USER_ONLINE_STATE_UPDATE",
                    connection_id: connectionId,
                    data: onlineStateData
                });
            });

        } catch (error) {
            console.log(error, "error occurred in emitUserOnlineState");
        }
    }

    async allMembers(connection_id: string) {
        try {
            let connection = await this.model.Connections.findOne({ _id: new Types.ObjectId(connection_id) });
            if (!connection) return;
            if (connection.connection_type == "GROUP") {
                let members = await this.model.GroupMembers.distinct("user_id", { connection_id: connection_id });
                return members;
            }
            return [connection.sent_by, connection.sent_to];
        }
        catch (error) {
            throw error;
        }
    }

    async disappearChatForMe(type: string, connection: any, user_id: string, otherUserId: any, deleted_for: any, socket: any) {
        try {

            let chatDisappearingValue = chatDisappearing.OFF;
            let message = "turned off disappearing messages. This message is only visible to you.";

            switch (type) {
                case chatDisappearing.DAYS_7:
                    message = "updated the message timer. New messages will disappear from this chat 7 days after they're sent, except when kept. This message is only visible to you.";
                    chatDisappearingValue = chatDisappearing.DAYS_7;
                    break;

                case chatDisappearing.HOURS_24:
                    message = "updated the message timer. New messages will disappear from this chat 24 hours after they're sent, except when kept. This message is only visible to you.";
                    chatDisappearingValue = chatDisappearing.HOURS_24;
                    break;

                case chatDisappearing.DAYS_90:
                    message = "updated the message timer. New messages will disappear from this chat 90 days after they're sent, except when kept. This message is only visible to you.";
                    chatDisappearingValue = chatDisappearing.DAYS_90;
                    break;
                default:
                    message = "turned off disappearing messages.";
                    chatDisappearingValue = chatDisappearing.OFF;
                    break;
            }

            // -------------------------
            // ONLY THIS USER GETS ENTRY
            // -------------------------
            let chat_disappear_by = connection.chat_disappear_by ?? [];

            let idx = chat_disappear_by.findIndex(
                d => d.user_id.toString() === user_id.toString()
            );

            if (idx !== -1) {
                chat_disappear_by[idx].chat_disappearing = chatDisappearingValue;
                chat_disappear_by[idx].chat_disapear_for = "ONLY_ME";
            } else {
                chat_disappear_by.push({
                    user_id: new Types.ObjectId(user_id),
                    chat_disappearing: chatDisappearingValue,
                    chat_disapear_for: "ONLY_ME"
                });
            }

            // save info message
            let sendMessage = await this.chatService.saveDisappearingMessage(
                connection,
                user_id,
                otherUserId,
                message,
                deleted_for
            );

            // update DB
            await this.model.Connections.findByIdAndUpdate(
                connection._id,
                {
                    chat_disappear_by,
                    last_updated_at: moment().utc().valueOf()
                },
                this.new_options
            );

            socket.to(socket.id).emit("listening_event", {
                event_type: "MESSAGE_DISAPPEAR",
                connection_id: connection._id,
                data: sendMessage,
            });

            return sendMessage;

        } catch (err) {
            throw err;
        }
    }

    async disappearChatForEveryOne(type, connection, user_id, otherUserId, allMemberIds, deleted_for) {
        try {

            let message = "";
            let updateValue = chatDisappearing.OFF;

            switch (type) {
                case chatDisappearing.DAYS_7:
                    message = "updated the message timer. New messages will disappear from this chat 7 days after they're sent, except when kept.";
                    updateValue = chatDisappearing.DAYS_7;
                    break;

                case chatDisappearing.HOURS_24:
                    message = "updated the message timer. New messages will disappear from this chat 24 hours after they're sent, except when kept.";
                    updateValue = chatDisappearing.HOURS_24;
                    break;

                case chatDisappearing.DAYS_90:
                    message = "updated the message timer. New messages will disappear from this chat 90 days after they're sent, except when kept.";
                    updateValue = chatDisappearing.DAYS_90;
                    break;

                default:
                    message = "turned off disappearing messages.";
                    updateValue = chatDisappearing.OFF;
            }

            // -------------------------
            // ALL USERS GET SAME SETTING
            // -------------------------
            let chat_disappear_by = allMemberIds.map(id => ({
                user_id: new Types.ObjectId(id),
                chat_disappearing: updateValue,
                chat_disapear_for: "EVERYONE"
            }));

            let sendMessage = await this.chatService.saveDisappearingMessage(
                connection,
                user_id,
                otherUserId,
                message,
                deleted_for
            );

            await this.model.Connections.findByIdAndUpdate(
                connection._id,
                {
                    chat_disappearing: updateValue,
                    chat_disappear_by
                },
                this.new_options
            );

            this.server.to(connection._id.toString()).emit("listening_event", {
                event_type: "MESSAGE_DISAPPEAR",
                connection_id: connection._id,
                data: sendMessage
            });

            return sendMessage;

        } catch (err) {
            throw err;
        }
    }


    async deliveredMessage(message_id: string, user_id: string) {
        try {
            const query = { _id: new Types.ObjectId(message_id) };
            let message: any = await this.model.Messages.findOne({ _id: new Types.ObjectId(message_id), message_status: { $in: ["DELIVERED", "READ"] } });
            if (message) return message
            else {

                let message: any = await this.model.Messages.findOne({ _id: new Types.ObjectId(message_id) });
                let delivered_to = message?.delivered_to.map((item) => item?.user_id?.toString());
                if (delivered_to.includes(user_id?.toString())) return message;
                let update: any = { updated_at: +new Date() };
                if (String(message?.sent_by) !== String(user_id)) {
                    if (message?.connection_type == "NORMAL")
                        update = {
                            $push: {
                                delivered_to: {
                                    user_id: new Types.ObjectId(user_id),
                                    delivered_at: moment().utc().valueOf()
                                }
                            },
                            $set: {
                                message_status: "DELIVERED"
                            }
                        }
                    if (message?.connection_type == "GROUP") {
                        let totalNumberOfGrpMembers = await this.model.GroupMembers.countDocuments({ connection_id: message.connection_id, is_exit_from_group: false });
                        console.log(totalNumberOfGrpMembers, "asdfalsdfjkalsdfjkkla;sdfjkl;asdfj");

                        let deliveryStatus = totalNumberOfGrpMembers == message?.delivered_to?.length + 1 ? "DELIVERED" : "SENT";
                        console.log(deliveryStatus, "deleiD+++++++++++++++");

                        update = {
                            $push: {
                                delivered_to: {
                                    user_id: new Types.ObjectId(user_id),
                                    delivered_at: moment().utc().valueOf()
                                }
                            },
                            $set: {
                                message_status: deliveryStatus
                            }
                        };
                    }
                }

                const options = { new: true };
                console.log(update, "update++++++++++++");

                const response = await this.model.Messages.findOneAndUpdate(
                    query,
                    update,
                    options
                );
                console.log(response, "response");
                let sender = await this.model.UserModel.findOne({ _id: message.sent_by }, { socket_id: 1 });
                this.server.to(String(sender?.socket_id)).emit("listening_event", {
                    event_type: "DELIVERED_MESSAGE",
                    connection_id: message.connection_id,
                    data: response,
                });
                return response;
            }
        } catch (error) {
            console.log(error, "error will ocucre in delivered message functions");
            throw error;
        }
    }

    async readSingleMessages(message_id: string, user_id: string) {
        try {
            const query = { _id: new Types.ObjectId(message_id) };
            let message: any = await this.model.Messages.findOne({ _id: new Types.ObjectId(message_id), message_status: { $eq: "READ" } });
            if (message) { return message; }
            else {
                let message: any = await this.model.Messages.findOne({ _id: new Types.ObjectId(message_id) });
                let update: any = { updated_at: +new Date() };
                let read_by = message.read_by.map((item) => item?.user_id?.toString());
                if (read_by.includes(user_id?.toString())) return message;
                await this.deliveredMessage(message_id, user_id);
                if (String(message?.sent_by) !== String(user_id)) {
                    if (message?.connection_type == "NORMAL")
                        update = {
                            $push: {
                                read_by: {
                                    user_id: new Types.ObjectId(user_id),
                                    read_at: moment().utc().valueOf()
                                }
                            },
                            $set: {
                                message_status: "READ"
                            }
                        }
                    if (message?.connection_type == "GROUP") {
                        let totalMembers = await this.model.GroupMembers.countDocuments({ connection_id: message.connection_id, is_exit_from_group: false });
                        const deliveryStatus = totalMembers == (message.read_by.length + 1) ? "READ" : (totalMembers == message.delivered_to.length) ? "DELIVERED" : "SENT";
                        update = {
                            $push: {
                                read_by: {
                                    user_id: new Types.ObjectId(user_id),
                                    read_at: moment().utc().valueOf()
                                }
                            },
                            $set: {
                                message_status: deliveryStatus
                            }
                        };
                    }
                }

                const options = { new: true };
                const response = await this.model.Messages.findOneAndUpdate(
                    query,
                    update,
                    options
                );
                let sender = await this.model.UserModel.findOne({ _id: message.sent_by }, { socket_id: 1 });
                this.server.to(String(sender?.socket_id)).emit("listening_event", {
                    event_type: "READ_MESSAGE",
                    connection_id: message.connection_id,
                    data: response,
                });
                return response;
            }
        } catch (error) {
            console.log(error, "error will ocucre in delivered message functions");
            throw error;
        }
    }

    async readAllMessages(connection_id: string, user_id: string) {
        try {
            const connId = new Types.ObjectId(connection_id);
            const uid = new Types.ObjectId(user_id);

            const messages = await this.model.Messages.find({
                connection_id: connId,
                message_status: { $ne: "READ" }
            });

            for (const msg of messages) {
                if (msg.sent_by.toString() == user_id?.toString()) continue; // skip user's own msg
                let update = {};
                // let read_by = [];
                let read_by = msg.read_by.map((item) => item?.user_id?.toString());
                if (read_by.includes(user_id?.toString())) continue;
                await this.deliveredMessage(msg?._id?.toString(), user_id);
                const query = { _id: msg._id };
                if (msg.connection_type === "NORMAL") {
                    update = {
                        $push: {
                            read_by: {
                                user_id: uid,
                                read_at: moment().utc().valueOf()
                            }
                        },
                        $set: { message_status: "READ" }
                    };
                }

                if (msg.connection_type === "GROUP") {
                    const totalMembers = await this.model.GroupMembers.countDocuments({
                        connection_id: msg.connection_id,
                        is_exit_from_group: false
                    });

                    const newStatus = totalMembers <= (msg.read_by.length + 1) ? "READ" : totalMembers <= msg.delivered_to.length ? "DELIVERED" : "SENT";
                    update = {
                        $push: {
                            read_by: {
                                user_id: uid,
                                read_at: moment().utc().valueOf()
                            }
                        },
                        $set: { message_status: newStatus }
                    };
                }

                // update message
                const updatedMsg = await this.model.Messages.findOneAndUpdate(
                    query,
                    update,
                    { new: true }
                );

                // notify sender
                const sender = await this.model.UserModel.findOne(
                    { _id: msg.sent_by },
                    { socket_id: 1 }
                );

                this.server.to(String(sender?.socket_id)).emit("listening_event", {
                    event_type: "READ_MESSAGE",
                    connection_id: msg.connection_id,
                    data: updatedMsg
                });
            }

            return { message: "All message read successfully." };
        } catch (error) {
            console.log(error, "error in readAllMessages");
            throw error;
        }
    }

    afterInit(server: Server) {
        console.log("after init log");
    }

    //You can handle their multiple operations like user ofline status and etc
    async handleDisconnect(socket: Socket) {
        try {
            let token = socket.handshake.headers.token;
            console.log(socket.id, "user disconnected - treating as logout");

            // Get user data before disconnecting
            let userData = socket['user_data'];
            if (userData) {
                console.log(`User ${userData._id} (${userData.name}) disconnected, marking as offline`);
                
                // Emit offline state to all user's connections (same as userOnlineState with is_online: false)
                await this.emitUserOnlineState(userData, false);
            }

            // Call chat service disconnect handler (updates DB and ends calls)
            await this.chatService.handleDisconnect(token as string, this.server);
        }
        catch (error) {
            console.error('Error in handleDisconnect:', error);
            socket.to(socket?.id?.toString()).emit("listening_event", {
                event_type: "DISCONNECTING_SOCKET_ERROR",
                data: error
            })
        }
    }
}
