"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("./models");
const helpers_1 = __importDefault(require("../../utils/helpers"));
const models_2 = require("../followers/models");
const models_3 = require("../connections/models");
const models_4 = require("../statuses/models");
const services_1 = __importDefault(require("../../utils/services"));
const models_5 = require("../chats/models");
const models_6 = require("../super-likes/models");
const constants_1 = __importDefault(require("../../utils/constants"));
function getUsers(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        var _a, _b, _c;
        try {
            const u = res.locals.user;
            let { limit = '10', page = '1', search, categories } = req.query;
            limit = +limit;
            page = +page;
            const matchStage = {
                _id: { $ne: u._id },
                name: { $exists: true },
                $and: [
                    {
                        $or: [
                            { terminated: false },
                            { terminated: { $exists: false } }
                        ]
                    }
                ]
            };
            if (search) {
                matchStage.$and.push({
                    $or: [
                        { name: RegExp(search, 'i') },
                        { username: RegExp(search, 'i') },
                    ]
                });
            }
            if (categories) {
                const categoryArray = categories.split(',');
                matchStage.categories = { $in: categoryArray };
            }
            const pipeline = [
                { $match: matchStage },
                {
                    // Lookup connection between current user and this user
                    $lookup: {
                        from: 'connections',
                        let: { otherUserId: '$_id' },
                        pipeline: [
                            {
                                $match: {
                                    $expr: {
                                        $or: [
                                            { $and: [{ $eq: ['$from', u._id] }, { $eq: ['$to', '$$otherUserId'] }] },
                                            { $and: [{ $eq: ['$to', u._id] }, { $eq: ['$from', '$$otherUserId'] }] }
                                        ]
                                    }
                                }
                            },
                            { $limit: 1 }
                        ],
                        as: 'connection'
                    }
                },
                { $unwind: { path: '$connection', preserveNullAndEmptyArrays: true } },
                {
                    $project: {
                        name: 1,
                        username: 1,
                        bio: 1,
                        auth: {
                            emailVerified: 1,
                            phoneVerified: 1,
                        },
                        photo: 1,
                        createdAt: 1,
                        updatedAt: 1,
                        connection: 1,
                        verified: 1,
                    }
                },
                { $sort: { createdAt: -1 } },
                {
                    $facet: {
                        users: [
                            { $skip: (page - 1) * limit },
                            { $limit: limit }
                        ],
                        totalCount: [
                            { $count: 'count' }
                        ]
                    }
                }
            ];
            const result = yield models_1.User.aggregate(pipeline);
            const users = ((_a = result[0]) === null || _a === void 0 ? void 0 : _a.users) || [];
            const count = ((_c = (_b = result[0]) === null || _b === void 0 ? void 0 : _b.totalCount[0]) === null || _c === void 0 ? void 0 : _c.count) || 0;
            return res.json({ users, count });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
// explore search
function getExploreUsersAndChats(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const u = res.locals.user;
            let { limit = '10', search = '' } = req.query;
            limit = +limit;
            let pipeline = [
                {
                    $match: {
                        _id: { $ne: u._id },
                        name: { $exists: true },
                        $and: [
                            {
                                $or: [
                                    { terminated: false },
                                    { terminated: { $exists: false } }
                                ]
                            },
                            {
                                $or: [
                                    { name: RegExp(search, 'i') },
                                    { username: RegExp(search, 'i') },
                                ]
                            }
                        ]
                    }
                },
                {
                    // Lookup connection between current user and this user
                    $lookup: {
                        from: 'connections',
                        let: { otherUserId: '$_id' },
                        pipeline: [
                            {
                                $match: {
                                    $expr: {
                                        $or: [
                                            { $and: [{ $eq: ['$from', u._id] }, { $eq: ['$to', '$$otherUserId'] }] },
                                            { $and: [{ $eq: ['$to', u._id] }, { $eq: ['$from', '$$otherUserId'] }] }
                                        ]
                                    }
                                }
                            },
                            { $limit: 1 }
                        ],
                        as: 'connection'
                    }
                },
                { $unwind: { path: '$connection', preserveNullAndEmptyArrays: true } },
                {
                    $project: {
                        name: 1,
                        username: 1,
                        bio: 1,
                        auth: {
                            emailVerified: 1,
                            phoneVerified: 1,
                        },
                        photo: 1,
                        createdAt: 1,
                        updatedAt: 1,
                        connection: 1,
                        verified: 1,
                    }
                },
                { $sort: { followers: -1 } },
                { $limit: limit }
            ];
            const users = yield models_1.User.aggregate(pipeline);
            const chats = yield models_5.Chat.aggregate([
                {
                    $match: {
                        name: { $regex: search, $options: 'i' },
                        isGroup: true
                    }
                },
                // populate request
                {
                    $lookup: {
                        from: 'requests',
                        let: { chatId: '$_id', userId: u._id },
                        pipeline: [
                            {
                                $match: {
                                    $expr: {
                                        $and: [
                                            { $eq: ['$chat', '$$chatId'] },
                                            { $eq: ['$user', '$$userId'] },
                                            { $eq: ['$status', 'pending'] },
                                        ]
                                    }
                                }
                            },
                            { $limit: 1 }
                        ],
                        as: 'request'
                    }
                },
                { $unwind: { path: '$request', preserveNullAndEmptyArrays: true } },
                {
                    $addFields: {
                        participantsCount: { $size: '$participants' }
                    }
                },
                {
                    $project: {
                        isGroup: 1,
                        name: 1,
                        event: 1,
                        photo: 1,
                        allowPublicPost: 1,
                        restricted: 1,
                        request: 1,
                        participants: 1,
                        participantsCount: 1,
                        createdAt: 1,
                        updatedAt: 1
                    }
                },
                {
                    $sort: { participantsCount: -1 }
                },
                {
                    $limit: limit
                }
            ]);
            for (const chat of chats) {
                const p = chat.participants.find((e) => `${e.user}` === `${u._id}`);
                chat.isJoined = !!p;
                chat.areYouAdmin = !!(p === null || p === void 0 ? void 0 : p.isAdmin);
            }
            return res.json({ users, chats });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
// meetme online users
function getMeetmeOnlineUsers(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const u = res.locals.user;
            let { limit = '10', page = '1' } = req.query;
            limit = +limit;
            page = +page;
            const skip = (page - 1) * limit;
            const onlineMinutesAgo = new Date(Date.now() - constants_1.default.maxOnlineVisibilityInMinutes * 60 * 1000);
            const match = {
                _id: { $ne: u._id },
                name: { $exists: true },
                $and: [
                    {
                        $or: [
                            { terminated: false },
                            { terminated: { $exists: false } }
                        ],
                    },
                    {
                        $or: [
                            { isOnline: true },
                            {
                                isOnline: { $ne: true },
                                lastOnlineAt: { $gte: onlineMinutesAgo }
                            }
                        ],
                    }
                ]
            };
            if (constants_1.default.locationMode && u.location) {
                match.$and.push({
                    $or: [
                        {
                            location: {
                                $geoWithin: {
                                    $centerSphere: [
                                        u.location.coordinates, // [lng, lat]
                                        constants_1.default.locationDistance / constants_1.default.earthRadius // meters → radians
                                    ]
                                }
                            }
                        },
                        { location: { $exists: false } }
                    ]
                });
            }
            const result = yield models_1.User.aggregate([
                // STEP 1: online filter
                { $match: match },
                // STEP 2: ensure mutual follow exists
                {
                    $lookup: {
                        from: 'mutual-follows',
                        let: { uid: '$_id' },
                        pipeline: [
                            {
                                $match: {
                                    $expr: {
                                        $and: [
                                            { $in: [u._id, '$users'] },
                                            { $in: ['$$uid', '$users'] }
                                        ]
                                    }
                                }
                            }
                        ],
                        as: 'mutual'
                    }
                },
                {
                    $match: {
                        'mutual.0': { $exists: true } // must be mutual
                    }
                },
                // STEP 3: FACET → apply count + pagination + chat lookup
                {
                    $facet: {
                        // total count
                        metadata: [
                            { $count: 'total' }
                        ],
                        users: [
                            // pagination
                            { $skip: skip },
                            { $limit: limit },
                            // chat lookup
                            {
                                $lookup: {
                                    from: 'chats',
                                    let: { uid: '$_id' },
                                    pipeline: [
                                        {
                                            $match: {
                                                isGroup: { $ne: true },
                                                $expr: {
                                                    $and: [
                                                        { $in: [u._id, '$participants.user'] },
                                                        { $in: ['$$uid', '$participants.user'] }
                                                    ]
                                                }
                                            }
                                        },
                                        { $limit: 1 }
                                    ],
                                    as: 'chat'
                                }
                            },
                            {
                                $addFields: {
                                    chat: { $arrayElemAt: ['$chat', 0] }
                                }
                            },
                            // projection
                            {
                                $project: {
                                    name: 1,
                                    username: 1,
                                    photo: 1,
                                    isOnline: 1,
                                    lastOnlineAt: 1,
                                    chat: 1
                                }
                            },
                            // sort users by recent online activity
                            { $sort: { lastOnlineAt: -1 } }
                        ]
                    }
                },
                // STEP 4: flatten metadata
                {
                    $addFields: {
                        total: { $ifNull: [{ $arrayElemAt: ['$metadata.total', 0] }, 0] }
                    }
                },
                {
                    $project: {
                        metadata: 0
                    }
                }
            ]);
            const { total: count, users } = result[0];
            return res.json({ users, count });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function getProfile(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const user = res.locals.user;
            if (user.isManuallyOffline) {
                user.isOnline = false;
            }
            else if (!user.isOnline && user.lastOnlineAt) {
                user.lastOnlineAt.setMinutes(user.lastOnlineAt.getMinutes() + constants_1.default.maxOnlineVisibilityInMinutes);
                if (user.lastOnlineAt >= new Date()) {
                    user.isOnline = true;
                }
            }
            if (user.status) {
                const status = yield models_4.Status.findById(user.status);
                if (status && status.endedAt < new Date()) {
                    user.status = undefined;
                    yield user.save();
                }
                else if (status) {
                    // @ts-ignore
                    user.status = status;
                }
            }
            // @ts-ignore
            user.auth = {
                twoFactorAuthentication: user.auth.twoFactorAuthentication,
                emailVerified: user.auth.emailVerified,
                phoneVerified: user.auth.phoneVerified,
            };
            const lastDay = new Date();
            lastDay.setHours(lastDay.getHours() - 24);
            const count = yield models_6.SuperLike.countDocuments({
                user: user._id,
                at: { $gt: lastDay }
            });
            // @ts-ignore
            const doc = user._doc;
            doc.isSuperLikeAvailable = count < 1;
            return res.json({ user: doc });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function getUser(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const u = res.locals.user;
            const id = req.params.id;
            const user = yield models_1.User.findById(id, {
                name: 1,
                username: 1,
                bio: 1,
                url: 1,
                school: 1,
                courses: 1,
                email: 1,
                phone: 1,
                dob: 1,
                auth: {
                    emailVerified: 1,
                    phoneVerified: 1,
                },
                photo: 1,
                terminated: 1,
                purposes: 1,
                studies: 1,
                categories: 1,
                isManuallyOffline: 1,
                isOnline: 1,
                lastOnlineAt: 1,
                followers: 1,
                followings: 1,
                posts: 1,
                status: 1,
                location: 1,
                locationString: 1,
                verified: 1,
                createdAt: 1,
                updatedAt: 1
            }).populate('status');
            if (!user || user.terminated) {
                return res.status(404).json({ message: 'User not found' });
            }
            if (user.isManuallyOffline) {
                user.isOnline = false;
            }
            else if (!user.isOnline && user.lastOnlineAt) {
                user.lastOnlineAt.setMinutes(user.lastOnlineAt.getMinutes() + constants_1.default.maxOnlineVisibilityInMinutes);
                if (user.lastOnlineAt >= new Date()) {
                    user.isOnline = true;
                }
            }
            const isBlocked = yield services_1.default.isUserBlocked(user._id, u._id);
            if (isBlocked) {
                return res.status(403).json({ message: "Unable to get this user's data as its blocked" });
            }
            // Check if target user follows me
            const isFollower = yield models_2.Follower.exists({
                follower: id,
                following: u._id
            });
            // Check if I follow target user
            const isFollowing = yield models_2.Follower.exists({
                follower: u._id,
                following: id
            });
            const status = user.status;
            user.status = undefined;
            const connection = yield models_3.Connection.findOne({
                $or: [
                    { from: user._id, to: u._id },
                    { to: user._id, from: u._id }
                ]
            })
                .populate({
                path: 'from',
                select: { name: 1, username: 1, bio: 1, photo: 1 }
            })
                .populate({
                path: 'to',
                select: { name: 1, username: 1, bio: 1, photo: 1 }
            });
            const chat = yield models_5.Chat.findOne({
                $or: [
                    { creator: u._id, user: user._id },
                    { user: u._id, creator: user._id },
                ]
            }, { _id: 1 });
            return res.json({
                user,
                isFollower: !!isFollower,
                isFollowing: !!isFollowing,
                status,
                connection,
                chatId: chat === null || chat === void 0 ? void 0 : chat._id
            });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function patchUser(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const { name, username, bio, url, school, courses, dob, phone, password, oldPassword, twoFactorAuthentication, isManuallyOffline, purposes, studies, categories, platform, fcmToken, latitude, longitude, locationString } = req.body;
            let user = res.locals.user;
            const device = res.locals.device;
            let token;
            if (password && oldPassword) {
                if (user.auth.password !== helpers_1.default.getHash(oldPassword)) {
                    return res.status(401).json({ message: 'Old password is incorrect' });
                }
                user.auth.password = helpers_1.default.getHash(password);
                token = helpers_1.default.getToken(user);
            }
            if (phone && user.phone !== phone) {
                user.phone = phone;
                user.auth.phoneVerified = false;
                user.auth.phoneOtp = undefined;
                user.auth.phoneOtpExpiry = undefined;
                user.auth.lastPhoneOtpSentAt = undefined;
            }
            if (username && user.username !== username) {
                const u = yield models_1.User.findOne({ username });
                if (u) {
                    return res.status(409).json({ message: 'username is not available' });
                }
            }
            user.dob = dob ? new Date(dob) : user.dob;
            user.name = name !== null && name !== void 0 ? name : user.name;
            user.bio = bio !== null && bio !== void 0 ? bio : user.bio;
            user.url = url !== null && url !== void 0 ? url : user.url;
            if (url === null) {
                user.url = undefined;
            }
            user.school = school !== null && school !== void 0 ? school : user.school;
            user.courses = courses !== null && courses !== void 0 ? courses : user.courses;
            user.purposes = purposes !== null && purposes !== void 0 ? purposes : user.purposes;
            user.studies = studies !== null && studies !== void 0 ? studies : user.studies;
            user.username = username !== null && username !== void 0 ? username : user.username;
            user.categories = categories !== null && categories !== void 0 ? categories : user.categories;
            user.locationString = locationString !== null && locationString !== void 0 ? locationString : user.locationString;
            if (typeof latitude === 'number' && typeof longitude === 'number') {
                user.location = {
                    type: 'Point',
                    coordinates: [longitude, latitude]
                };
            }
            user.auth.twoFactorAuthentication = twoFactorAuthentication !== null && twoFactorAuthentication !== void 0 ? twoFactorAuthentication : user.auth.twoFactorAuthentication;
            if (!user.school && studies) {
                user.school = studies.flatMap(e => e.value).join(', ');
            }
            if (typeof isManuallyOffline === 'boolean') {
                user.isManuallyOffline = isManuallyOffline || undefined;
            }
            device.fcmToken = fcmToken !== null && fcmToken !== void 0 ? fcmToken : device.fcmToken;
            device.platform = platform !== null && platform !== void 0 ? platform : device.platform;
            yield user.save();
            if (typeof isManuallyOffline === 'boolean') {
                services_1.default.socketNotifyOnUserPresenceUpdated(user, isManuallyOffline ? 'offline' : 'online');
            }
            return res.json({ token, message: 'user updated successfully' });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function postCheckUsername(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const username = req.body.username;
            const user = yield models_1.User.findOne({ username });
            if (user) {
                return res.status(409).json({ message: `The username ${username} is already taken` });
            }
            return res.json({ message: 'username is available' });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function putProfilePhoto(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            let user = res.locals.user;
            const file = req.file;
            const path = `users/${user._id}/${file.filename}`;
            if (user.photo) {
                yield helpers_1.default.deleteR2File(user.photo);
            }
            user.photo = yield helpers_1.default.uploadFile(file, path);
            yield user.save();
            helpers_1.default.deleteFile(file);
            return res.status(201).json({ message: 'photo updated successfully', photo: user.photo });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
function deleteUser(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const user = res.locals.user;
            user.auth.loggedinDevices = [];
            user.deactivatedAt = new Date();
            yield user.save();
            yield helpers_1.default.sendOauthGmail(user.email, 'Account scheduled for deletion', `Account is successfully scheduled for deletion, It will be automatically deleted after 7 days`);
            return res.json({ message: 'Account is successfully scheduled for deletion, It will be automatically deleted after 7 days' });
        }
        catch (error) {
            console.log(error);
            return res.status(500).json({ message: 'server error' });
        }
    });
}
const controllers = {
    getUsers,
    getExploreUsersAndChats,
    getMeetmeOnlineUsers,
    getProfile,
    getUser,
    patchUser,
    postCheckUsername,
    putProfilePhoto,
    deleteUser
};
exports.default = controllers;
