/** * Application Use Case: MarkNotificationReadUseCase * * Marks a notification as read. */ import type { Logger, UseCaseOutputPort } from '@core/shared/application'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { INotificationRepository } from '../../domain/repositories/INotificationRepository'; // import { NotificationDomainError } from '../../domain/errors/NotificationDomainError'; export interface MarkNotificationReadCommand { notificationId: string; recipientId: string; // For validation } export interface MarkNotificationReadResult { notificationId: string; recipientId: string; wasAlreadyRead: boolean; } export type MarkNotificationReadErrorCode = | 'NOTIFICATION_NOT_FOUND' | 'RECIPIENT_MISMATCH' | 'REPOSITORY_ERROR'; export class MarkNotificationReadUseCase { constructor( private readonly notificationRepository: INotificationRepository, private readonly output: UseCaseOutputPort, private readonly logger: Logger, ) {} async execute( command: MarkNotificationReadCommand, ): Promise>> { this.logger.debug( `Attempting to mark notification ${command.notificationId} as read for recipient ${command.recipientId}`, ); try { const notification = await this.notificationRepository.findById(command.notificationId); if (!notification) { this.logger.warn(`Notification not found for ID: ${command.notificationId}`); return Result.err>({ code: 'NOTIFICATION_NOT_FOUND', details: { message: 'Notification not found' }, }); } if (notification.recipientId !== command.recipientId) { this.logger.warn( `Unauthorized attempt to mark notification ${command.notificationId}. Recipient ID mismatch.`, ); return Result.err>({ code: 'RECIPIENT_MISMATCH', details: { message: "Cannot mark another user's notification as read" }, }); } if (!notification.isUnread()) { this.logger.info( `Notification ${command.notificationId} is already read. Skipping update.`, ); this.output.present({ notificationId: command.notificationId, recipientId: command.recipientId, wasAlreadyRead: true, }); return Result.ok(undefined); } const updatedNotification = notification.markAsRead(); await this.notificationRepository.update(updatedNotification); this.logger.info( `Notification ${command.notificationId} successfully marked as read.`, ); this.output.present({ notificationId: command.notificationId, recipientId: command.recipientId, wasAlreadyRead: false, }); return Result.ok(undefined); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); this.logger.error( `Failed to mark notification ${command.notificationId} as read: ${err.message}`, ); return Result.err>({ code: 'REPOSITORY_ERROR', details: { message: err.message }, }); } } } /** * Application Use Case: MarkAllNotificationsReadUseCase * * Marks all notifications as read for a recipient. */ export interface MarkAllNotificationsReadInput { recipientId: string; } export interface MarkAllNotificationsReadResult { recipientId: string; } export type MarkAllNotificationsReadErrorCode = 'REPOSITORY_ERROR'; export class MarkAllNotificationsReadUseCase { constructor( private readonly notificationRepository: INotificationRepository, private readonly output: UseCaseOutputPort, ) {} async execute( input: MarkAllNotificationsReadInput, ): Promise>> { try { await this.notificationRepository.markAllAsReadByRecipientId(input.recipientId); this.output.present({ recipientId: input.recipientId }); return Result.ok(undefined); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); return Result.err>({ code: 'REPOSITORY_ERROR', details: { message: err.message }, }); } } } /** * Application Use Case: DismissNotificationUseCase * * Dismisses a notification. */ export interface DismissNotificationCommand { notificationId: string; recipientId: string; } export interface DismissNotificationResult { notificationId: string; recipientId: string; wasAlreadyDismissed: boolean; } export type DismissNotificationErrorCode = | 'NOTIFICATION_NOT_FOUND' | 'RECIPIENT_MISMATCH' | 'CANNOT_DISMISS_REQUIRING_RESPONSE' | 'REPOSITORY_ERROR'; export class DismissNotificationUseCase { constructor( private readonly notificationRepository: INotificationRepository, private readonly output: UseCaseOutputPort, ) {} async execute( command: DismissNotificationCommand, ): Promise>> { try { const notification = await this.notificationRepository.findById( command.notificationId, ); if (!notification) { return Result.err>({ code: 'NOTIFICATION_NOT_FOUND', details: { message: 'Notification not found' }, }); } if (notification.recipientId !== command.recipientId) { return Result.err>({ code: 'RECIPIENT_MISMATCH', details: { message: "Cannot dismiss another user's notification" }, }); } if (notification.isDismissed()) { this.output.present({ notificationId: command.notificationId, recipientId: command.recipientId, wasAlreadyDismissed: true, }); return Result.ok(undefined); } if (!notification.canDismiss()) { return Result.err>({ code: 'CANNOT_DISMISS_REQUIRING_RESPONSE', details: { message: 'Cannot dismiss notification that requires response' }, }); } const updatedNotification = notification.dismiss(); await this.notificationRepository.update(updatedNotification); this.output.present({ notificationId: command.notificationId, recipientId: command.recipientId, wasAlreadyDismissed: false, }); return Result.ok(undefined); } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); return Result.err>({ code: 'REPOSITORY_ERROR', details: { message: err.message }, }); } } }