"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.enrollment = exports.METRIPORT_PATIENT_IDENTIFIER_SYSTEM = void 0;
const lodash_1 = require("lodash");
const zod_1 = require("zod");
const zod_validation_error_1 = require("zod-validation-error");
const verifyWebhookSignature_1 = require("../shared/verifyWebhookSignature");
const types_1 = require("./types");
const validation_zod_1 = require("./validation.zod");
/**
 * The identifier system used to enroll a patient based on their Metriport
 * patient ID.
 */
exports.METRIPORT_PATIENT_IDENTIFIER_SYSTEM = 'https://metriport.com/fhir/patient';
const dataPoints = {
    eventType: {
        key: 'eventType',
        valueType: 'string',
    },
    metriportPatientId: {
        key: 'metriportPatientId',
        valueType: 'string',
    },
    externalId: {
        key: 'externalId',
        valueType: 'string',
    },
    admitTimestamp: {
        key: 'admitTimestamp',
        valueType: 'date',
    },
    whenSourceSent: {
        key: 'whenSourceSent',
        valueType: 'date',
    },
    messageId: {
        key: 'messageId',
        valueType: 'string',
    },
    bundleUrl: {
        key: 'bundleUrl',
        valueType: 'string',
    },
};
const isAdmit = (webhook) => webhook.meta.type === types_1.MetriportWebhookType.PatientAdmit;
const isDischargeSummary = (webhook) => webhook.meta.type === types_1.MetriportWebhookType.DischargeSummary;
exports.enrollment = {
    key: 'enrollment',
    description: 'Enrolls a patient when Metriport sends a real-time notification. The `eventType` data point carries the Metriport webhook type (`patient.admit` or `medical.discharge-summary`) so it can be distinguished on. The FHIR bundle is not fetched here — the pre-signed URL is passed on the `bundleUrl` data point and can be retrieved later with the "Get Webhook Bundle" action.',
    dataPoints,
    onEvent: async ({ payload: { payload, rawBody, headers, settings }, onSuccess, onError, }) => {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
        try {
            // Verify the Metriport webhook signature (HMAC-SHA256 over the raw body)
            // when a webhook key is configured. See `verifyWebhookSignature.ts`.
            if (!(0, verifyWebhookSignature_1.isWebhookRequestAuthorized)({
                webhookKey: settings.webhookKey,
                rawBody,
                headers,
            })) {
                await onError({
                    response: {
                        statusCode: 401,
                        message: 'Invalid or missing x-metriport-signature header',
                    },
                });
                return;
            }
            const webhook = validation_zod_1.webhookPayloadSchema.parse(payload);
            // Acknowledge Metriport's verification ping without enrolling anyone.
            // Metriport expects the response to echo back the ping value as `pong`.
            // https://docs.metriport.com/medical-api/getting-started/webhooks#the-ping-message
            if ('ping' in webhook) {
                await onError({
                    response: {
                        statusCode: 200,
                        message: `pong: ${webhook.ping}`,
                    },
                });
                return;
            }
            // We enroll on admit (adt) and discharge-summary events only. Every other
            // notification type (patient.discharge, patient.transfer, ...) is
            // acknowledged with a 200 but does not enroll a patient.
            //
            // The bundle referenced by `bundleUrl` is intentionally NOT fetched here:
            // we validate, emit the data points (including the URL), and reply
            // immediately. The bundle is fetched later via the "Get Webhook Bundle"
            // action.
            let data_points;
            let patientId;
            if (isAdmit(webhook)) {
                const event = webhook.payload;
                patientId = event.patientId;
                data_points = {
                    eventType: webhook.meta.type,
                    metriportPatientId: event.patientId,
                    externalId: (_a = event.externalId) !== null && _a !== void 0 ? _a : '',
                    admitTimestamp: (_b = event.admitTimestamp) !== null && _b !== void 0 ? _b : '',
                    whenSourceSent: (_c = event.whenSourceSent) !== null && _c !== void 0 ? _c : '',
                    messageId: webhook.meta.messageId,
                    bundleUrl: (_d = event.url) !== null && _d !== void 0 ? _d : '',
                };
            }
            else if (isDischargeSummary(webhook)) {
                // TBD: the `medical.discharge-summary` payload shape is not yet
                // documented by Metriport — we're still discussing it with them. The
                // handling below (patient entry in a `patients` array, bundle URL) is a
                // best guess modelled on the published `medical.*` webhook family and
                // should be revisited once the event is confirmed.
                const patient = (_e = webhook.patients) === null || _e === void 0 ? void 0 : _e[0];
                patientId = (_f = patient === null || patient === void 0 ? void 0 : patient.patientId) !== null && _f !== void 0 ? _f : (_g = webhook.payload) === null || _g === void 0 ? void 0 : _g.patientId;
                data_points = {
                    eventType: webhook.meta.type,
                    metriportPatientId: patientId !== null && patientId !== void 0 ? patientId : '',
                    externalId: (_k = (_h = patient === null || patient === void 0 ? void 0 : patient.externalId) !== null && _h !== void 0 ? _h : (_j = webhook.payload) === null || _j === void 0 ? void 0 : _j.externalId) !== null && _k !== void 0 ? _k : '',
                    admitTimestamp: '',
                    whenSourceSent: '',
                    messageId: webhook.meta.messageId,
                    bundleUrl: (_o = (_l = patient === null || patient === void 0 ? void 0 : patient.url) !== null && _l !== void 0 ? _l : (_m = webhook.payload) === null || _m === void 0 ? void 0 : _m.url) !== null && _o !== void 0 ? _o : '',
                };
            }
            else {
                await onError({
                    response: {
                        statusCode: 200,
                        message: `Ignoring unhandled event type: ${webhook.meta.type}`,
                    },
                });
                return;
            }
            if ((0, lodash_1.isNil)(patientId) || patientId.length === 0) {
                await onError({
                    response: {
                        statusCode: 400,
                        message: 'Unable to determine the Metriport patient ID from the payload',
                    },
                });
                return;
            }
            await onSuccess({
                data_points,
                patient_identifier: {
                    system: exports.METRIPORT_PATIENT_IDENTIFIER_SYSTEM,
                    value: patientId,
                },
            });
        }
        catch (error) {
            const message = error instanceof zod_1.ZodError
                ? (0, zod_validation_error_1.fromZodError)(error).message
                : error.message;
            await onError({
                events: [
                    {
                        date: new Date().toISOString(),
                        text: { en: message },
                        error: {
                            category: error instanceof zod_1.ZodError ? 'WRONG_INPUT' : 'SERVER_ERROR',
                            message,
                        },
                    },
                ],
            });
        }
    },
};
//# sourceMappingURL=enrollment.js.map