import { prisma as db } from "@repo/db/client"; import { PdfCategory, PdfTitle } from "@repo/db/generated/prisma"; import { Appointment, Claim, ClaimWithServiceLines, DatabaseBackup, InsertAppointment, InsertClaim, InsertInsuranceCredential, InsertPatient, InsertPayment, InsertUser, InsuranceCredential, Notification, NotificationTypes, Patient, Payment, PaymentWithExtras, PdfFile, PdfGroup, Staff, UpdateAppointment, UpdateClaim, UpdatePatient, UpdatePayment, User, } from "@repo/db/types"; export interface IStorage { // User methods getUser(id: number): Promise; getUsers(limit: number, offset: number): Promise; getUserByUsername(username: string): Promise; createUser(user: InsertUser): Promise; updateUser(id: number, updates: Partial): Promise; deleteUser(id: number): Promise; // Patient methods getPatient(id: number): Promise; getPatientByInsuranceId(insuranceId: string): Promise; getPatientsByUserId(userId: number): Promise; getRecentPatients(limit: number, offset: number): Promise; getPatientsByIds(ids: number[]): Promise; createPatient(patient: InsertPatient): Promise; updatePatient(id: number, patient: UpdatePatient): Promise; deletePatient(id: number): Promise; searchPatients(args: { filters: any; limit: number; offset: number; }): Promise< { id: number; firstName: string | null; lastName: string | null; phone: string | null; gender: string | null; dateOfBirth: Date; insuranceId: string | null; insuranceProvider: string | null; status: string; }[] >; getTotalPatientCount(): Promise; countPatients(filters: any): Promise; // optional but useful // Appointment methods getAppointment(id: number): Promise; getAllAppointments(): Promise; getAppointmentsByUserId(userId: number): Promise; getAppointmentsByPatientId(patientId: number): Promise; getRecentAppointments(limit: number, offset: number): Promise; getAppointmentsOnRange(start: Date, end: Date): Promise; createAppointment(appointment: InsertAppointment): Promise; updateAppointment( id: number, appointment: UpdateAppointment ): Promise; deleteAppointment(id: number): Promise; getPatientAppointmentByDateTime( patientId: number, date: Date, startTime: string ): Promise; getStaffAppointmentByDateTime( staffId: number, date: Date, startTime: string, excludeId?: number ): Promise; getPatientConflictAppointment( patientId: number, date: Date, startTime: string, excludeId: number ): Promise; getStaffConflictAppointment( staffId: number, date: Date, startTime: string, excludeId: number ): Promise; // Staff methods getStaff(id: number): Promise; getAllStaff(): Promise; createStaff(staff: Staff): Promise; updateStaff(id: number, updates: Partial): Promise; deleteStaff(id: number): Promise; countAppointmentsByStaffId(staffId: number): Promise; countClaimsByStaffId(staffId: number): Promise; // Claim methods getClaim(id: number): Promise; getRecentClaimsByPatientId( patientId: number, limit: number, offset: number ): Promise; getTotalClaimCountByPatient(patientId: number): Promise; getClaimsByAppointmentId(appointmentId: number): Promise; getRecentClaims(limit: number, offset: number): Promise; getTotalClaimCount(): Promise; createClaim(claim: InsertClaim): Promise; updateClaim(id: number, updates: UpdateClaim): Promise; deleteClaim(id: number): Promise; // InsuranceCredential methods getInsuranceCredential(id: number): Promise; getInsuranceCredentialsByUser(userId: number): Promise; createInsuranceCredential( data: InsertInsuranceCredential ): Promise; updateInsuranceCredential( id: number, updates: Partial ): Promise; deleteInsuranceCredential(userId: number, id: number): Promise; getInsuranceCredentialByUserAndSiteKey( userId: number, siteKey: string ): Promise; // General PDF Methods createPdfFile( groupId: number, filename: string, pdfData: Buffer ): Promise; getPdfFileById(id: number): Promise; getPdfFilesByGroupId(groupId: number): Promise; getRecentPdfFiles(limit: number, offset: number): Promise; deletePdfFile(id: number): Promise; updatePdfFile( id: number, updates: Partial> ): Promise; // PDF Group management createPdfGroup( patientId: number, title: string, titleKey: PdfTitle, category: PdfCategory ): Promise; findPdfGroupByPatientTitleKeyAndCategory( patientId: number, titleKey: PdfTitle, category: PdfCategory ): Promise; getAllPdfGroups(): Promise; getPdfGroupById(id: number): Promise; getPdfGroupsByPatientId(patientId: number): Promise; updatePdfGroup( id: number, updates: Partial> ): Promise; deletePdfGroup(id: number): Promise; // Payment methods: getPayment(id: number): Promise; createPayment(data: InsertPayment): Promise; updatePayment(id: number, updates: UpdatePayment): Promise; deletePayment(id: number, userId: number): Promise; getPaymentById(id: number): Promise; getRecentPaymentsByPatientId( patientId: number, limit: number, offset: number ): Promise; getTotalPaymentCountByPatient(patientId: number): Promise; getPaymentsByClaimId(claimId: number): Promise; getRecentPayments( limit: number, offset: number ): Promise; getPaymentsByDateRange(from: Date, to: Date): Promise; getTotalPaymentCount(): Promise; // Database Backup methods createBackup(userId: number): Promise; getLastBackup(userId: number): Promise; getBackups(userId: number, limit?: number): Promise; deleteBackups(userId: number): Promise; // clears all for user // Notification methods createNotification( userId: number, type: NotificationTypes, message: string ): Promise; getNotifications( userId: number, limit?: number, offset?: number ): Promise; markNotificationRead( userId: number, notificationId: number ): Promise; markAllNotificationsRead(userId: number): Promise; deleteNotificationsByType( userId: number, type: NotificationTypes ): Promise; deleteAllNotifications(userId: number): Promise; } export const storage: IStorage = { // User methods async getUser(id: number): Promise { const user = await db.user.findUnique({ where: { id } }); return user ?? undefined; }, async getUsers(limit: number, offset: number): Promise { return await db.user.findMany({ skip: offset, take: limit }); }, async getUserByUsername(username: string): Promise { const user = await db.user.findUnique({ where: { username } }); return user ?? undefined; }, async createUser(user: InsertUser): Promise { return await db.user.create({ data: user as User }); }, async updateUser( id: number, updates: Partial ): Promise { try { return await db.user.update({ where: { id }, data: updates }); } catch { return undefined; } }, async deleteUser(id: number): Promise { try { await db.user.delete({ where: { id } }); return true; } catch { return false; } }, // Patient methods async getPatient(id: number): Promise { const patient = await db.patient.findUnique({ where: { id } }); return patient ?? undefined; }, async getPatientsByUserId(userId: number): Promise { return await db.patient.findMany({ where: { userId } }); }, async getPatientByInsuranceId(insuranceId: string): Promise { return db.patient.findFirst({ where: { insuranceId }, }); }, async getRecentPatients(limit: number, offset: number): Promise { return db.patient.findMany({ skip: offset, take: limit, orderBy: { createdAt: "desc" }, }); }, async getPatientsByIds(ids: number[]): Promise { if (!ids || ids.length === 0) return []; const uniqueIds = Array.from(new Set(ids)); return db.patient.findMany({ where: { id: { in: uniqueIds } }, select: { id: true, firstName: true, lastName: true, phone: true, email: true, dateOfBirth: true, gender: true, insuranceId: true, insuranceProvider: true, status: true, userId: true, createdAt: true, }, }); }, async createPatient(patient: InsertPatient): Promise { return await db.patient.create({ data: patient as Patient }); }, async updatePatient(id: number, updateData: UpdatePatient): Promise { try { return await db.patient.update({ where: { id }, data: updateData, }); } catch (err) { throw new Error(`Patient with ID ${id} not found`); } }, async deletePatient(id: number): Promise { try { await db.patient.delete({ where: { id } }); } catch (err) { console.error("Error deleting patient:", err); throw new Error(`Failed to delete patient: ${err}`); } }, async searchPatients({ filters, limit, offset, }: { filters: any; limit: number; offset: number; }) { return db.patient.findMany({ where: filters, orderBy: { createdAt: "desc" }, take: limit, skip: offset, select: { id: true, firstName: true, lastName: true, phone: true, gender: true, dateOfBirth: true, insuranceId: true, insuranceProvider: true, status: true, }, }); }, async getTotalPatientCount(): Promise { return db.patient.count(); }, async countPatients(filters: any) { return db.patient.count({ where: filters }); }, // Appointment methods async getAppointment(id: number): Promise { const appointment = await db.appointment.findUnique({ where: { id } }); return appointment ?? undefined; }, async getAllAppointments(): Promise { return await db.appointment.findMany(); }, async getAppointmentsByUserId(userId: number): Promise { return await db.appointment.findMany({ where: { userId } }); }, async getAppointmentsByPatientId(patientId: number): Promise { return await db.appointment.findMany({ where: { patientId } }); }, async getAppointmentsOnRange(start: Date, end: Date): Promise { return db.appointment.findMany({ where: { date: { gte: start, lte: end, }, }, orderBy: { date: "asc" }, }); }, async getRecentAppointments( limit: number, offset: number ): Promise { return db.appointment.findMany({ skip: offset, take: limit, orderBy: { date: "desc" }, }); }, async createAppointment( appointment: InsertAppointment ): Promise { return await db.appointment.create({ data: appointment as Appointment }); }, async updateAppointment( id: number, updateData: UpdateAppointment ): Promise { try { return await db.appointment.update({ where: { id }, data: updateData, }); } catch (err) { throw new Error(`Appointment with ID ${id} not found`); } }, async deleteAppointment(id: number): Promise { try { await db.appointment.delete({ where: { id } }); } catch (err) { throw new Error(`Appointment with ID ${id} not found`); } }, async getPatientAppointmentByDateTime( patientId: number, date: Date, startTime: string ): Promise { return ( (await db.appointment.findFirst({ where: { patientId, date, startTime, }, })) ?? undefined ); }, async getStaffAppointmentByDateTime( staffId: number, date: Date, startTime: string, excludeId?: number ): Promise { return ( (await db.appointment.findFirst({ where: { staffId, date, startTime, NOT: excludeId ? { id: excludeId } : undefined, }, })) ?? undefined ); }, async getPatientConflictAppointment( patientId: number, date: Date, startTime: string, excludeId: number ): Promise { return ( (await db.appointment.findFirst({ where: { patientId, date, startTime, NOT: { id: excludeId }, }, })) ?? undefined ); }, async getStaffConflictAppointment( staffId: number, date: Date, startTime: string, excludeId: number ): Promise { return ( (await db.appointment.findFirst({ where: { staffId, date, startTime, NOT: { id: excludeId }, }, })) ?? undefined ); }, // Staff methods async getStaff(id: number): Promise { const staff = await db.staff.findUnique({ where: { id } }); return staff ?? undefined; }, async getAllStaff(): Promise { const staff = await db.staff.findMany(); return staff; }, async createStaff(staff: Staff): Promise { const createdStaff = await db.staff.create({ data: staff, }); return createdStaff; }, async updateStaff( id: number, updates: Partial ): Promise { const updatedStaff = await db.staff.update({ where: { id }, data: updates, }); return updatedStaff ?? undefined; }, async deleteStaff(id: number): Promise { try { await db.staff.delete({ where: { id } }); return true; } catch (error) { console.error("Error deleting staff:", error); return false; } }, async countAppointmentsByStaffId(staffId: number): Promise { return await db.appointment.count({ where: { staffId } }); }, async countClaimsByStaffId(staffId: number): Promise { return await db.claim.count({ where: { staffId } }); }, // Claim methods implementation async getClaim(id: number): Promise { const claim = await db.claim.findUnique({ where: { id } }); return claim ?? undefined; }, async getRecentClaimsByPatientId( patientId: number, limit: number, offset: number ): Promise { return db.claim.findMany({ where: { patientId }, orderBy: { createdAt: "desc" }, skip: offset, take: limit, include: { serviceLines: true, staff: true, claimFiles: true, }, }); }, async getTotalClaimCountByPatient(patientId: number): Promise { return db.claim.count({ where: { patientId }, }); }, async getClaimsByAppointmentId(appointmentId: number): Promise { return await db.claim.findMany({ where: { appointmentId } }); }, async getRecentClaims( limit: number, offset: number ): Promise { return db.claim.findMany({ orderBy: { createdAt: "desc" }, skip: offset, take: limit, include: { serviceLines: true, staff: true, claimFiles: true }, }); }, async getTotalClaimCount(): Promise { return db.claim.count(); }, async createClaim(claim: InsertClaim): Promise { return await db.claim.create({ data: claim as Claim }); }, async updateClaim(id: number, updates: UpdateClaim): Promise { try { return await db.claim.update({ where: { id }, data: updates, }); } catch (err) { throw new Error(`Claim with ID ${id} not found`); } }, async deleteClaim(id: number): Promise { try { await db.claim.delete({ where: { id } }); } catch (err) { throw new Error(`Claim with ID ${id} not found`); } }, // Insurance Creds async getInsuranceCredential(id: number) { return await db.insuranceCredential.findUnique({ where: { id } }); }, async getInsuranceCredentialsByUser(userId: number) { return await db.insuranceCredential.findMany({ where: { userId } }); }, async createInsuranceCredential(data: InsertInsuranceCredential) { return await db.insuranceCredential.create({ data: data as InsuranceCredential, }); }, async updateInsuranceCredential( id: number, updates: Partial ) { return await db.insuranceCredential.update({ where: { id }, data: updates, }); }, async deleteInsuranceCredential(userId: number, id: number) { try { await db.insuranceCredential.delete({ where: { userId, id } }); return true; } catch { return false; } }, async getInsuranceCredentialByUserAndSiteKey( userId: number, siteKey: string ): Promise { return await db.insuranceCredential.findFirst({ where: { userId, siteKey }, }); }, // PDF Files async createPdfFile(groupId, filename, pdfData) { return db.pdfFile.create({ data: { groupId, filename, pdfData, }, }); }, async getAllPdfGroups(): Promise { return db.pdfGroup.findMany({ orderBy: { createdAt: "desc", }, }); }, async getPdfFileById(id) { return (await db.pdfFile.findUnique({ where: { id } })) ?? undefined; }, async getPdfFilesByGroupId(groupId) { return db.pdfFile.findMany({ where: { groupId }, orderBy: { uploadedAt: "desc" }, }); }, async getRecentPdfFiles(limit: number, offset: number): Promise { return db.pdfFile.findMany({ skip: offset, take: limit, orderBy: { uploadedAt: "desc" }, include: { group: true }, }); }, async updatePdfFile(id, updates) { try { return await db.pdfFile.update({ where: { id }, data: updates, }); } catch { return undefined; } }, async deletePdfFile(id) { try { await db.pdfFile.delete({ where: { id } }); return true; } catch { return false; } }, // ---------------------- // PdfGroup CRUD // ---------------------- async createPdfGroup(patientId, title, titleKey, category) { return db.pdfGroup.create({ data: { patientId, title, titleKey, category, }, }); }, async findPdfGroupByPatientTitleKeyAndCategory( patientId, titleKey, category ) { return ( (await db.pdfGroup.findFirst({ where: { patientId, titleKey, category, }, })) ?? undefined ); }, async getPdfGroupById(id) { return (await db.pdfGroup.findUnique({ where: { id } })) ?? undefined; }, async getPdfGroupsByPatientId(patientId) { return db.pdfGroup.findMany({ where: { patientId }, orderBy: { createdAt: "desc" }, }); }, async updatePdfGroup(id, updates) { try { return await db.pdfGroup.update({ where: { id }, data: updates, }); } catch { return undefined; } }, async deletePdfGroup(id) { try { await db.pdfGroup.delete({ where: { id } }); return true; } catch { return false; } }, // Payment Methods async getPayment(id: number): Promise { const payment = await db.payment.findUnique({ where: { id } }); return payment ?? undefined; }, async createPayment(payment: InsertPayment): Promise { return db.payment.create({ data: payment as Payment }); }, async updatePayment(id: number, updates: UpdatePayment): Promise { const existing = await db.payment.findFirst({ where: { id } }); if (!existing) { throw new Error("Payment not found"); } return db.payment.update({ where: { id }, data: updates, }); }, async deletePayment(id: number, userId: number): Promise { const existing = await db.payment.findFirst({ where: { id, userId } }); if (!existing) { throw new Error("Not authorized or payment not found"); } await db.payment.delete({ where: { id } }); }, async getRecentPaymentsByPatientId( patientId: number, limit: number, offset: number ): Promise { const payments = await db.payment.findMany({ where: { patientId }, orderBy: { createdAt: "desc" }, skip: offset, take: limit, include: { claim: { include: { serviceLines: true, }, }, serviceLines: true, serviceLineTransactions: { include: { serviceLine: true, }, }, updatedBy: true, patient: true, }, }); return payments.map((payment) => ({ ...payment, patientName: payment.claim?.patientName ?? "", paymentDate: payment.createdAt, paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER", })); }, async getTotalPaymentCountByPatient(patientId: number): Promise { return db.payment.count({ where: { patientId }, }); }, async getPaymentById(id: number): Promise { const payment = await db.payment.findFirst({ where: { id }, include: { claim: { include: { serviceLines: true, }, }, serviceLines: true, serviceLineTransactions: { include: { serviceLine: true, }, }, updatedBy: true, patient: true, }, }); if (!payment) return null; return { ...payment, patientName: payment.claim?.patientName ?? "", paymentDate: payment.createdAt, paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER", }; }, async getPaymentsByClaimId( claimId: number ): Promise { const payment = await db.payment.findFirst({ where: { claimId }, include: { claim: { include: { serviceLines: true, }, }, serviceLines: true, serviceLineTransactions: { include: { serviceLine: true, }, }, updatedBy: true, patient: true, }, }); if (!payment) return null; return { ...payment, patientName: payment.claim?.patientName ?? "", paymentDate: payment.createdAt, paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER", }; }, async getRecentPayments( limit: number, offset: number ): Promise { const payments = await db.payment.findMany({ orderBy: { createdAt: "desc" }, skip: offset, take: limit, include: { claim: { include: { serviceLines: true, }, }, serviceLines: true, serviceLineTransactions: { include: { serviceLine: true, }, }, updatedBy: true, patient: true, }, }); return payments.map((payment) => ({ ...payment, patientName: payment.claim?.patientName ?? "", paymentDate: payment.createdAt, paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER", })); }, async getPaymentsByDateRange( from: Date, to: Date ): Promise { const payments = await db.payment.findMany({ where: { createdAt: { gte: from, lte: to, }, }, orderBy: { createdAt: "desc" }, include: { claim: { include: { serviceLines: true, }, }, serviceLines: true, serviceLineTransactions: { include: { serviceLine: true, }, }, updatedBy: true, patient: true, }, }); return payments.map((payment) => ({ ...payment, patientName: payment.claim?.patientName ?? "", paymentDate: payment.createdAt, paymentMethod: payment.serviceLineTransactions[0]?.method ?? "OTHER", })); }, async getTotalPaymentCount(): Promise { return db.payment.count(); }, // ============================== // Database Backup methods // ============================== async createBackup(userId) { return await db.databaseBackup.create({ data: { userId } }); }, async getLastBackup(userId) { return await db.databaseBackup.findFirst({ where: { userId }, orderBy: { createdAt: "desc" }, }); }, async getBackups(userId, limit = 10) { return await db.databaseBackup.findMany({ where: { userId }, orderBy: { createdAt: "desc" }, take: limit, }); }, async deleteBackups(userId) { const result = await db.databaseBackup.deleteMany({ where: { userId } }); return result.count; }, // ============================== // Notification methods // ============================== async createNotification(userId, type, message) { return await db.notification.create({ data: { userId, type, message }, }); }, async getNotifications( userId: number, limit = 50, offset = 0 ): Promise { return await db.notification.findMany({ where: { userId }, orderBy: { createdAt: "desc" }, take: limit, skip: offset, }); }, async markNotificationRead(userId, notificationId) { const result = await db.notification.updateMany({ where: { id: notificationId, userId }, data: { read: true }, }); return result.count > 0; }, async markAllNotificationsRead(userId) { const result = await db.notification.updateMany({ where: { userId }, data: { read: true }, }); return result.count; }, async deleteNotificationsByType(userId, type) { const result = await db.notification.deleteMany({ where: { userId, type }, }); return result.count; }, async deleteAllNotifications(userId: number): Promise { const result = await db.notification.deleteMany({ where: { userId }, }); return result.count; }, };