import { DomainConflictError } from '../../../common/errors/domain-conflict.error';
type NotificationInput = Record<string, any>;
function object(input: unknown, code: string): asserts input is NotificationInput {
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new DomainConflictError(code);
}
function text(value: unknown, max: number, code: string): asserts value is string {
if (typeof value !== 'string' || value.trim().length === 0 || value.length > max) throw new DomainConflictError(code);
}
function optionalText(value: unknown, max: number, code: string): void {
if (value !== undefined && value !== null) text(value, max, code);
}
function scope(input: unknown, code: string): asserts input is NotificationInput {
object(input, code);
text(input.organizationId, 200, code);
text(input.userId, 200, code);
}
function record(value: unknown, code: string, maxKeys = 50, maxBytes = 32_000): void {
if (value === undefined || value === null) return;
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).length > maxKeys) throw new DomainConflictError(code);
try {
const serialized = JSON.stringify(value);
if (typeof serialized !== 'string' || serialized.length > maxBytes) throw new DomainConflictError(code);
} catch {
throw new DomainConflictError(code);
}
}
function optionalBoolean(value: unknown, code: string): void {
if (value !== undefined && value !== null && typeof value !== 'boolean') throw new DomainConflictError(code);
}
export function assertNotificationCreateInput(input: unknown): asserts input is NotificationInput {
const code = 'notifications.input_invalid';
scope(input, code);
optionalText(input.locationId, 200, code);
text(input.notificationType, 160, code);
text(input.titleKey, 250, code);
text(input.bodyKey, 250, code);
optionalText(input.throttleKey, 250, code);
optionalText(input.relatedEntityType, 100, code);
optionalText(input.relatedEntityId, 200, code);
optionalText(input.deduplicationKey, 500, code);
if (input.severity !== undefined && !['info','success','warning','error','critical','urgent'].includes(String(input.severity))) throw new DomainConflictError(code);
if (input.throttleMinutes !== undefined && (!Number.isSafeInteger(input.throttleMinutes) || input.throttleMinutes < 0 || input.throttleMinutes > 10080)) throw new DomainConflictError(code);
record(input.params, code);
}
export function assertNotificationPreferenceInput(input: unknown): asserts input is NotificationInput {
const code = 'notifications.input_invalid';
scope(input, code);
text(input.notificationType, 160, code);
optionalBoolean(input.inAppEnabled, code);
optionalBoolean(input.emailEnabled, code);
optionalBoolean(input.pushEnabled, code);
if (input.quietHoursPolicy !== undefined && input.quietHoursPolicy !== null) {
const policy = input.quietHoursPolicy;
if (!policy || typeof policy !== 'object' || Array.isArray(policy) || typeof policy.enabled !== 'boolean' ||
typeof policy.start !== 'string' || !/^\d{2}:\d{2}$/.test(policy.start) ||
typeof policy.end !== 'string' || !/^\d{2}:\d{2}$/.test(policy.end)) throw new DomainConflictError(code);
}
}
