import { Injectable, Logger, NotFoundException } from '@nestjs/common'; import { EntityManager, FilterQuery } from '@mikro-orm/postgresql'; import { Notification } from '../entities/notification.entity'; import { NotificationPreferenceService } from './notification-preference.service'; import { NotificationQueueService } from './notification-queue.service'; import { NotificationsGateway } from '../notifications.gateway'; import { NotifChannelEnum, NotifRequest, NotifTitleEnum } from '../interfaces/notification.interface'; import { SmsLog } from '../entities/smsLogs.entity'; import { SmsLogRepository } from '../repositories/sms-log.repository'; import { PaginatedResult } from '../../../common/interfaces/pagination.interface'; @Injectable() export class NotificationService { private readonly logger = new Logger(NotificationService.name); constructor( private readonly em: EntityManager, private readonly preferenceService: NotificationPreferenceService, private readonly queueService: NotificationQueueService, private readonly notificationGateway: NotificationsGateway, private readonly smsLogRepository: SmsLogRepository, ) { } // async sendNotification(params: NotifRequest): Promise { // const { recipients, message, metadata, } = params; // // create Database notifications // const notifications = await this.createAdminBulkNotifications( // recipients.map(recipient => ({ // title: message.title, // content: message.content, // adminId: 'adminId' in recipient ? recipient.adminId : null, // userId: 'userId' in recipient ? recipient.userId : null, // })), // ); // // get admin prefrences // const preference = await this.preferenceService.findByRestaurantAndType(, message.title); // if(preference?.channels?.length === 0) { // this.logger.warn(`Notification type is NONE for restaurant ${}, title ${message.title}`); // return notifications; // } // // send in app notification // if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) { // await this.queueService.addBulkInAppNotifications( // notifications.map(notification => ({ // recipient: { adminId: notification.admin?.id || '', }, // subject: message.title, // body: message.content, // notificationId: notification.id, // })), // ); // } // // add sms notifications to queue // if (preference?.channels?.includes(NotifChannelEnum.SMS)) { // await this.queueService.addBulkSmsNotifications( // recipients.map(recipient => ({ // recipient, // templateId: message.sms.templateId, // parameters: message.sms.parameters, // })), // ); // } // this.logger.log(`Queued notification for restaurant ${}, title ${message.title}`); // return notifications; // } // async createAdminBulkNotifications( // params: { // title: NotifTitleEnum; // content: string; // adminId: string | null; // userId: string | null; // }[], // ): Promise < Notification[] > { // const notifications = params.map(param => { // return this.em.create(Notification, { // admin: param.adminId, // user: param.userId && param.userId.trim() !== '' ? param.userId : null, // title: param.title, // content: param.content, // }); // }); // await this.em.persistAndFlush(notifications); // return notifications; // } // async findOne(id: string): Promise < Notification > { // const notification = await this.em.findOne(Notification, { id }, { populate: ['restaurant', 'user'] }); // if(!notification) { // throw new NotFoundException('Notification not found'); // } // return notification; // } // async findByRestaurant( // : string, // adminId: string, // limit = 50, // cursor ?: string, // status ?: 'seen' | 'unseen', // ): Promise < { data: Notification[]; nextCursor: string | null } > { // const where: FilterQuery = { // restaurant: { id: }, // admin: { id: adminId }, // }; // // Filter by status (seen/unseen) // if (status === 'seen') { // where.seenAt = { $ne: null }; // } else if (status === 'unseen') { // where.seenAt = null; // } // // Cursor-based pagination: if cursor is provided, get items with id < cursor // if (cursor) { // where.id = { $lt: cursor }; // } // const notifications = await this.em.find(Notification, where, { // orderBy: { createdAt: 'DESC', id: 'DESC' }, // limit: limit + 1, // fetch one extra to determine next page // populate: ['user'], // }); // const hasNextPage = notifications.length > limit; // const data = hasNextPage ? notifications.slice(0, limit) : notifications; // const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null; // return { // data, // nextCursor: nextCursor ?? null, // }; // } // async findByUserAndRestaurant( // userId: string, // : string, // limit = 50, // cursor ?: string, // status ?: 'seen' | 'unseen', // ): Promise < { data: Notification[]; nextCursor: string | null } > { // const where: FilterQuery = { // user: { id: userId }, // restaurant: { id: }, // }; // // Filter by status (seen/unseen) // if (status === 'seen') { // where.seenAt = { $ne: null }; // } else if (status === 'unseen') { // where.seenAt = null; // } // // Cursor-based pagination: if cursor is provided, get items with id < cursor (since ULIDs are time-ordered) // if (cursor) { // where.id = { $lt: cursor }; // } // const notifications = await this.em.find(Notification, where, { // orderBy: { createdAt: 'DESC', id: 'DESC' }, // limit: limit + 1, // Fetch one extra to determine if there's a next page // }); // // Check if there's a next page // const hasNextPage = notifications.length > limit; // const data = hasNextPage ? notifications.slice(0, limit) : notifications; // const nextCursor = hasNextPage && data.length > 0 ? data[data.length - 1].id : null; // return { // data, // nextCursor: nextCursor ?? null, // }; // } // async readNotificationAdmin(id: string, adminId: string, : string): Promise < void> { // const notification = await this.em.findOne(Notification, { // id, // admin: { id: adminId }, // restaurant: { id: }, // }); // if(!notification) { // throw new NotFoundException('Notification not found'); // } // notification.seenAt = new Date(); // await this.em.persistAndFlush(notification); // } // async readNotificationAsUser(id: string, userId: string, : string): Promise < void> { // const notification = await this.em.findOne(Notification, { // id, // user: { id: userId }, // restaurant: { id: }, // }); // if(!notification) { // throw new NotFoundException('Notification not found'); // } // notification.seenAt = new Date(); // await this.em.persistAndFlush(notification); // } // async findByRestaurantAndType(: string, title: NotifTitleEnum, limit = 50): Promise < Notification[] > { // return this.em.find( // Notification, // { // restaurant: { id: }, // title, // }, // { // orderBy: { createdAt: 'DESC' }, // limit, // populate: ['user'], // }, // ); // } // async findByAdminAndRestaurant(adminId: string, : string, limit = 50): Promise < Notification[] > { // return this.em.find( // Notification, // { admin: { id: adminId }, restaurant: { id: } }, // { // orderBy: { createdAt: 'DESC' }, // limit, // }, // ); // } // async countUnseenByUserAndRestaurant(userId: string, : string): Promise < number > { // const where: FilterQuery = { // user: { id: userId }, // restaurant: { id: }, // seenAt: null, // }; // return this.em.count(Notification, where); // } // async countUnseenByRestaurant(adminId: string, : string): Promise < number > { // const where: FilterQuery = { // admin: { id: adminId }, // restaurant: { id: }, // seenAt: null, // }; // return this.em.count(Notification, where); // } // readAllNotifsAsUser(userId: string, : string): Promise < number > { // const where: FilterQuery = { // user: { id: userId }, // restaurant: { id: }, // seenAt: null, // }; // return this.em.nativeUpdate(Notification, where, { seenAt: new Date() }); // } // readAllNotifsAsAdmin(adminId: string, : string): Promise < number > { // const where: FilterQuery = { // admin: { id: adminId }, // restaurant: { id: }, // seenAt: null, // }; // return this.em.nativeUpdate(Notification, where, { seenAt: new Date() }); // } // async getSmsCountByRestaurant( // page: number = 1, // limit: number = 10, // ): Promise < PaginatedResult < { : string; restaurantName: string; smsCount: number } >> { // return this.smsLogRepository.getSmsCountByRestaurant(page, limit); // } // async getSmsCountBy(: string): Promise < number > { // return this.smsLogRepository.getSmsCountBy(); // } }