import { prisma as db } from "@repo/db/client"; export type TwilioSettingsData = { accountSid: string; authToken: string; phoneNumber: string; greetingMessage?: string | null; twimlAppSid?: string | null; }; export type CommunicationCreateData = { patientId: number; userId?: number; channel: "sms" | "voice"; direction: "outbound" | "inbound"; status: "queued" | "sent" | "delivered" | "failed" | "completed" | "busy" | "no_answer"; body?: string; callDuration?: number; twilioSid?: string; }; export const twilioStorage = { async getTwilioSettings(userId: number) { return db.twilioSettings.findUnique({ where: { userId } }); }, async upsertTwilioSettings(userId: number, data: TwilioSettingsData) { const { twimlAppSid, ...rest } = data; const existing = await db.twilioSettings.findUnique({ where: { userId } }); const existingTemplates = (existing?.templates as Record) || {}; const templates: Record = { ...existingTemplates }; if (twimlAppSid !== undefined) { if (twimlAppSid) templates["_twiml_app_sid"] = twimlAppSid; else delete templates["_twiml_app_sid"]; } return db.twilioSettings.upsert({ where: { userId }, update: { ...rest, templates }, create: { userId, ...rest, templates }, }); }, async createCommunication(data: CommunicationCreateData) { return db.communication.create({ data: data as any }); }, async getCommunicationsByPatient(patientId: number) { return db.communication.findMany({ where: { patientId }, orderBy: { createdAt: "asc" }, }); }, async getTemplates(userId: number): Promise> { const settings = await db.twilioSettings.findUnique({ where: { userId } }); if (!settings?.templates) return {}; return settings.templates as Record; }, async saveTemplate(userId: number, key: string, body: string) { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const existing = (settings?.templates as Record) || {}; const updated = { ...existing, [key]: body }; return db.twilioSettings.upsert({ where: { userId }, update: { templates: updated }, create: { userId, accountSid: "", authToken: "", phoneNumber: "", templates: updated }, }); }, async getAiChatTemplates(userId: number): Promise> { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const all = (settings?.templates as Record) || {}; return { reminderGreeting: all["_ai_chat_reminder_greeting"] ?? "", newPatientGreeting: all["_ai_chat_new_patient_greeting"] ?? "", generalFallback: all["_ai_chat_general_fallback"] ?? "", rescheduleGreeting: all["_ai_chat_reschedule_greeting"] ?? "", reminderSms: all["_ai_chat_reminder_sms"] ?? "", }; }, async saveAiChatTemplates(userId: number, templates: { reminderGreeting?: string; newPatientGreeting?: string; generalFallback?: string; rescheduleGreeting?: string; reminderSms?: string }) { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const existing = (settings?.templates as Record) || {}; const updated: Record = { ...existing }; if (templates.reminderGreeting !== undefined) updated["_ai_chat_reminder_greeting"] = templates.reminderGreeting; if (templates.newPatientGreeting !== undefined) updated["_ai_chat_new_patient_greeting"] = templates.newPatientGreeting; if (templates.generalFallback !== undefined) updated["_ai_chat_general_fallback"] = templates.generalFallback; if (templates.rescheduleGreeting !== undefined) updated["_ai_chat_reschedule_greeting"] = templates.rescheduleGreeting; if (templates.reminderSms !== undefined) updated["_ai_chat_reminder_sms"] = templates.reminderSms; return db.twilioSettings.upsert({ where: { userId }, update: { templates: updated }, create: { userId, accountSid: "", authToken: "", phoneNumber: "", templates: updated }, }); }, // ── Call Templates — wording spoken during live AI phone calls, kept // separate from the SMS wording above (routePatientTurn() consumes // whichever set the caller passes in). ──────────────────────────────────── async getAiCallTemplates(userId: number): Promise> { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const all = (settings?.templates as Record) || {}; return { greeting: all["_ai_call_greeting"] ?? all["_ai_chat_call_template"] ?? "Hi {firstName}, this is Lisa, the AI assistant at {officeName}. How can I help you today?", reminderGreeting: all["_ai_call_reminder_greeting"] ?? "Sure, I can help with that.", newPatientGreeting: all["_ai_call_new_patient_greeting"] ?? "No problem, let's get you set up.", generalFallback: all["_ai_call_general_fallback"] ?? "Sure, how can I help?", recallGreeting: all["_ai_call_recall_greeting"] ?? "Hi {firstName}, this is Lisa from {officeName}. We wanted to check in — you're due for a routine exam and cleaning, which most insurance plans cover at no cost. Would you like to schedule one?", }; }, async saveAiCallTemplates(userId: number, templates: { greeting?: string; reminderGreeting?: string; newPatientGreeting?: string; generalFallback?: string; recallGreeting?: string }) { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const existing = (settings?.templates as Record) || {}; const updated: Record = { ...existing }; if (templates.greeting !== undefined) updated["_ai_call_greeting"] = templates.greeting; if (templates.reminderGreeting !== undefined) updated["_ai_call_reminder_greeting"] = templates.reminderGreeting; if (templates.newPatientGreeting !== undefined) updated["_ai_call_new_patient_greeting"] = templates.newPatientGreeting; if (templates.generalFallback !== undefined) updated["_ai_call_general_fallback"] = templates.generalFallback; if (templates.recallGreeting !== undefined) updated["_ai_call_recall_greeting"] = templates.recallGreeting; return db.twilioSettings.upsert({ where: { userId }, update: { templates: updated }, create: { userId, accountSid: "", authToken: "", phoneNumber: "", templates: updated }, }); }, async getSmsTemplateList(userId: number): Promise<{ id: string; name: string; body: string }[]> { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const all = (settings?.templates as Record) || {}; const raw = all["_sms_template_list"]; if (!raw) return []; try { return JSON.parse(raw); } catch { return []; } }, async saveSmsTemplateList(userId: number, templates: { id: string; name: string; body: string }[]) { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const existing = (settings?.templates as Record) || {}; const updated: Record = { ...existing, "_sms_template_list": JSON.stringify(templates), }; // Keep _ai_chat_reminder_sms in sync with the first template for batch sends if (templates.length > 0) updated["_ai_chat_reminder_sms"] = templates[0]!.body; return db.twilioSettings.upsert({ where: { userId }, update: { templates: updated }, create: { userId, accountSid: "", authToken: "", phoneNumber: "", templates: updated }, }); }, async getInternalChatSystemPrompt(userId: number): Promise { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const all = (settings?.templates as Record) || {}; return all["_internal_chat_system_prompt"] ?? ""; }, async saveInternalChatSystemPrompt(userId: number, prompt: string): Promise { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const existing = (settings?.templates as Record) || {}; const updated = { ...existing, "_internal_chat_system_prompt": prompt }; await db.twilioSettings.upsert({ where: { userId }, update: { templates: updated }, create: { userId, accountSid: "", authToken: "", phoneNumber: "", templates: updated }, }); }, async getCdtAliases(userId: number): Promise<{ phrase: string; cdtCode: string }[]> { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const all = (settings?.templates as Record) || {}; const raw = all["_cdt_aliases"]; if (!Array.isArray(raw)) return []; return raw.filter( (r: any) => typeof r?.phrase === "string" && typeof r?.cdtCode === "string" ); }, async saveCdtAliases(userId: number, aliases: { phrase: string; cdtCode: string }[]): Promise { const settings = await db.twilioSettings.findUnique({ where: { userId } }); const existing = (settings?.templates as Record) || {}; const updated = { ...existing, "_cdt_aliases": aliases }; await db.twilioSettings.upsert({ where: { userId }, update: { templates: updated }, create: { userId, accountSid: "", authToken: "", phoneNumber: "", templates: updated }, }); }, async getRecentCommunicationsByUser(userId: number, limit = 20) { return db.communication.findMany({ where: { patient: { userId } }, orderBy: { createdAt: "desc" }, take: limit, include: { patient: { select: { id: true, firstName: true, lastName: true, phone: true } }, }, }); }, };