using adas_core.Application.Repositories.Interfaces; using adas_core.Domain.Models; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.MongoModels; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; using Serilog; using MongoUtils = adas_core.Infrastructure.Utils.MongoUtils; namespace adas_core.Infrastructure.Repositories; /// /// Repository implementation for managing Patient archive entities in MongoDB. /// Provides CRUD operations for historical/archived patient data. /// public class PatientArchiveRepository : MongoRepository, IPatientArchiveRepository { private readonly ApiSettings _apiSettings; /// /// Initializes a new instance of the PatientArchiveRepository. /// /// API settings containing collection names configuration. /// The MongoDB database instance. /// Thrown when apiSettings is null. public PatientArchiveRepository(IOptions apiSettings, IMongoDatabase database) : base(database) { if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings)); _apiSettings = apiSettings.Value; } //For testing /// /// Gets the name of the collection for archived patients. /// /// The collection name from API settings, or default "archive_patient". public override string GetCollectionName() { return _apiSettings.ArchivePatient ?? "archive_patient"; } /// /// Finds an archived patient by their patient number. /// /// The patient number to search for. /// The Patient if found; otherwise, null. public async Task FindByPatientNumber(string patientNumber) { if (string.IsNullOrWhiteSpace(patientNumber)) return null; var filterBuilder = Builders.Filter; var filter = filterBuilder.Eq(p => p.PatientNumber, patientNumber); var result = await Collection.FindAsync(filter); return await result.FirstOrDefaultAsync(); } /// /// Retrieves all archived patients from the collection. /// /// A list of all Patient entities in the archive. public async Task> FindAll() { var filterBuilder = Builders.Filter; var filter = filterBuilder.Empty; var result = await Collection.FindAsync(filter); return await result.ToListAsync(); } /// /// Searches for an archived patient by patient number, returning null if multiple matches exist. /// This is useful when patientNumber may be incomplete and could match multiple patients. /// /// The patient number to search for. /// The unit ID (currently not used in query, kept for interface compatibility). /// The unique Patient if exactly one match is found; otherwise, null if multiple or none. public async Task SearchByPatientNumberAndDistinctUnit(string patientNumber, ObjectId unitId) { if (string.IsNullOrWhiteSpace(patientNumber)) return null; // Paciente ubicado en un PoC pero en diferente unidad var patient = await Collection.Find(Builders.Filter.And( Builders.Filter.Eq(p => p.PatientNumber, patientNumber) )).ToListAsync(); // Si hay mas de una coincidencia devolvemos null por que puede no estar completo el patientNumber return patient.Count > 1 ? null : patient.FirstOrDefault(); } /// /// Inserts or updates an archived patient. /// If a patient with the same PatientNumber already exists, merges the data and updates the record. /// If no match exists, performs a regular insert. /// /// The Patient entity to insert or merge. /// Logs warning and silently fails on error. public override async Task InsertOneAsync(Patient obj) { try { if (obj.PatientNumber != null) { var patient = await FindByPatientNumber(obj.PatientNumber); if (patient != null) { patient.Allergies = obj.Allergies; patient.Doctors = obj.Doctors; patient.Procedures?.AddRange(obj.Procedures ?? []); patient.Tests?.AddRange(obj.Tests ?? []); patient.Treatment?.AddRange(obj.Treatment ?? []); patient.Diagnosis = obj.Diagnosis; patient.DiagnosisAux = obj.DiagnosisAux; patient.Insulation = obj.Insulation; patient.Mobility = obj.Mobility; patient.Origin = obj.Origin; patient.OriginAux = obj.OriginAux; patient.Person = patient.Person; patient.ArchiveDate = DateTime.UtcNow; patient.Visits = obj.Visits; patient.AccessControl = obj.AccessControl; patient.AdmTime = obj.AdmTime; patient.PointOfCareId = obj.PointOfCareId; patient.UnitId = obj.UnitId; patient.TherapeuticCeiling = obj.TherapeuticCeiling; patient.Altable = obj.Altable; if (patient.HistoricalLocations == null) patient.HistoricalLocations = obj.HistoricalLocations; else if (obj.HistoricalLocations != null) foreach (var objHistoricalLocation in obj.HistoricalLocations) if (!patient.HistoricalLocations.Any(c=> c.AdmTime == objHistoricalLocation.AdmTime)) patient.HistoricalLocations.Add(objHistoricalLocation); await UpdateOneAsync(patient.Id, patient); } else { await base.InsertOneAsync(obj); } } else { await base.InsertOneAsync(obj); } } catch (Exception e) { Log.Warning("Exception trying to insert archive patient: {obj}. Exception {e}", obj, e); } } /// /// Creates the necessary indexes for the PatientArchive collection. /// Creates an index on patientNumber for improved query performance. /// public override async Task CreateIndexes() { var options = new CreateIndexOptions { Background = true, Unique = false }; var indexes = new List> { new("{ patientNumber: 1 }", options) }; await MongoUtils.EnsureIndexes(Collection, indexes); } }