import mongoose, { Document, Model, Schema, model } from "mongoose"; export type NotificationChannel = "whatsapp" | "email" | "system"; export type NotificationJobStatus = "pending" | "processing" | "sent" | "failed" | "cancelled"; export type NotificationJobType = "creation" | "reminder" | "update" | "cancellation"; export const DELETABLE_NOTIFICATION_JOB_STATUSES: NotificationJobStatus[] = ["sent", "failed", "cancelled"]; export interface NotificationJobPayload { email?: string; phoneNumber?: string; userId?: string; companyOwnerId?: string; subject?: string; message?: string; emailSubject?: string; emailMessage?: string; emailTemplateId?: string; emailContext?: Record; wapMessage?: string; systemSubject?: string; systemMessage?: string; } export interface INotificationJob { _id: string; companyId: string; appointmentId: string; clientId: string; channel: NotificationChannel; type: NotificationJobType; scheduledAt: Date; status: NotificationJobStatus; attempts: number; maxAttempts: number; lastError?: string; nextRetryAt?: Date; providerResponse?: string; payload?: NotificationJobPayload; createdAt: Date; updatedAt: Date; sentAt?: Date; } export interface INotificationJobDocument extends Omit, Document {} export class NotificationJobModel { schema: Schema; notificationJobList: Model; constructor() { this.schema = new Schema( { companyId: { type: Schema.Types.ObjectId, required: true, ref: "Companie", }, appointmentId: { type: Schema.Types.ObjectId, required: true, ref: "Appointment", }, clientId: { type: Schema.Types.ObjectId, required: true, ref: "Client", }, channel: { type: String, required: true, enum: ["whatsapp", "email", "system"], }, type: { type: String, required: true, enum: ["creation", "reminder", "update", "cancellation"], }, scheduledAt: { type: Date, required: true }, status: { type: String, required: true, enum: ["pending", "processing", "sent", "failed", "cancelled"], default: "pending", }, attempts: { type: Number, required: true, default: 0 }, maxAttempts: { type: Number, required: true, default: 3 }, lastError: { type: String, required: false }, nextRetryAt: { type: Date, required: false }, providerResponse: { type: String, required: false }, payload: { type: Schema.Types.Mixed, required: false }, sentAt: { type: Date, required: false }, }, { timestamps: true, } ); this.schema.index({ status: 1, scheduledAt: 1 }); this.schema.index({ status: 1, scheduledAt: 1, nextRetryAt: 1 }); this.schema.index({ companyId: 1, status: 1 }); this.schema.index({ appointmentId: 1 }); this.notificationJobList = model( "NotificationJob", this.schema ); } async deleteBeforeScheduledAt(cutoff: Date): Promise { const result = await this.notificationJobList.deleteMany({ scheduledAt: { $lt: cutoff }, status: { $in: DELETABLE_NOTIFICATION_JOB_STATUSES }, }).exec(); return result.deletedCount || 0; } }