export interface ActivationAnswer { childPatientId: string; claimId?: string; standing: 'unknown' | 'checking' | 'associated' | 'refused' | 'not-submitted' | 'producer-settled'; account: 'unknown' | 'not-created' | 'created-observed' | 'associated-proved'; setup: 'outstanding' | 'producer-settled'; canStartNew: boolean; caseReference?: string; } export interface ActivationReference { version: 1; childPatientId: string; claimId?: string } const object = (value: unknown): value is Record => Boolean(value && typeof value === 'object' && !Array.isArray(value)); const uuid = /^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/i; export const unknownActivation = (childPatientId: string, claimId?: string): ActivationAnswer => ({ childPatientId, ...(claimId && uuid.test(claimId) ? { claimId } : {}), standing: 'unknown', account: 'unknown', setup: 'outstanding', canStartNew: false }); export function activationAnswerOf(body: unknown, status: number, childPatientId: string, claimId?: string): ActivationAnswer { const unknown = () => unknownActivation(childPatientId, claimId); if (!object(body) || !object(body.activation) || ![200, 202].includes(status)) return unknown(); const value = body.activation; if (value.childPatientId !== childPatientId || typeof value.canStartNew !== 'boolean' || !['unknown', 'checking', 'associated', 'refused', 'not-submitted', 'producer-settled'].includes(String(value.standing)) || !['unknown', 'not-created', 'created-observed', 'associated-proved'].includes(String(value.account)) || !['outstanding', 'producer-settled'].includes(String(value.setup)) || (value.claimId !== undefined && (typeof value.claimId !== 'string' || !uuid.test(value.claimId))) || (claimId !== undefined && value.claimId !== claimId) || (value.caseReference !== undefined && (typeof value.caseReference !== 'string' || !/^activation-(?:held-)?[a-zA-Z0-9-]{1,80}$/.test(value.caseReference)))) return unknown(); const known = value.account === 'created-observed' || value.account === 'associated-proved'; if (known && !value.claimId) return unknown(); if (['associated', 'producer-settled'].includes(String(value.standing)) && (!known || !value.claimId || value.canStartNew)) return unknown(); if (value.standing === 'producer-settled' ? value.setup !== 'producer-settled' || status !== 200 : value.setup !== 'outstanding') return unknown(); if (['unknown', 'checking', 'associated'].includes(String(value.standing)) && status !== 202) return unknown(); if (['refused', 'not-submitted'].includes(String(value.standing)) && (value.account !== 'not-created' || status !== 200)) return unknown(); if (value.standing === 'refused' && !value.claimId) return unknown(); if (value.standing === 'checking' && (value.account !== 'unknown' || !value.claimId || value.canStartNew)) return unknown(); if (value.standing === 'unknown' && (value.canStartNew || value.account === 'not-created')) return unknown(); return { childPatientId, standing: value.standing as ActivationAnswer['standing'], account: value.account as ActivationAnswer['account'], setup: value.setup as ActivationAnswer['setup'], canStartNew: value.canStartNew, ...(typeof value.claimId === 'string' ? { claimId: value.claimId } : {}), ...(typeof value.caseReference === 'string' ? { caseReference: value.caseReference } : {}) }; } export function readActivationReference(storage: Pick, key: string, childPatientId: string): { kind: 'none' | 'unavailable' } | { kind: 'kept'; reference: ActivationReference } { try { const raw = storage.getItem(key); if (raw === null) return { kind: 'none' }; const value: unknown = JSON.parse(raw); if (!object(value) || value.version !== 1 || value.childPatientId !== childPatientId || Object.keys(value).some(name => !['version', 'childPatientId', 'claimId'].includes(name)) || (value.claimId !== undefined && (typeof value.claimId !== 'string' || !uuid.test(value.claimId)))) return { kind: 'unavailable' }; return { kind: 'kept', reference: { version: 1, childPatientId, ...(typeof value.claimId === 'string' ? { claimId: value.claimId } : {}) } }; } catch { return { kind: 'unavailable' }; } } export function keepActivationReference(storage: Pick, key: string, reference: ActivationReference): void { if (!/^[A-Za-z0-9.-]{1,64}$/.test(reference.childPatientId) || (reference.claimId && !uuid.test(reference.claimId))) throw new Error('Activation reference unavailable'); storage.setItem(key, JSON.stringify({ version: 1, childPatientId: reference.childPatientId, ...(reference.claimId ? { claimId: reference.claimId } : {}) })); } export const activationNotice = (answer: ActivationAnswer): string => { if (answer.standing === 'producer-settled') return 'Login setup is saved. The sign-in notice was queued; delivery is not confirmed.'; if (answer.standing === 'unknown' && (answer.account === 'created-observed' || answer.account === 'associated-proved')) { return answer.account === 'created-observed' ? 'Creation of the original login was acknowledged. Its current setup could not be confirmed. Check status before taking another action.' : 'An existing login was confirmed on this record. Its current setup could not be confirmed. Check status before taking another action.'; } if (answer.account === 'created-observed') return 'The login was created. Some setup or notices are still being saved. Check status to continue this activation.'; if (answer.account === 'associated-proved') return 'The login is linked to this record. Some setup or notices are still being saved. Check status to continue this activation.'; if (answer.standing === 'refused') return answer.canStartNew ? 'The login was not created. You can start a new activation.' : 'The login was refused. Its record is still being saved. Check status before starting another activation.'; if (answer.standing === 'not-submitted') return answer.canStartNew ? 'No activation was submitted. You can start a new activation.' : 'This activation did not send a create request. Check status while its record is saved.'; return 'We could not confirm the login yet. Check status using the original email; this continues the same activation.'; };