"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cancelAppointments = void 0;
const lodash_1 = require("lodash");
const extensions_core_1 = require("@awell-health/extensions-core");
const date_fns_1 = require("date-fns");
const addEventLog_1 = require("../../../../src/lib/awell/addEventLog");
const client_1 = require("../../client");
const createOpenAIModel_1 = require("../../../../src/lib/llm/openai/createOpenAIModel");
const constants_1 = require("../../../../src/lib/llm/openai/constants");
const config_1 = require("./config");
const findAppointmentsWithLLM_1 = require("../../lib/findAppointmentsWithLLM/findAppointmentsWithLLM");
const utils_1 = require("../../../../src/utils");
const extractDatesFromInstructions_1 = require("../../lib/extractDatesFromInstructions/extractDatesFromInstructions");
exports.cancelAppointments = {
    key: 'cancelAppointments',
    category: extensions_core_1.Category.EHR_INTEGRATIONS,
    title: '✨ Cancel Appointments',
    description: 'Cancel appointments for a patient using natural language.',
    fields: config_1.fields,
    previewable: false,
    dataPoints: config_1.dataPoints,
    onEvent: async ({ payload, onComplete, onError, helpers }) => {
        const { prompt, patientId } = config_1.FieldsValidationSchema.parse(payload.fields);
        const api = (0, client_1.makeAPIClient)(payload.settings);
        /**
         * Fetch all upcoming appointments for the patient.
         * We're only interested in the upcoming appointments, so we set the from_date to the current date.
         */
        const appointments = await api.findAppointments({
            patient: patientId,
            from_date: new Date().toISOString(),
        });
        // Early return if no appointments found
        if ((0, lodash_1.isNil)(appointments) || appointments.length === 0) {
            const message = `No upcoming appointments found so none to cancel.`;
            await onComplete({
                data_points: {
                    explanation: message,
                    cancelledAppointments: JSON.stringify([]),
                },
                events: [
                    (0, addEventLog_1.addActivityEventLog)({
                        message,
                    }),
                ],
            });
            return;
        }
        try {
            // Initialize OpenAI model for natural language processing
            const { model, metadata, callbacks } = await (0, createOpenAIModel_1.createOpenAIModel)({
                settings: {}, // we use built-in API key for OpenAI
                helpers,
                payload,
                modelType: constants_1.OPENAI_MODELS.GPT4o,
            });
            // Extract date information from the prompt
            const { from, to, instructions } = await (0, extractDatesFromInstructions_1.extractDatesFromInstructions)({
                model,
                prompt,
                metadata,
                callbacks,
            });
            // Use LLM to find appointments matching the user's natural language prompt
            const { appointmentIds, explanation } = await (0, findAppointmentsWithLLM_1.findAppointmentsWithLLM)({
                model,
                appointments,
                prompt: (0, lodash_1.defaultTo)(instructions, prompt),
                metadata,
                callbacks,
            });
            const htmlExplanation = await (0, utils_1.markdownToHtml)(explanation);
            // Filter appointments based on both LLM selection and date range
            const appointmentsToCancel = appointments.filter((appointment) => appointmentIds.includes(appointment.id) &&
                ((0, lodash_1.isNil)(from) ||
                    (0, date_fns_1.isAfter)((0, date_fns_1.parseISO)(appointment.scheduled_date), (0, date_fns_1.parseISO)(from))) &&
                ((0, lodash_1.isNil)(to) ||
                    (0, date_fns_1.isBefore)((0, date_fns_1.parseISO)(appointment.scheduled_date), (0, date_fns_1.parseISO)(to))));
            const appointmentIdsToCancel = appointmentsToCancel.map((appointment) => appointment.id);
            // If no appointments match the criteria after filtering, return early
            if (appointmentIdsToCancel.length === 0) {
                await onComplete({
                    data_points: {
                        cancelledAppointments: JSON.stringify([]),
                        explanation: (0, lodash_1.isNil)(htmlExplanation) ? 'No matching appointments found to cancel.' : htmlExplanation,
                    }
                });
                return;
            }
            // Cancel the filtered appointments
            const trace = await Promise.all(appointmentIdsToCancel.map(async (appointmentId) => {
                var _a, _b;
                try {
                    // Find the original appointment to get all its fields
                    const appointmentToCancel = appointmentsToCancel.find((appointment) => appointment.id === appointmentId);
                    if ((0, lodash_1.isNil)(appointmentToCancel)) {
                        throw new Error(`Appointment ${appointmentId} not found`);
                    }
                    // Elation API requires all these fields in the update request, as their API doesn't support partial updates.
                    // We need to include the original values for fields we don't want to change (known limitation in their API).
                    // The API only allows status_detail for 'Not Seen' status, not for 'Cancelled'.
                    const updateData = {
                        status: {
                            status: 'Cancelled',
                            status_date: new Date().toISOString(),
                            room: (_a = appointmentToCancel.status) === null || _a === void 0 ? void 0 : _a.room // Keep the original room value if any
                        },
                        service_location: (_b = appointmentToCancel.service_location) === null || _b === void 0 ? void 0 : _b.id,
                        patient: appointmentToCancel.patient,
                        physician: appointmentToCancel.physician,
                        practice: appointmentToCancel.practice,
                        scheduled_date: appointmentToCancel.scheduled_date,
                        reason: appointmentToCancel.reason,
                        duration: appointmentToCancel.duration,
                    };
                    await api.updateAppointment(appointmentId, updateData);
                    return { appointmentId, status: 'success' };
                }
                catch (error) {
                    console.error(`Error canceling appointment ${appointmentId}:`, error);
                    return { appointmentId, status: 'error' };
                }
            }));
            const failedCancellations = trace.filter((t) => t.status === 'error');
            if (failedCancellations.length > 0) {
                await onError({
                    events: [
                        (0, addEventLog_1.addActivityEventLog)({
                            message: `Successfully cancelled the following appointments: ${trace
                                .filter((t) => t.status === 'success')
                                .map((t) => t.appointmentId)
                                .join(', ')}`,
                        }),
                        (0, addEventLog_1.addActivityEventLog)({
                            message: `Failed to cancel the following appointments: ${failedCancellations
                                .map((t) => t.appointmentId)
                                .join(', ')}`,
                        }),
                    ],
                });
                return;
            }
            await onComplete({
                data_points: {
                    cancelledAppointments: JSON.stringify(appointmentsToCancel),
                    explanation: htmlExplanation,
                },
                events: [
                    (0, addEventLog_1.addActivityEventLog)({
                        message: `${appointmentsToCancel.length} appointments for patient ${patientId} were cancelled: ${appointmentIdsToCancel.join(', ')}`,
                    }),
                ],
            });
        }
        catch (error) {
            await onError({
                events: [
                    {
                        date: new Date().toISOString(),
                        text: { en: 'Failed to find and cancel appointments' },
                        error: {
                            category: 'SERVER_ERROR',
                            message: error instanceof Error ? error.message : 'Unknown error',
                        },
                    },
                ],
            });
        }
    },
};
//# sourceMappingURL=cancelAppointments.js.map