"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("../api/admin/models");
const models_2 = require("../api/users/models");
const constants_1 = __importDefault(require("./constants"));
const helpers_1 = __importDefault(require("./helpers"));
const models_3 = require("../api/reels/models");
const models_4 = require("../api/notifications/models");
const models_5 = require("../api/chats/models");
const config_1 = __importDefault(require("./config"));
const models_6 = require("../api/scores/models");
const models_7 = require("../api/reward-hashes/models");
const models_8 = require("../api/rewards/models");
const promises_1 = __importDefault(require("fs/promises"));
const models_9 = require("../api/blockings/models");
function createAdmin(name, email, password, phone) {
    return __awaiter(this, void 0, void 0, function* () {
        const hash = helpers_1.default.getHash(password);
        const admin = yield models_1.Admin.create({
            name,
            email,
            phone,
            auth: { password: hash },
        });
        console.log(admin);
    });
}
// createAdmin('Test Admin', 'test@example.com', '123456')
function deleteOldUnverifiedUsers() {
    return __awaiter(this, void 0, void 0, function* () {
        console.log('🕒  Deleting unverified users!');
        try {
            const date = new Date();
            date.setDate(date.getDate() - 7);
            const result = yield models_2.User.deleteMany({
                $or: [
                    { 'auth.emailVerified': { $exists: false } },
                    { 'auth.emailVerified': false }
                ],
                createdAt: { $lt: date }
            });
            console.log(`✅  Unverified users deleted: ${result.deletedCount}`);
        }
        catch (error) {
            console.error('❌ Error while deleting unverified users:', error);
        }
    });
}
function deleteDeactivatedUsers() {
    return __awaiter(this, void 0, void 0, function* () {
        console.log('🕒  Deleting old deactivated users!');
        try {
            const date = new Date();
            date.setDate(date.getDate() - 7);
            const users = yield models_2.User.find({
                $or: [
                    { terminated: { $exists: false } },
                    { terminated: false }
                ],
                deactivatedAt: { $lt: date }
            });
            for (const user of users) {
                // TODO: set user fields as undefined
                // TODO: set all those user field in user's "data" field
                // TODO: terminate the user account
            }
            console.log(`✅  Old deactivated users deletion completed!`);
        }
        catch (error) {
            console.error('❌ Error while deleting deactivated users:', error);
        }
    });
}
function deleteOldNotifications() {
    return __awaiter(this, void 0, void 0, function* () {
        console.log('🕒  Deleting old notifications!');
        try {
            const date = new Date();
            date.setDate(date.getDate() - 7);
            const result = yield models_4.Notification.deleteMany({ at: { $lt: date } });
            console.log(`✅  Old notifications deleted count: ${result.deletedCount}`);
        }
        catch (error) {
            console.error('❌ Error while deleting old notifications:', error);
        }
    });
}
function sendOtp(user_1) {
    return __awaiter(this, arguments, void 0, function* (user, sendTo = 'phone') {
        try {
            // don't send otp if already send within delay time, in this case just update expiry
            if (sendTo === 'phone' && user.auth.lastPhoneOtpSentAt) {
                const lastSentAt = new Date(user.auth.lastPhoneOtpSentAt);
                lastSentAt.setSeconds(lastSentAt.getSeconds() + constants_1.default.minPhoneOtpResendDelay);
                if (lastSentAt >= new Date()) {
                    user.auth.lastPhoneOtpSentAt = new Date();
                    const expiry = new Date();
                    expiry.setMinutes(expiry.getMinutes() + constants_1.default.phoneOtpExpiry);
                    user.auth.phoneOtpExpiry = expiry;
                    yield user.save();
                    return;
                }
            }
            if (sendTo === 'email' && user.auth.lastEmailOtpSentAt) {
                const lastSentAt = new Date(user.auth.lastEmailOtpSentAt);
                lastSentAt.setSeconds(lastSentAt.getSeconds() + constants_1.default.minEmailOtpResendDelay);
                if (lastSentAt >= new Date()) {
                    user.auth.lastEmailOtpSentAt = new Date();
                    const expiry = new Date();
                    expiry.setMinutes(expiry.getMinutes() + constants_1.default.emailOtpExpiry);
                    user.auth.emailOtpExpiry = expiry;
                    yield user.save();
                    return;
                }
            }
            // now send the otp if its expired or not exist
            const otp = helpers_1.default.getRandomOtp();
            const expiry = new Date();
            if (sendTo === 'phone') {
                expiry.setMinutes(expiry.getMinutes() + constants_1.default.phoneOtpExpiry);
                user.auth.phoneOtp = otp;
                user.auth.phoneOtpExpiry = expiry;
                user.auth.lastPhoneOtpSentAt = new Date();
                yield helpers_1.default.sendSms(user.phone, `Your OTP is ${otp}`);
            }
            else {
                expiry.setMinutes(expiry.getMinutes() + constants_1.default.emailOtpExpiry);
                user.auth.emailOtp = otp;
                user.auth.emailOtpExpiry = expiry;
                user.auth.lastEmailOtpSentAt = new Date();
                yield helpers_1.default.sendOauthGmail(user.email, 'OTP Verification', `Your OTP is ${otp}`);
            }
            yield user.save();
        }
        catch (error) {
            console.log(error);
        }
    });
}
function checkAndResetOtp(user_1, otp_1) {
    return __awaiter(this, arguments, void 0, function* (user, otp, sendTo = 'phone', reset = false) {
        try {
            const now = new Date();
            if (sendTo === 'phone') {
                if (!user.auth.phoneOtpExpiry) {
                    return { status: 404, message: 'OTP not found' };
                }
                if (user.auth.phoneOtpExpiry < now) {
                    return { status: 410, message: 'OTP expired' };
                }
                if (user.auth.phoneOtp !== otp) {
                    return { status: 401, message: 'OTP verification failed' };
                }
                if (reset) {
                    user.auth.phoneOtp = undefined;
                    user.auth.phoneOtpExpiry = undefined;
                    user.auth.phoneVerified = true;
                    yield user.save();
                }
            }
            else {
                if (!user.auth.emailOtpExpiry) {
                    return { status: 404, message: 'OTP not found' };
                }
                if (user.auth.emailOtpExpiry < now) {
                    return { status: 410, message: 'OTP expired' };
                }
                if (user.auth.emailOtp !== otp) {
                    return { status: 401, message: 'OTP verification failed' };
                }
                if (reset) {
                    user.auth.emailOtp = undefined;
                    user.auth.emailOtpExpiry = undefined;
                    user.auth.emailVerified = true;
                    yield user.save();
                }
            }
            return {};
        }
        catch (error) {
            return { error };
        }
    });
}
function pushNotifyUser(payload) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            let { user, loggedinDevice, title, body, event, data = {}, deviceType = 'all' } = payload;
            const removals = [];
            for (const device of user.auth.loggedinDevices) {
                if (!device.fcmToken) {
                    continue;
                }
                if (deviceType === 'loggedin' && `${device._id}` !== `${loggedinDevice}`) {
                    continue;
                }
                if (deviceType === 'others' && `${device._id}` === `${loggedinDevice}`) {
                    continue;
                }
                data = Object.assign(Object.assign({}, data), { event });
                const error = yield helpers_1.default.sendPushNotification(device.fcmToken, title, body, data);
                if (error) {
                    removals.push(device);
                }
            }
            if (removals.length) {
                user.auth.loggedinDevices = user.auth.loggedinDevices.filter(e => !removals.some(r => r.fcmToken === e.fcmToken));
                yield user.save();
            }
        }
        catch (error) {
            console.log(error);
        }
    });
}
function socketNotifyUser(payload) {
    var _a;
    try {
        const { userId, event, data, currentSocket, socketType = 'all' } = payload;
        const sockets = constants_1.default.sockets[userId];
        if (!sockets) {
            return;
        }
        for (const socket of sockets) {
            if (socketType === 'current' && currentSocket && `${socket.id}` !== currentSocket) {
                continue;
            }
            if (socketType === 'others' && socket.id === currentSocket) {
                continue;
            }
            socket.emit('message', { event, data });
            const user = socket.data.user;
            console.log(`💬 socket message sent to ${(_a = user.username) !== null && _a !== void 0 ? _a : user.email}`);
            console.dir({ event, data }, { depth: null });
        }
    }
    catch (error) {
        console.log(error);
    }
}
function socketErrorNotifyUser(socket, event, data) {
    var _a;
    try {
        socket.emit('error', { event, data });
        const user = socket.data.user;
        console.log(`❌ socket error message sent to ${(_a = user.username) !== null && _a !== void 0 ? _a : user.email}`);
        console.dir({ event, data }, { depth: null });
    }
    catch (error) {
        console.log(error);
    }
}
/**
 * User only requires _id and auth field
 */
