import type { Patient } from 'fhir/r4'; // A desk registration is a server operation the client names. Its answer is a receipt only when it is for that // operation and carries its own Patient; anything else leaves the registration unresolved, to be checked, not redone. export const REGISTRATION_TAG_SYSTEM = 'https://akashichealthrecords.com/fhir/CodeSystem/registration-operation'; export type AccountBinding = 'bound' | 'bound-elsewhere' | 'held' | 'unconfirmed' | 'none'; export type InvitationStatus = 'sent' | 'held' | 'unconfirmed'; export interface RegistrationReceipt { registrationId: string; patient: Patient; binding: AccountBinding; invitation?: InvitationStatus; } export interface RegistrationListEntry { registrationId: string; startedAt: string; readable: boolean; displayName?: string; patientId?: string; binding?: string; invitation?: string; pending?: string[]; settled?: boolean; } export interface RegistrationList { receipts: RegistrationListEntry[]; cursor?: string; complete: boolean; } /** The answer did not prove this registration's outcome; it stays open for Check. */ export class RegistrationUnresolvedError extends Error { constructor(readonly registrationId: string, reason: string) { super(`We could not confirm this registration (${reason}). Check it before registering again.`); this.name = 'RegistrationUnresolvedError'; } } const BINDINGS: readonly string[] = ['bound', 'bound-elsewhere', 'held', 'unconfirmed', 'none']; const INVITATIONS: readonly string[] = ['sent', 'held', 'unconfirmed']; export const newRegistrationId = (): string => crypto.randomUUID(); export function readRegistrationReceipt( registrationId: string, data: unknown, header: (name: string) => string | null, ): RegistrationReceipt { if (header('X-Registration-Id') !== registrationId) throw new RegistrationUnresolvedError(registrationId, 'another operation answered'); const patient = data as Patient | null; if (patient?.resourceType !== 'Patient' || typeof patient.id !== 'string' || !patient.id) { throw new RegistrationUnresolvedError(registrationId, 'no patient in the answer'); } const tagged = (patient.meta?.tag ?? []).some((tag) => tag.system === REGISTRATION_TAG_SYSTEM && tag.code === registrationId); if (!tagged) throw new RegistrationUnresolvedError(registrationId, 'the patient is not this registration\'s'); const bindingHeader = header('X-Account-Binding'); const binding = (bindingHeader && BINDINGS.includes(bindingHeader) ? bindingHeader : 'unconfirmed') as AccountBinding; const invitationHeader = header('X-Invitation-Status'); // An invitation is only ever claimed for a login this registration attached. const invitation = binding === 'bound' ? ((invitationHeader && INVITATIONS.includes(invitationHeader) ? invitationHeader : 'unconfirmed') as InvitationStatus) : undefined; return { registrationId, patient, binding, ...(invitation ? { invitation } : {}) }; } export function readRegistrationList(data: unknown): RegistrationList { const answer = data as Partial | null; if (!answer || !Array.isArray(answer.receipts) || typeof answer.complete !== 'boolean') { throw new Error('The registrations list could not be read.'); } return { receipts: answer.receipts, ...(typeof answer.cursor === 'string' ? { cursor: answer.cursor } : {}), complete: answer.complete }; } /** The login and invitation lines, each only as strong as what the receipt proves. */ export function accountLines(receipt: Pick, email?: string): string[] { const lines: string[] = []; if (receipt.binding === 'bound') lines.push('Their login is attached.'); else if (receipt.binding === 'bound-elsewhere') lines.push('Their login could not be attached; the patient record was created.'); else if (receipt.binding !== 'none') lines.push('We could not confirm their login was attached.'); if (receipt.binding === 'bound') { if (receipt.invitation === 'sent') lines.push(email ? `A login invite was sent to ${email}.` : 'A login invite was sent.'); else if (receipt.invitation === 'held') lines.push('Their login invite will be sent once your practice\'s agreement is active.'); else lines.push('We could not confirm the login invite was sent.'); } return lines; } export const RECOVERED_REGISTRATION_COPY = 'The patient record exists. We could not confirm the original extra details or manager invitation. They have not been retried.';