use axum::{ extract::{Extension, Json, Path, Query, State}, http::StatusCode, }; use serde::Deserialize; use std::time::SystemTime; use crate::{ auth::jwt::Claims, config::AppState, models::appointment::{ AppointmentRepository, AppointmentResponse, CreateAppointmentRequest, UpdateAppointmentRequest, }, models::health_data::EncryptedField, }; use mongodb::bson::DateTime; #[derive(Debug, Deserialize)] pub struct AppointmentListQuery { /// Filter by status (upcoming/completed/cancelled). Optional. pub status: Option, /// Filter by profile id. Optional. pub profile_id: Option, } pub async fn create_appointment( State(state): State, Extension(claims): Extension, Json(req): Json, ) -> Result, StatusCode> { let database = state.db.get_database(); let repo = AppointmentRepository::new(database.collection("appointments")); let now = SystemTime::now(); let appointment_id = uuid::Uuid::new_v4().to_string(); // Zero-knowledge: store the client's opaque encrypted blob verbatim. let appointment_data = EncryptedField { data: req.encrypted_data.data, encrypted: true, iv: req.encrypted_data.iv, auth_tag: req.encrypted_data.auth_tag, }; let appointment = crate::models::appointment::Appointment { id: None, appointment_id, user_id: claims.sub, profile_id: req.profile_id.clone(), appointment_data, reminders: vec![], status: req.status, created_at: DateTime::from_system_time(now), updated_at: DateTime::from_system_time(now), }; match repo.create(appointment).await { Ok(appt) => Ok(Json(appt.into())), Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } } pub async fn list_appointments( State(state): State, Extension(claims): Extension, Query(query): Query, ) -> Result>, StatusCode> { let database = state.db.get_database(); let repo = AppointmentRepository::new(database.collection("appointments")); match repo .find_by_user_filtered( &claims.sub, query.status.as_deref(), query.profile_id.as_deref(), ) .await { Ok(appointments) => { let resp: Vec = appointments.into_iter().map(Into::into).collect(); Ok(Json(resp)) } Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } } pub async fn get_appointment( State(state): State, Extension(_claims): Extension, Path(id): Path, ) -> Result, StatusCode> { let database = state.db.get_database(); let repo = AppointmentRepository::new(database.collection("appointments")); match repo.find_by_appointment_id(&id).await { Ok(Some(appt)) => Ok(Json(appt.into())), Ok(None) => Err(StatusCode::NOT_FOUND), Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } } pub async fn update_appointment( State(state): State, Extension(_claims): Extension, Path(id): Path, Json(req): Json, ) -> Result, StatusCode> { let database = state.db.get_database(); let repo = AppointmentRepository::new(database.collection("appointments")); match repo.update_by_appointment_id(&id, req).await { Ok(Some(appt)) => Ok(Json(appt.into())), Ok(None) => Err(StatusCode::NOT_FOUND), Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } } pub async fn delete_appointment( State(state): State, Extension(_claims): Extension, Path(id): Path, ) -> Result { let database = state.db.get_database(); let repo = AppointmentRepository::new(database.collection("appointments")); match repo.delete_by_appointment_id(&id).await { Ok(true) => Ok(StatusCode::NO_CONTENT), Ok(false) => Err(StatusCode::NOT_FOUND), Err(_) => Err(StatusCode::INTERNAL_SERVER_ERROR), } }