use serde::{Deserialize, Serialize}; use mongodb::bson::{oid::ObjectId, DateTime, doc}; use mongodb::Collection; use super::health_data::EncryptedField; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Medication { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, #[serde(rename = "medicationId")] pub medication_id: String, #[serde(rename = "userId")] pub user_id: String, #[serde(rename = "profileId")] pub profile_id: String, #[serde(rename = "medicationData")] pub medication_data: EncryptedField, #[serde(rename = "reminders")] pub reminders: Vec, #[serde(rename = "createdAt")] pub created_at: DateTime, #[serde(rename = "updatedAt")] pub updated_at: DateTime, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MedicationReminder { #[serde(rename = "reminderId")] pub reminder_id: String, #[serde(rename = "scheduledTime")] pub scheduled_time: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MedicationDose { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, #[serde(rename = "medicationId")] pub medication_id: String, #[serde(rename = "userId")] pub user_id: String, #[serde(rename = "loggedAt")] pub logged_at: DateTime, #[serde(rename = "scheduledTime")] pub scheduled_time: Option, #[serde(rename = "taken")] pub taken: bool, #[serde(rename = "notes")] pub notes: Option, } /// Repository for Medication operations #[derive(Clone)] pub struct MedicationRepository { collection: Collection, dose_collection: Collection, } impl MedicationRepository { pub fn new(collection: Collection, dose_collection: Collection) -> Self { Self { collection, dose_collection } } /// Create a new medication pub async fn create(&self, medication: &Medication) -> mongodb::error::Result> { let result = self.collection.insert_one(medication, None).await?; Ok(Some(result.inserted_id.as_object_id().unwrap())) } /// Find a medication by ID pub async fn find_by_id(&self, id: &ObjectId) -> mongodb::error::Result> { self.collection.find_one(doc! { "_id": id }, None).await } /// Find all medications for a user pub async fn find_by_user(&self, user_id: &str) -> mongodb::error::Result> { use futures::stream::TryStreamExt; self.collection .find(doc! { "userId": user_id }, None) .await? .try_collect() .await .map_err(|e| mongodb::error::Error::from(e)) } /// Find medications for a user filtered by profile pub async fn find_by_user_and_profile(&self, user_id: &str, profile_id: &str) -> mongodb::error::Result> { use futures::stream::TryStreamExt; self.collection .find(doc! { "userId": user_id, "profileId": profile_id }, None) .await? .try_collect() .await .map_err(|e| mongodb::error::Error::from(e)) } /// Update a medication pub async fn update(&self, medication: &Medication) -> mongodb::error::Result<()> { if let Some(id) = &medication.id { self.collection.replace_one(doc! { "_id": id }, medication, None).await?; } Ok(()) } /// Delete a medication pub async fn delete(&self, medication_id: &ObjectId) -> mongodb::error::Result<()> { self.collection.delete_one(doc! { "_id": medication_id }, None).await?; Ok(()) } /// Log a dose pub async fn log_dose(&self, dose: &MedicationDose) -> mongodb::error::Result> { let result = self.dose_collection.insert_one(dose, None).await?; Ok(Some(result.inserted_id.as_object_id().unwrap())) } /// Get doses for a medication pub async fn get_doses(&self, medication_id: &str, limit: Option) -> mongodb::error::Result> { use futures::stream::TryStreamExt; use mongodb::options::FindOptions; let opts = if let Some(limit) = limit { FindOptions::builder() .sort(doc! { "loggedAt": -1 }) .limit(limit) .build() } else { FindOptions::builder() .sort(doc! { "loggedAt": -1 }) .build() }; self.dose_collection .find(doc! { "medicationId": medication_id }, opts) .await? .try_collect() .await .map_err(|e| mongodb::error::Error::from(e)) } /// Calculate adherence for a medication pub async fn calculate_adherence(&self, medication_id: &str, days: i64) -> mongodb::error::Result { use futures::stream::TryStreamExt; // Calculate the timestamp for 'days' ago let now = DateTime::now(); let now_millis = now.timestamp_millis(); let since_millis = now_millis - (days * 24 * 60 * 60 * 1000); let since = DateTime::from_millis(since_millis); let doses = self.dose_collection .find( doc! { "medicationId": medication_id, "loggedAt": { "$gte": since } }, None, ) .await? .try_collect::>() .await .map_err(|e| mongodb::error::Error::from(e))?; let total = doses.len() as i32; let taken = doses.iter().filter(|d| d.taken).count() as i32; let percentage = if total > 0 { (taken as f32 / total as f32) * 100.0 } else { 100.0 }; Ok(AdherenceStats { total_doses: total, taken_doses: taken, missed_doses: total - taken, adherence_percentage: percentage, }) } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AdherenceStats { pub total_doses: i32, pub taken_doses: i32, pub missed_doses: i32, pub adherence_percentage: f32, }