import { getModelByName } from '../../../database/ObjectModels' const normalizeType = (type) => typeof type === 'string' ? `${type.charAt(0).toLowerCase()}${type.slice(1)}` : type export const getReferenceId = (value) => { if (typeof value === 'string') return value return value?._id?._id || value?._id || null } const addCandidate = (candidates, candidate) => { if (!candidate?.email) return const key = `${candidate.email.toLowerCase()}:${candidate.recipientType || ''}:${ getReferenceId(candidate.recipient) || '' }:${getReferenceId(candidate.object) || ''}` if (!candidates.some((entry) => entry.key === key)) { candidates.push({ ...candidate, key }) } } export const resolveEmailRecipients = async ({ type, objectData, fetchObject }) => { if (!type || !objectData) return [] const model = getModelByName(type, true) if (model?.name === 'unknown') return [] const candidates = [] const properties = Array.isArray(model.properties) ? model.properties : [] for (const property of properties) { const value = objectData[property.name] if (property.type === 'email' && typeof value === 'string' && value.trim()) { addCandidate(candidates, { email: value.trim(), label: `${property.label || property.name}: ${value.trim()}`, property: property.name, object: objectData }) continue } if (property.type !== 'object' || value == null) continue const resolvedType = normalizeType( typeof property.objectType === 'function' ? property.objectType(objectData) : property.objectType ) if (resolvedType !== 'client' && resolvedType !== 'vendor') continue let recipient = value const recipientId = getReferenceId(value) if ( recipientId && (typeof recipient !== 'object' || typeof recipient.email !== 'string') ) { recipient = await fetchObject(recipientId, resolvedType) } if (typeof recipient?.email === 'string' && recipient.email.trim()) { addCandidate(candidates, { email: recipient.email.trim(), label: `${property.label || property.name}: ${ recipient.name || recipient._reference || recipient.email }`, property: property.name, recipientType: resolvedType, recipient, object: objectData }) } } return candidates } const hydrateObject = async (item, type, fetchObject) => { if (item && typeof item === 'object' && !Array.isArray(item)) { return item } const id = getReferenceId(item) if (!id || !type || typeof fetchObject !== 'function') return null return fetchObject(id, type) } export const resolveEmailRecipientsForObjects = async ({ type, objects, fetchObject }) => { const list = Array.isArray(objects) ? objects.filter(Boolean) : [] const merged = [] for (const item of list) { const objectData = await hydrateObject(item, type, fetchObject) if (!objectData) continue const result = await resolveEmailRecipients({ type, objectData, fetchObject }) if (result.length) { for (const candidate of result) { addCandidate(merged, candidate) } } else { merged.push({ email: '', object: objectData, key: `:${getReferenceId(objectData) || ''}` }) } } return merged } export const candidatesToRecipients = (candidates = []) => { if (!Array.isArray(candidates) || !candidates.length) return [] return candidates.map((candidate) => ({ recipientEmail: candidate.email || '', recipientType: candidate.recipientType ?? null, recipient: candidate.recipient ?? null, object: candidate.object ?? null, read: false, sent: false })) } /** Prefer a client/vendor ref; otherwise use a plain email candidate. */ export const getDefaultEmailRecipient = (candidates = []) => { const recipients = candidatesToRecipients(candidates) if (!recipients.length) return null const objectRecipient = recipients.find( (recipient) => (recipient.recipientType === 'client' || recipient.recipientType === 'vendor') && recipient.recipient && recipient.recipientEmail ) return objectRecipient || recipients[0] }