services/NotificationService.jsjavascript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const errors_1 = require("../utils/errors.js");
const notification_1 = __importDefault(require("../db/models/notification"));
const UserService_1 = __importDefault(require("./UserService"));
const { Op } = require("sequelize");
const { logger } = require("../plugins/winston");
const roundToDecimals = require("../utils/numbers");
/**
 * Service for managing notifications.
 * Handles notification creation, delivery, and management.
 *
 * @class NotificationService
 */
class NotificationService {
    /**
     * Get all notifications for a user.
     * @param {UUID} UserId - The user's UUID.
     * @param {Object} options - Options for fetching notifications.
     * @param {boolean} options.all - Whether to fetch all notifications.
     * @returns {Promise<Array>} List of notifications.
     */
    getAll(UserId, options = { all: false }) {
        logger.debug(`NotificationService getAll ${JSON.stringify({ UserId, options })}`);
        return notification_1.default.findAll({
            where: options.all || !UserId ? {} : { UserId },
            order: [["createdAt", "DESC"]],
        });
    }
    /**
     * Find a notification by ID.
     * @param {UUID} id - Notification ID.
     * @param {UUID} UserId - The user's UUID.
     * @returns {Promise<Object|null>} The notification object or null.
     */
    findById(id, UserId) {
        logger.debug(`NotificationService findById ${JSON.stringify({ id, UserId })}`);
        return notification_1.default.findOne({ where: { id, UserId } });
    }
    /**
     * Create notifications for all users.
     * @param {string} title - Notification title.
     * @param {string} message - Notification message.
     * @returns {Promise<Array>} List of created notifications.
     */
    createForAll(title, message) {
        logger.debug(`NotificationService createForAll ${JSON.stringify({ title, message })}`);
        return UserService_1.default.getAll().then((users) => {
            return Promise.all(users.map((user) => this.createOne(title, message, user.id)));
        });
    }
    /**
     * Create a notification for a single user.
     * @param {string} title - Notification title.
     * @param {string} message - Notification message.
     * @param {UUID} UserId - The user's UUID.
     * @returns {Promise<Object>} The created notification object.
     */
    createOne(title, message, UserId) {
        logger.debug(`NotificationService createOne ${JSON.stringify({
            title,
            message,
            UserId,
        })}`);
        return notification_1.default.create({
            title,
            message,
            UserId,
        });
    }
    /**
     * Find or create a notification.
     * @param {string} title - Notification title.
     * @param {string} message - Notification message.
     * @param {UUID} UserId - The user's UUID.
     * @returns {Promise<Array<Object|boolean>>} The notification object and a boolean indicating if the notification was created.
     */
    async findOrCreate(title, message, UserId) {
        logger.debug(`NotificationService findOrCreate ${JSON.stringify({
            title,
            message,
            UserId,
        })}`);
        const [notification, created] = await notification_1.default.findOrCreate({
            where: {
                title,
                message,
                UserId,
            },
        });
        return [notification, created];
    }
    /**
     * Mark a notification as read.
     * @param {UUID} id - Notification ID.
     * @param {UUID} UserId - The user's UUID.
     * @returns {Promise<Object>} The updated notification object.
     */
    markRead(id, UserId) {
        logger.debug(`NotificationService markRead ${JSON.stringify({ id, UserId })}`);
        return this.update(id, UserId, { read: true });
    }
    /**
     * Mark a notification as unread.
     * @param {UUID} id - Notification ID.
     * @param {UUID} UserId - The user's UUID.
     * @returns {Promise<Object>} The updated notification object.
     */
    markUnread(id, UserId) {
        logger.debug(`NotificationService markUnread ${JSON.stringify({ id, UserId })}`);
        return this.update(id, UserId, { read: false });
    }
    /**
     * Update a notification.
     * @param {UUID} id - Notification ID.
     * @param {UUID} UserId - The user's UUID.
     * @param {Object} data - Data to update.
     * @returns {Promise<Object>} The updated notification object.
     */
    update(id, UserId, data) {
        logger.debug(`NotificationService update ${JSON.stringify({
            id,
            data,
            UserId,
        })}`);
        return notification_1.default.findOne({
            where: { id, UserId },
        }).then(async (foundNotification) => {
            if (foundNotification) {
                await foundNotification.update(data);
                return foundNotification.save();
            }
            else {
                logger.error(`No notification found with ID ${id} when updating`);
                throw new errors_1.NotFoundError(`No notification found with ID ${id} when updating`);
            }
        });
    }
    /**
     * Remove a notification.
     * @param {UUID} id - Notification ID.
     * @param {UUID} UserId - The user's UUID.
     * @param {Object} options - Options for removing notifications.
     * @param {boolean} options.all - Whether to remove all notifications.
     * @returns {Promise<void>} Resolves if notification removed.
     */
    remove(id, UserId, options = { all: false }) {
        logger.debug(`NotificationService remove ${JSON.stringify({ id, UserId })}`);
        return notification_1.default.findOne({
            where: options.all || !UserId ? { id } : { id, UserId },
        }) // njsscan-ignore: node_nosqli_injection
            .then((foundNotification) => {
            if (foundNotification) {
                return foundNotification.destroy();
            }
            else {
                logger.error(`No notification found with ID ${id} when deleting`);
                throw new errors_1.NotFoundError(`No notification found with ID ${id} when deleting`);
            }
        });
    }
    /**
     * Get the percentage of read notifications.
     * @returns {Promise<number>} The percentage of read notifications.
     */
    getReadPercentage() {
        logger.debug(`NotificationService getReadPercentage`);
        return Promise.all([
            notification_1.default.count({
                where: {
                    read: true,
                    createdAt: {
                        [Op.gt]: new Date().valueOf() - 1000 * 60 * 60 * 24 * 30,
                    },
                },
            }),
            notification_1.default.count({
                where: {
                    createdAt: {
                        [Op.gt]: new Date().valueOf() - 1000 * 60 * 60 * 24 * 30,
                    },
                },
            }),
        ]).then(([readNoticationsCount, allNoticiationsCount]) => {
            if (allNoticiationsCount == 0)
                return 100;
            return roundToDecimals((readNoticationsCount / allNoticiationsCount) * 100, 1);
        });
    }
    /**
     * Get the trend of read notifications.
     * @returns {Promise<number>} The trend percentage of read notifications.
     */
    getReadTrend() {
        logger.debug(`NotificationService getReadTrend`);
        return Promise.all([
            notification_1.default.count({
                where: {
                    read: true,
                    createdAt: {
                        [Op.gt]: new Date().valueOf() - 1000 * 60 * 60 * 24 * 30,
                    },
                },
            }),
            notification_1.default.count({
                where: {
                    createdAt: {
                        [Op.gt]: new Date().valueOf() - 1000 * 60 * 60 * 24 * 30,
                    },
                },
            }),
            notification_1.default.count({
                where: {
                    read: true,
                    createdAt: {
                        [Op.lt]: new Date().valueOf() - 1000 * 60 * 60 * 24 * 30,
                    },
                },
            }),
            notification_1.default.count({
                where: {
                    createdAt: {
                        [Op.lt]: new Date().valueOf() - 1000 * 60 * 60 * 24 * 30,
                    },
                },
            }),
        ]).then(([readNoticationsCountLastMonth, allNoticiationsCountLastMonth, readNoticationsCountBeforeLastMonth, allNoticationsCountBeforeLastMonth,]) => {
            if (allNoticationsCountBeforeLastMonth == 0 ||
                allNoticiationsCountLastMonth ||
                0)
                return 0;
            return roundToDecimals((readNoticationsCountBeforeLastMonth /
                allNoticationsCountBeforeLastMonth -
                readNoticationsCountLastMonth / allNoticiationsCountLastMonth) *
                100, 1);
        });
    }
}
exports.default = new NotificationService();