"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const models_1 = require("../users/models");
const models_2 = require("./models");
const helpers_1 = __importDefault(require("../../utils/helpers"));
const mongoose_1 = require("mongoose");
const models_3 = require("../chats/models");
const services_1 = __importDefault(require("../../utils/services"));
const controllers_1 = __importDefault(require("../reels/controllers"));
const models_4 = require("../reels/models");
const models_5 = require("../shares/models");
function getMessages(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const chatId = req.params.chatId;
            const user = res.locals.user;
            let { limit = '10', page = '1' } = req.query;
            limit = +limit;
            page = +page;
            const skip = (page - 1) * limit;
            const chat = yield models_3.Chat.findById(chatId);
            if (!chat) {
                return res.status(404).json({ message: 'chat not found' });
            }
            const participant = chat.participants.find(e => `${e.user}` === `${user._id}`);
            if (!participant) {
                return res.status(403).json({ message: 'user must belong to this chat' });
            }
            const query = { chat: chatId };
            const messages = yield models_2.Message.find(query)
                .sort({ at: -1 })
                .skip(skip)
                .limit(limit)
                .populate({
                path: 'user',
                select: { name: 1, username: 1, bio: 1, photo: 1 },
            })
                .populate({
                path: 'reel',
                select: Object.assign(Object.assign({}, controllers_1.default.reelFieldsToProject), { user: 1 }),
            });
            const count = yield models_2.Message.countDocuments(query);
            return res.json({ messages, count });
        }
        catch (error) {
            console.error(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function postMessage(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const user = res.locals.user;
            const { chat: chatId, content, files, socket: socketId } = req.body;
            const chat = yield models_3.Chat.findById(chatId);
            if (!chat) {
                return res.status(404).json({ message: 'chat not found' });
            }
            if (chat.blocked) {
                return res.status(403).json({ message: "Can't post new messages once its blocked" });
            }
            const participant = chat.participants.find(e => `${e.user}` === `${user._id}`);
            if (!participant) {
                return res.status(403).json({ message: 'user must belong to this chat' });
            }
            const message = yield models_2.Message.create({
                user: user._id,
                chat: chat._id,
                content,
                files
            });
            participant.lastReadAt = new Date();
            // @ts-ignore
            chat.lastMessage = message._id;
            yield chat.save();
            (() => __awaiter(this, void 0, void 0, function* () {
                var _a, _b, _c, _d, _e, _f, _g;
                try {
                    // notify other participants with push only notification
                    const otherParticipants = chat.participants.filter(e => `${e.user}` !== `${user._id}`);
                    const participants = yield models_1.User.find({ _id: { $in: otherParticipants.map(e => e.user) } });
                    const title = `New message from ${(_b = (_a = user.name) !== null && _a !== void 0 ? _a : user.username) !== null && _b !== void 0 ? _b : 'a user'}`;
                    const description = (_c = message.content) !== null && _c !== void 0 ? _c : `${(_e = (_d = message.files) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0} file${((_g = (_f = message.files) === null || _f === void 0 ? void 0 : _f.length) !== null && _g !== void 0 ? _g : 0 > 1) ? 's' : ''}`;
                    for (const participant of participants) {
                        yield services_1.default.pushNotifyUser({
                            title,
                            body: description,
                            event: 'message-received',
                            user: participant,
                            data: {
                                message: {
                                    _id: message._id,
                                    user: {
                                        _id: user._id,
                                        username: user.username,
                                        name: user.name,
                                        photo: user.photo,
                                    },
                                    isGroup: chat.isGroup,
                                    chat: message.chat,
                                    content: message.content,
                                    files: message.files,
                                    at: message.at,
                                },
                            }
                        });
                    }
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            // socket notify
            (() => __awaiter(this, void 0, void 0, function* () {
                try {
                    // notify other participants with event "message-received"
                    const participantIds = chat.participants.filter(e => `${e.user}` !== `${user._id}`).map(e => `${e.user}`);
                    const payload = {
                        message: {
                            _id: `${message._id}`,
                            user: {
                                _id: `${user._id}`,
                                name: user.name,
                                photo: user.photo,
                                username: user.username
                            },
                            chat: `${chat._id}`,
                            content: message.content,
                            files: message.files,
                            at: message.at.toISOString()
                        }
                    };
                    for (const participantId of participantIds) {
                        services_1.default.socketNotifyUser({
                            userId: participantId,
                            event: 'messages-received',
                            data: payload
                        });
                    }
                    // notify current user's other sockets(if socket available) with event "message-sent"
                    services_1.default.socketNotifyUser({
                        userId: `${user._id}`,
                        event: 'messages-sent',
                        data: payload,
                        currentSocket: socketId,
                        socketType: 'others'
                    });
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            (() => __awaiter(this, void 0, void 0, function* () {
                try {
                    if (chat.isGroup) {
                        return;
                    }
                    const score = yield services_1.default.getOrCreateTodayScore(user._id);
                    if (!score) {
                        return;
                    }
                    const action = score.social.actions.find(e => e.type === 'send-dm-message');
                    if (!action || action.completed) {
                        return;
                    }
                    action.value = 1;
                    action.completed = true;
                    yield services_1.default.updateScore(score);
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            return res.status(201).json({ message });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function postMessageShareReels(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const user = res.locals.user;
            const { chats: chatIds, reel: reelId, socket: socketId } = req.body;
            const reel = yield models_4.Reel.findById(reelId).populate('user');
            if (!reel) {
                return res.status(404).json({ message: 'vibe not found' });
            }
            const chats = yield models_3.Chat.find({
                _id: { $in: chatIds },
                'participants.user': user._id
            });
            const messages = [];
            const chatUpdates = [];
            for (const chat of chats) {
                if (chat.blocked) {
                    continue;
                }
                const message = {
                    _id: new mongoose_1.Types.ObjectId(),
                    user: user._id,
                    chat: chat._id,
                    reel: reel._id,
                    at: Date.now()
                };
                const chatUpdate = {
                    updateOne: {
                        filter: { _id: chat._id },
                        update: {
                            $set: {
                                lastMessage: message._id,
                                'participants.$[p].lastReadAt': message.at
                            }
                        },
                        arrayFilters: [
                            {
                                'p.user': user._id
                            }
                        ]
                    }
                };
                messages.push(message);
                chatUpdates.push(chatUpdate);
            }
            yield Promise.all([
                models_2.Message.insertMany(messages),
                models_3.Chat.bulkWrite(chatUpdates)
            ]);
            // fcm notify
            (() => __awaiter(this, void 0, void 0, function* () {
                var _a, _b;
                try {
                    // notify other participants with push only notification
                    let userIds = [];
                    for (const chat of chats) {
                        const otherParticipants = chat.participants.filter(e => `${e.user}` !== `${user._id}`);
                        userIds.push(...otherParticipants.map(e => `${e.user}`));
                    }
                    userIds = [...new Set(userIds)];
                    const users = yield models_1.User.find({ _id: { $in: userIds } });
                    for (const chat of chats) {
                        const messageData = messages.find(e => `${e.chat}` === `${chat._id}`);
                        if (!messageData) {
                            continue;
                        }
                        const otherParticipants = chat.participants.filter(e => `${e.user}` !== `${user._id}`);
                        const participants = users.filter(e => otherParticipants.some(o => `${o.user}` === `${e._id}`));
                        const title = `New message from ${(_b = (_a = user.name) !== null && _a !== void 0 ? _a : user.username) !== null && _b !== void 0 ? _b : 'a user'}`;
                        const description = `A vibe has been shared with you`;
                        for (const participant of participants) {
                            services_1.default.pushNotifyUser({
                                title,
                                body: description,
                                event: 'message-received',
                                user: participant,
                                data: {
                                    message: {
                                        _id: messageData._id,
                                        user: {
                                            _id: user._id,
                                            username: user.username,
                                            name: user.name,
                                            photo: user.photo,
                                        },
                                        isGroup: chat.isGroup,
                                        chat: messageData.chat,
                                        content: '',
                                        at: messageData.at,
                                    },
                                }
                            });
                        }
                    }
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            // socket notify
            (() => __awaiter(this, void 0, void 0, function* () {
                try {
                    // notify other participants with event "message-received"
                    for (const chat of chats) {
                        const messageData = messages.find(e => `${e.chat}` === `${chat._id}`);
                        if (!messageData) {
                            continue;
                        }
                        const participantIds = chat.participants.filter(e => `${e.user}` !== `${user._id}`).map(e => `${e.user}`);
                        const payload = {
                            message: {
                                _id: `${messageData._id}`,
                                user: {
                                    _id: `${user._id}`,
                                    name: user.name,
                                    photo: user.photo,
                                    username: user.username
                                },
                                chat: `${chat._id}`,
                                content: '',
                                at: new Date(messageData.at).toISOString()
                            }
                        };
                        for (const participantId of participantIds) {
                            services_1.default.socketNotifyUser({
                                userId: participantId,
                                event: 'messages-received',
                                data: payload
                            });
                        }
                        // notify current user's other sockets(if socket available) with event "message-sent"
                        services_1.default.socketNotifyUser({
                            userId: `${user._id}`,
                            event: 'messages-sent',
                            data: payload,
                            currentSocket: socketId,
                            socketType: 'others'
                        });
                    }
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            // update reels share count and create entries
            (() => __awaiter(this, void 0, void 0, function* () {
                try {
                    const reelUser = reel.user;
                    yield models_5.Share.create({
                        user: user._id,
                        reel: reel._id,
                        chats: chats,
                        count: chats.length
                    });
                    const count = yield models_5.Share.countDocuments({ reel: reel._id });
                    reel.shares = count;
                    yield reel.save();
                    if (`${reelUser._id}` !== `${user._id}`) {
                        yield services_1.default.notifyUser({
                            title: 'Your vibe got shared',
                            description: `${user.username} shared your vibe`,
                            event: 'reel-shared',
                            user: reelUser,
                            from: user._id,
                            reel: reel._id
                        });
                    }
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            return res.status(201).json({ message: 'Vibe shared successfully' });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function patchMessage(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const user = res.locals.user;
            const id = req.params.id;
            const { tag } = req.body;
            const message = yield models_2.Message.findById(id).populate('chat');
            if (!message) {
                return res.status(404).json({ message: 'message not found' });
            }
            const chat = message.chat;
            if (!chat) {
                return res.status(404).json({ chat: 'chat not found' });
            }
            if (chat.blocked) {
                return res.status(403).json({ message: "Can't update the messages once its blocked" });
            }
            const participant = chat.participants.find(e => `${e.user}` === `${user._id}`);
            if (!participant) {
                return res.status(403).json({ message: 'permission denied' });
            }
            if (`${message.user}` !== `${user._id}` && !participant.isAdmin) {
                return res.status(403).json({ message: 'you must be chat admin or message creator' });
            }
            if (!message.files) {
                return res.status(404).json({ message: 'files not found' });
            }
            if (tag && chat.tags.every(e => e.tag !== tag)) {
                return res.status(404).json({ message: 'tag not found' });
            }
            message.tag = tag !== null && tag !== void 0 ? tag : undefined;
            yield message.save();
            return res.json({ message: 'tag updated successfully' });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function postPresignUrl(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const user = res.locals.user;
            const fileNames = req.body.files;
            const chatId = new mongoose_1.Types.ObjectId(req.body.chat);
            const chat = yield models_3.Chat.findById(chatId);
            if (!chat) {
                return res.status(404).json({ message: 'chat not found' });
            }
            if (chat.blocked) {
                return res.status(403).json({ message: "Can't post new messages once its blocked" });
            }
            const participant = chat.participants.find(e => `${e.user}` === `${user._id}`);
            if (!participant) {
                return res.status(403).json({ message: 'user must belong to this chat' });
            }
            const files = fileNames.map(e => {
                const extension = e.split('.').at(-1);
                e = e.split('.').slice(0, -1).join('.');
                let name = e
                    .replace(/ /g, '-')
                    .replace(/#/g, '-')
                    .replace(/,/g, '-')
                    .split('')
                    .filter(c => /[A-Za-z0-9_\-.]/.test(c))
                    .join('');
                while (name.includes('--')) {
                    name = name.replace(/\-\-/g, '-');
                }
                name = name.length >= 10 ? name.slice(0, 10) : name;
                if (name.endsWith('-')) {
                    name = name.slice(0, -1);
                }
                name += '.' + extension;
                const hash = Date.now().toString() + '-' + Math.random().toString().split('.')[1];
                const path = `chats/${chat._id}/${hash}-${name}`;
                return { path, name: e + '.' + extension };
            });
            const urls = [];
            for (const file of files) {
                const url = yield helpers_1.default.generateSignedUrl(file.path);
                if (!url) {
                    return res.status(500).json({ message: 'server error' });
                }
                urls.push({
                    file: file.name,
                    upload: url.upload,
                    download: url.download,
                });
            }
            return res.status(201).json({ urls });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function deleteMessage(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const user = res.locals.user;
            const id = req.params.id;
            const message = yield models_2.Message.findById(id).populate('chat');
            if (!message) {
                return res.status(404).json({ message: 'message not found' });
            }
            const chat = message.chat;
            if (!chat) {
                return res.status(404).json({ chat: 'chat not found' });
            }
            if (chat.blocked) {
                return res.status(403).json({ message: "Can't delete older messages once its blocked" });
            }
            const participant = chat.participants.find(e => `${e.user}` === `${user._id}`);
            if (!participant) {
                return res.status(403).json({ message: 'permission denied' });
            }
            if (`${message.user}` !== `${user._id}` && !participant.isAdmin) {
                return res.status(403).json({ message: 'you must be chat admin or message creator' });
            }
            yield message.deleteOne();
            (() => __awaiter(this, void 0, void 0, function* () {
                var _a;
                try {
                    if (message.files) {
                        for (const f of message.files) {
                            yield helpers_1.default.deleteR2File(f.url);
                        }
                    }
                    if (`${chat.lastMessage}` === id) {
                        const lastMessages = yield models_2.Message.find({ chat: chat._id })
                            .sort({ at: -1 })
                            .limit(1);
                        chat.lastMessage = (_a = lastMessages[0]) === null || _a === void 0 ? void 0 : _a._id;
                        yield chat.save();
                    }
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            return res.json({ message: 'message deleted successfully' });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
const controllers = {
    getMessages,
    postMessage,
    postMessageShareReels,
    patchMessage,
    postPresignUrl,
    deleteMessage
};
exports.default = controllers;