function notifyUser(payload) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            let { user, data = {}, title, description, event, from, comment, connection, follower, like, superLike, reel, status, chat, request, meetMeEvent, rewardHash } = payload;
            const notification = yield models_4.Notification.create({
                user: user._id,
                data,
                title,
                description,
                event,
                from,
                comment,
                connection,
                follower,
                like,
                superLike,
                reel,
                status,
                chat,
                request,
                meetMeEvent,
                rewardHash
            });
            data = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, data), { notification: `${notification._id}` }), (from ? { from: `${from}` } : {})), (comment ? { comment: `${comment}` } : {})), (connection ? { connection: `${connection}` } : {})), (follower ? { follower: `${follower}` } : {})), (like ? { like: `${like}` } : {})), (superLike ? { superLike: `${superLike}` } : {})), (reel ? { reel: `${reel}` } : {})), (status ? { status: `${status}` } : {})), (request ? { request: `${request}` } : {})), (chat ? { chat: `${chat}` } : {})), (meetMeEvent ? { meetMeEvent: `${meetMeEvent}` } : {})), (rewardHash ? { rewardHash: `${rewardHash}` } : {}));
            (() => __awaiter(this, void 0, void 0, function* () {
                try {
                    yield pushNotifyUser({
                        user,
                        title,
                        body: description,
                        event,
                        data,
                    });
                }
                catch (error) {
                    console.log(error);
                }
            }))();
            return notification;
        }
        catch (error) {
            console.log(error);
        }
    });
}
function nestedDynamicToFlat(dynamic, list = [], parent) {
    let { _id, label, cover, video, question, children } = dynamic;
    if (!_id || (typeof _id === 'string' && !/^[0-9a-f]{24}$/.test(_id))) {
        _id = Date.now() + '-' + Math.random().toString().substring(2);
        dynamic._id = _id;
    }
    const entry = Object.assign({ _id, label, cover, video, question }, (parent ? { parent: parent._id } : {}));
    list.push(entry);
    if (children === null || children === void 0 ? void 0 : children.length) {
        for (const child of children) {
            nestedDynamicToFlat(child, list, dynamic);
        }
    }
    return list;
}
function flatDynamicToNested(serialized) {
    const insertEntryInDynamic = (dynamic, entry, root) => {
        const { _id, label, cover, video, question, parent } = entry;
        if (!dynamic) {
            dynamic = { _id, label, cover, video, question, children: [] };
            return root !== null && root !== void 0 ? root : dynamic;
        }
        if (!dynamic.children) {
            dynamic.children = [];
        }
        if (parent && dynamic._id === parent) {
            dynamic.children.push({ _id, label, cover, video, question });
        }
        for (const child of dynamic.children) {
            root = insertEntryInDynamic(child, entry, root);
        }
        if (!dynamic.children.length) {
            dynamic.children = undefined;
        }
        return root;
    };
    let dynamic = undefined;
    for (const entry of serialized) {
        if (entry.parent) {
            dynamic = insertEntryInDynamic(dynamic, entry, dynamic);
        }
        else {
            const { _id, label, cover, video, question } = entry;
            dynamic = { _id, label, cover, video, question, children: [] };
        }
    }
    return dynamic;
}
function createLoggedinDevice(user_1, platform_1, fcmToken_1) {
    return __awaiter(this, arguments, void 0, function* (user, platform, fcmToken, remember = true) {
        let devices = user.auth.loggedinDevices;
        if (devices.length >= constants_1.default.maxLoggedinDevices) {
            // remove the device with oldest accessed
            devices = devices.sort((a, b) => b.lastAccessedAt.getTime() - a.lastAccessedAt.getTime());
            devices = devices.slice(0, -1);
        }
        const token = helpers_1.default.getToken(user, remember);
        const tokenHash = helpers_1.default.getHash(token);
        // @ts-ignore
        devices.push({ platform, tokenHash, fcmToken });
        user.auth.loggedinDevices = devices;
        yield user.save();
        return token;
    });
}
function socketNotifyOnUserPresenceUpdated(user, status) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            // fetch all distinct chat participants where this user belongs
            const chats = yield models_5.Chat.find({ 'participants.user': user._id }, { isGroup: 1, 'participants.user': 1 })
                .populate({
                path: 'participants.user',
                select: { name: 1, username: 1, photo: 1 }
            });
            const people = {};
            let id;
            for (const chat of chats) {
                for (const person of chat.participants) {
                    id = `${person.user._id}`;
                    if (id === `${user._id}`) {
                        continue;
                    }
                    if (!(id in people)) {
                        people[id] = {
                            user: {
                                _id: `${user._id}`,
                                name: user.name,
                                username: user.username,
                                photo: user.photo,
                            },
                            chats: []
                        };
                    }
                    people[id].chats.push(`${chat._id}`);
                    if (!chat.isGroup) {
                        people[id].chat = chat._id.toString();
                    }
                }
            }
            for (const userId in people) {
                people[userId].chats = [...new Set(people[userId].chats)];
            }
            // emit socket events to those users
            const event = status === 'offline' ? 'user-offline' : 'user-online';
            for (const userId in people) {
                socketNotifyUser({
                    userId,
                    event,
                    data: people[userId]
                });
            }
        }
        catch (error) {
            console.log(error);
        }
    });
}
function runAiPrompt(userPrompt, systemPrompt) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const response = yield config_1.default.openai.chat.completions.create({
                model: 'gpt-4o-mini',
                messages: [
                    { role: 'system', content: systemPrompt },
                    { role: 'user', content: userPrompt }
                ]
            });
            return response.choices[0].message.content;
        }
        catch (error) {
            console.log(error);
        }
    });
}
function analyzeReelByAi(systemPrompt, reelData) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const content = [];
            if (reelData.video) {
                content.push({ type: 'input_file', file_url: reelData.video });
            }
            else if (reelData.photos) {
                for (const image of reelData.photos) {
                    content.push({ type: 'input_file', file_url: image });
                }
            }
            else if (reelData.dynamic) {
                for (const video of reelData.dynamic) {
                    content.push({ type: 'input_file', file_url: video });
                }
            }
            const response = yield config_1.default.openai.responses.create({
                model: 'gpt-4o-mini',
                input: [
                    { role: 'system', content: systemPrompt },
                    { role: 'user', content }
                ]
            });
            const json = JSON.parse(response.output_text);
            return json;
        }
        catch (error) {
            console.log(error);
        }
    });
}
function updateScore(score) {
    return __awaiter(this, void 0, void 0, function* () {
        var _a, _b, _c, _d, _e, _f, _g;
        try {
            if (score.totalScore === 1) {
                return;
            }
            // recalculate social score and save it
            const social = score.social;
            let count = social.actions.length;
            let completedCount = social.actions.filter(e => e.completed).length;
            let totalValues = social.actions.reduce((acc, e) => acc + e.value, 0.0);
            social.score = totalValues / count;
            social.score = +(social.score).toFixed(2);
            if (completedCount === count) {
                social.score = 1;
            }
            // recalculate mindfulness score and save it
            const mindfulness = score.mindfulness;
            count = mindfulness.actions.length;
            completedCount = mindfulness.actions.filter(e => e.completed).length;
            totalValues = mindfulness.actions.reduce((acc, e) => acc + e.value, 0.0);
            mindfulness.score = totalValues / count;
            mindfulness.score = +(mindfulness.score).toFixed(2);
            if (completedCount === count) {
                mindfulness.score = 1;
            }
            score.totalScore = (social.score + mindfulness.score) / 2;
            score.totalScore = +score.totalScore.toFixed(2);
            score.totalScore = Math.min(score.totalScore, 1);
            yield score.save();
            // send socket notification
            const _mindfulness = JSON.parse(JSON.stringify(score.mindfulness));
            delete _mindfulness.journal;
            services.socketNotifyUser({
                userId: `${score.user}`,
                event: 'score-updated',
                data: {
                    date: score.date,
                    reelsSeen: score.reelsSeen,
                    totalScore: score.totalScore,
                    social: JSON.parse(JSON.stringify(score.social)),
                    mindfulness: _mindfulness
                }
            });
            if (score.totalScore === 1) {
                const day = constants_1.default.days[score.date.getDay()];
                if (day !== 'wednesday' && day !== 'sunday') {
                    return;
                }
                const previousScores = score.weeklyScores;
                let isRewardEligible = false;
                // checking if the day is wednesday or thursday, if yes, then all previous days scores must be all completed
                const monday = ((_a = previousScores.find(e => e.day === 'monday')) === null || _a === void 0 ? void 0 : _a.score) === 1;
                const tuesday = ((_b = previousScores.find(e => e.day === 'tuesday')) === null || _b === void 0 ? void 0 : _b.score) === 1;
                const wednesday = ((_c = previousScores.find(e => e.day === 'wednesday')) === null || _c === void 0 ? void 0 : _c.score) === 1;
                const thursday = ((_d = previousScores.find(e => e.day === 'thursday')) === null || _d === void 0 ? void 0 : _d.score) === 1;
                const friday = ((_e = previousScores.find(e => e.day === 'friday')) === null || _e === void 0 ? void 0 : _e.score) === 1;
                const saturday = ((_f = previousScores.find(e => e.day === 'saturday')) === null || _f === void 0 ? void 0 : _f.score) === 1;
                if (day === 'wednesday' && monday && tuesday) {
                    isRewardEligible = true;
                }
                if (day === 'sunday' && monday && tuesday && wednesday && thursday && friday && saturday) {
                    isRewardEligible = true;
                }
                if (!isRewardEligible) {
                    return;
                }
                // now creating reward for that user
                const user = yield models_2.User.findById(score.user);
                if (!user) {
                    return;
                }
                // fetching unique profile rewardIds from reward hashes which already exist for this user
                const rewardIds = (yield models_7.RewardHash.find({ user: score.user }, { reward: 1 })).map(e => e.reward);
                // now fetching any one reward where _id don't includes any from rewardIds
                rewardIds.forEach(e => console.log(`${e}`));
                const query = {
                    _id: { $nin: rewardIds },
                    remaining: { $gt: 0 },
                    $and: [
                        {
                            $or: [
                                { isMeetme: false },
                                { isMeetme: { $exists: false } }
                            ]
                        },
                        {
                            $or: [
                                { expiredAt: { $gt: new Date() } },
                                { expiredAt: { $exists: false } }
                            ]
                        },
                    ]
                };
                if (user.location) {
                    query.$and.push({
                        $or: [
                            {
                                location: {
                                    $geoWithin: {
                                        $centerSphere: [
                                            user.location.coordinates, // [lng, lat]
                                            constants_1.default.locationDistance / constants_1.default.earthRadius // meters → radians
                                        ]
                                    }
                                }
                            },
                            { location: { $exists: false } }
                        ]
                    });
                }
                const reward = yield models_8.Reward.findOne(query);
                if (!reward) {
                    return;
                }
                // creating reward hash for this reward
                const rewardHash = yield models_7.RewardHash.create({
                    user: user._id,
                    reward: reward._id,
                    hash: helpers_1.default.getHash(`${user._id}:${reward._id}`),
                    score: score._id,
                });
                score.hash = rewardHash._id;
                yield score.save();
                // finally notify user
                yield services.notifyUser({
                    title: 'Reward Unlocked',
                    description: `Congrats ${(_g = user.name) === null || _g === void 0 ? void 0 : _g.split(' ')[0]}! you have unlocked a new reward`,
                    event: 'reward-unlocked',
                    user: user,
                    rewardHash: rewardHash._id
                });
            }
        }
        catch (error) {
            console.error(error);
        }
    });
}
function getOrCreateTodayScore(userId) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const now = new Date();
            const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
            let score = yield models_6.Score.findOne({ date: today, user: userId });
            if (score) {
                return score;
            }
            const weekAgo = new Date(today);
            weekAgo.setDate(today.getDate() - 7);
            const scores = yield models_6.Score
                .find({ date: { $gte: weekAgo }, user: userId })
                .sort({ createdAt: -1 });
            score = yield models_6.Score.create({
                user: userId,
                date: today,
                weeklyScores: scores.map(e => ({
                    score: e.totalScore,
                    date: e.date,
                    day: constants_1.default.days[e.date.getDay()],
                    reference: e._id
                }))
            });
            return score;
        }
        catch (error) {
            console.error(error);
        }
    });
}
function enqueueVideoCompression(inputUrl, message, onDone) {
    const getCompressedUrls = (url) => {
        const inputPath = 'temp/' + url.split('/').at(-1);
        const lastDot = url.lastIndexOf('.');
        let u = '';
        if (lastDot === -1) {
            u = url + '-compressed';
        }
        else {
            const base = url.substring(0, lastDot);
            const ext = url.substring(lastDot);
            u = `${base}-compressed${ext}`;
        }
        const outputFile = u.split('/').at(-1);
        const outputPath = 'temp/' + outputFile;
        const outputCloudFilePath = u.substring(u.indexOf('reels/'));
        return { outputUrl: u, outputFile, outputCloudFilePath, outputPath, inputPath };
    };
    return config_1.default.compressionQueue.add(() => __awaiter(this, void 0, void 0, function* () {
        try {
            constants_1.default.compressionProcessingCount++;
            console.log(message);
            // downloading video
            const inputFile = inputUrl.split('/').at(-1);
            const inputPath = 'temp/' + inputFile;
            yield helpers_1.default.downloadFile(inputUrl, inputPath);
            // generate output path, and pass it to the function
            const { outputUrl, outputFile, outputCloudFilePath, outputPath } = getCompressedUrls(inputUrl);
            yield helpers_1.default.compressVideo(inputPath, outputPath);
            // check if generated file size is smaller or even more than previous size
            const [inputStat, outputStat] = yield Promise.all([promises_1.default.stat(inputPath), promises_1.default.stat(outputPath)]);
            if (outputStat.size >= inputStat.size) {
                throw new Error('Unable to compress even more, file already compressed');
            }
            // upload the file to cloudflare R2 with same path
            const fileBuffer = yield promises_1.default.readFile(outputPath);
            yield helpers_1.default.uploadFile(fileBuffer, outputCloudFilePath);
            yield onDone(undefined, outputUrl);
        }
        catch (error) {
            // handling onDone in error case
            console.log(error);
            try {
                yield onDone(error, undefined);
            }
            catch (error) {
                console.log(error);
            }
        }
        finally {
            // deleting file
            try {
                const { outputPath, inputPath } = getCompressedUrls(inputUrl);
                yield promises_1.default.unlink(outputPath);
                yield promises_1.default.unlink(inputPath);
            }
            catch (error) {
                console.log(error);
            }
            constants_1.default.compressionProcessingCount--;
        }
    }));
}
function addReelToCompressionQueue(reel) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const hasCompressedUrl = (url) => {
                const lastDot = url.lastIndexOf('.');
                if (lastDot === -1)
                    return url.endsWith('-compressed');
                const base = url.substring(0, lastDot);
                return base.endsWith('-compressed');
            };
            if (!reel.dynamic && !reel.video) {
                return;
            }
            if (reel.compressionResult.isCompleted) {
                return;
            }
            if (`${reel._id}` in constants_1.default.reelsInCompression) {
                return;
            }
            constants_1.default.reelsInCompression[`${reel._id}`] = true;
            console.log(`🧱 Adding reel ${reel._id} to compression queue`);
            if (reel.dynamic) {
                for (let i = 0; i < reel.dynamic.length; i++) {
                    const dynamic = reel.dynamic[i];
                    if (hasCompressedUrl(dynamic.video)) {
                        continue;
                    }
                    enqueueVideoCompression(dynamic.video, `🧱 Reel ${reel._id} dynamic.${i} compression started`, (err, outputUrl) => __awaiter(this, void 0, void 0, function* () {
                        var _a;
                        try {
                            if (err) {
                                (_a = reel.compressionResult).errors || (_a.errors = {});
                                reel.compressionResult.errors[`dynamic.${i}`] = err.toString();
                            }
                            if (outputUrl) {
                                dynamic.video = outputUrl;
                            }
                            yield reel.save();
                            if (!reel.video) {
                                reel.compressionResult.isCompleted = true;
                                yield reel.save();
                                delete constants_1.default.reelsInCompression[`${reel._id}`];
                                console.log(`✅ reel ${reel._id} dynamic.${i} compression completed`);
                            }
                        }
                        catch (error) {
                            console.log(error);
                            if (!reel.video) {
                                delete constants_1.default.reelsInCompression[`${reel._id}`];
                            }
                        }
                        finally {
                        }
                    }));
                }
            }
            if (reel.video && !hasCompressedUrl(reel.video)) {
                enqueueVideoCompression(reel.video, `🧱 Reel ${reel._id} video compression started`, (err, outputUrl) => __awaiter(this, void 0, void 0, function* () {
                    var _a;
                    try {
                        if (err) {
                            (_a = reel.compressionResult).errors || (_a.errors = {});
                            reel.compressionResult.errors['video'] = err.toString();
                        }
                        if (outputUrl) {
                            reel.video = outputUrl;
                        }
                        yield reel.save();
                        reel.compressionResult.isCompleted = true;
                        yield reel.save();
                        delete constants_1.default.reelsInCompression[`${reel._id}`];
                        console.log(`✅ reel ${reel._id} video compression completed`);
                    }
                    catch (error) {
                        console.log(error);
                        delete constants_1.default.reelsInCompression[`${reel._id}`];
                        console.log(`✅ reel ${reel._id} video compression completed`);
                    }
                }));
            }
            if (!reel.dynamic && !(reel.video && !hasCompressedUrl(reel.video))) {
                reel.compressionResult.isCompleted = true;
                yield reel.save();
                delete constants_1.default.reelsInCompression[`${reel._id}`];
                console.log(`✅ reel ${reel._id} compression completed`);
            }
        }
        catch (error) {
            console.log(error);
        }
    });
}
function addUncompressedReelsToQueue() {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            if (!constants_1.default.compressionEnabled) {
                return;
            }
            if (config_1.default.compressionQueue.size >= constants_1.default.maxCompressionQueueSize) {
                return;
            }
            const reels = yield models_3.Reel.find({
                $or: [
                    { 'compressionResult.isCompleted': false },
                    { 'compressionResult.isCompleted': { $exists: false } },
                ]
            }).sort({ createdAt: 1 }).limit(100);
            for (const reel of reels) {
                yield addReelToCompressionQueue(reel);
                if (config_1.default.compressionQueue.size >= constants_1.default.maxCompressionQueueSize) {
                    break;
                }
            }
        }
        catch (error) {
            console.log(error);
        }
    });
}
function clearTempFolder() {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const files = yield promises_1.default.readdir('temp');
            for (const file of files) {
                yield promises_1.default.unlink(`temp/${file}`);
            }
        }
        catch (err) { }
    });
}
function isUserBlocked(userId1, userId2) {
    return __awaiter(this, void 0, void 0, function* () {
        const blocking = yield models_9.Blocking.findOne({
            $or: [
                { user: userId1, blockedBy: userId2 },
                { blockedBy: userId1, user: userId2 },
            ]
        });
        return !!blocking;
    });
}
function createScheduler(event, at, data) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const id = `job-${event}-${Date.now()}-${Math.random().toString().split('.').at(-1)}`;
            yield helpers_1.default.createEventBridgeSchedule(id, at, { id, event, data });
            console.log('⏰ Schedule created:', id);
            return id;
        }
        catch (error) {
            console.log('Error occurred while create schedule');
            console.log(error);
        }
    });
}
// config.compressionQueue.addListener('idle', addUncompressedReelsToQueue)
addUncompressedReelsToQueue();
const services = {
    createAdmin,
    sendOtp,
    checkAndResetOtp,
    deleteOldUnverifiedUsers,
    deleteDeactivatedUsers,
    pushNotifyUser,
    nestedDynamicToFlat,
    notifyUser,
    flatDynamicToNested,
    createLoggedinDevice,
    deleteOldNotifications,
    socketNotifyUser,
    socketErrorNotifyUser,
    socketNotifyOnUserPresenceUpdated,
    runAiPrompt,
    analyzeReelByAi,
    updateScore,
    getOrCreateTodayScore,
    enqueueVideoCompression,
    addReelToCompressionQueue,
    addUncompressedReelsToQueue,
    clearTempFolder,
    isUserBlocked,
    createScheduler
};
exports.default = services;
