import { Injectable, Logger, NotFoundException, ServiceUnavailableException, } from "@nestjs/common"; import { EmailNotificationStrategy } from "./strategies/notification.email.strategy"; import { NotificationStrategy } from "./strategies/notification.strategy"; import { SmsNotificationStrategy } from "./strategies/notification.sms.strategy"; type StrategyMethod = "sms" | "email" @Injectable() export class NotificationsService { private readonly logger = new Logger(NotificationsService.name); private readonly strategies: Map constructor(private readonly email: EmailNotificationStrategy, private readonly sms: SmsNotificationStrategy) { this.strategies = new Map([ ["sms", this.sms as NotificationStrategy], ["email", this.email as NotificationStrategy] ]) } /** * Dispatch a notification to an operator or customer. * TODO: wire to email/SMS provider (SendGrid, SMS API, etc.) via a mailer service. */ async directSend(method: StrategyMethod, recipient: string, message: string) { const strategy = this.strategies.get(method); if (!strategy) { throw new NotFoundException(); } // A strategy returning false (or throwing) is a real delivery failure — do // not swallow it. Surface it so callers observe the failure (existing // callers wrap directSend in try/catch for best-effort notifications). const sent = await strategy.send(recipient, message); if (!sent) { this.logger.error( `Notification via ${method} to ${recipient} failed to send`, ); throw new ServiceUnavailableException( `Failed to send ${method} notification`, ); } this.logger.log(`Notification via ${method} to ${recipient} sent`); } async notifyDriverVehicleAssignment(params: { driverPhone: string; driverName: string; vehiclePlateNumber: string; bookingReference: string; pickupAddress?: string | null; destinationYard?: string | null; }): Promise { const { driverPhone, driverName, vehiclePlateNumber, bookingReference, pickupAddress, destinationYard } = params; const message = `Dear ${driverName}, you have been assigned to a first-mile pickup. ` + `Booking: ${bookingReference}. Vehicle: ${vehiclePlateNumber}. ` + (pickupAddress ? `Pickup: ${pickupAddress}. ` : '') + (destinationYard ? `Destination: ${destinationYard}.` : ''); try { await this.directSend('sms', driverPhone, message); } catch (err) { this.logger.error(`Failed to notify driver ${driverName} (${driverPhone}): ${String(err)}`); } } }