using adas_core.Application.Exceptions; using adas_core.Application.Repositories.Interfaces; using adas_core.Application.Services.Interfaces; using adas_core.Application.Subscriptions; using adas_core.Domain.Enums; using adas_core.Domain.Models; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.MongoModels; using adas_core.Domain.Utils; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; namespace adas_core.Application.Services; public class DiagnosisService : IDiagnosisService { private readonly ILocalAuditService _auditService; private readonly Lazy _calculatedObservations; private readonly IClientMessageService _clientMessageService; private readonly IDiagnosisArchiveRepository _diagnosisArchiveRepository; private readonly List _diagnosisCode = []; private readonly IDiagnosisRepository _diagnosisRepository; private readonly string _diagnosisSystem; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; private readonly Lazy _patientService; private readonly ISubscribersService _subscribersService; private readonly IUnitService _unitService; public DiagnosisService( Lazy patientService, IOptions apiSettings, IDiagnosisRepository diagnosisRepository, IDiagnosisArchiveRepository diagnosisArchiveRepository, ILogger logger, IClientMessageService clientMessageService, ISubscribersService subscribersService, Lazy calculatedObservations, IHttpContextAccessor httpContextAccessor, ILocalAuditService auditService, IUnitService unitService) { _patientService = patientService; _diagnosisRepository = diagnosisRepository; _diagnosisArchiveRepository = diagnosisArchiveRepository; _logger = logger; _clientMessageService = clientMessageService; _subscribersService = subscribersService; _calculatedObservations = calculatedObservations; _unitService = unitService; _httpContextAccessor = httpContextAccessor; _auditService = auditService; _diagnosisSystem = apiSettings.Value.DiagnosisSystem ?? "CUSTOM"; if (apiSettings.Value.DiagnosisCode.Any()) _diagnosisCode = apiSettings.Value.DiagnosisCode; } // private async Task SendBroadcast(PatientDiagnosis diagnosis) // { // var patient = await _patientService.Value.FindById(diagnosis.PatientId); // if (patient == null) return; // // // var subscribers = _subscribersService.GetSubscribers().Where(s => // (s.SubscriptionType == SubscriptionType.Box && s.Box == patient.Bed && // s.Section == patient.UnitString) || // (s.SubscriptionType == SubscriptionType.Section && s.Section == patient.UnitString)).ToList(); // subscribers.ForEach(Action); // return; // // async void Action(WsSubscriber subscriber) => // await _clientMessageService.SendAsync(subscriber.Id, OperationType.Diagnosis, diagnosis); // } public async Task Archive(Patient patient) { await ArchiveByPatientId(patient.Id); } public async Task ArchiveByPatientId(ObjectId id) { _logger.LogDebug("Archive Diagnoses by Patient Id {id}", id); using (var cursor = await FindByPatientIdAsync(id)) { while (await cursor.MoveNextAsync()) foreach (var current in cursor.Current) await _diagnosisArchiveRepository.InsertOneAsync(current); } await DeleteByPatientId(id); } public async Task DeleteByPatientId(ObjectId id) { _logger.LogDebug("Delete Diagnoses by Patient Id {id}", id); var oldPatient = await _diagnosisRepository.GetByPatient(id); await _diagnosisRepository.DeleteByPatientId(id); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldPatient, null); } public async Task> GetByPatientId(ObjectId patientId) { var diagnosis = await _diagnosisRepository.GetByPatient(patientId); return diagnosis; } public Task SaveRequest(ApiRequest apiRequest) { return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest, null); })); } public Task SaveRequestAsync(ApiRequest apiRequest) { return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest, null); })); } public async Task ProcessDiagnosisObservation(ApiRequest apiRequest, Patient patient) { _logger.LogDebug("INSERT DiagnosisObservation from obsservation"); var time = apiRequest.ObservationData?.Time; if (time == null) _logger.LogWarning("Processing diagnosis without time. Set Datetime now {DateTimeNow}. ", DateTime.Now); var obs = new PatientDiagnosis { CodingSystem = _diagnosisSystem, Time = time ?? DateTime.Now, PatientId = patient.Id, MessageTime = apiRequest.MessageTime }; if (apiRequest.Observations == null) { _logger.LogError("ApiRequest Observations null. "); return; } for (var i = 0; i <= apiRequest.Observations.Count - 1; i++) { var value = apiRequest.Observations[i].Value; var strValue = value.ToString() ?? "null"; switch (apiRequest.Observations[i].Code) { case "272099008": obs.Description = strValue; break; case "1000000013": obs.Label = strValue; break; case "1000000014": obs.Code = strValue; break; case "394731006": obs.State = strValue; break; case "272125009": obs.Category = strValue; break; case "398201009": if (DateTime.TryParse(apiRequest.Observations[i].Value.ToString(), out var startTime)) obs.StartTime = startTime; break; case "397898000": if (DateTime.TryParse(apiRequest.Observations[i].Value.ToString(), out var endTime)) obs.EndTime = endTime; break; } } await InsertDiagnosis(obs); } public async Task SaveRequest(ApiRequest apiRequest, Patient? patient) { if (patient == null) { _logger.LogDebug("message:ApiRequest Diagnosis"); if (string.IsNullOrEmpty(apiRequest.PatientNumber) && string.IsNullOrEmpty(apiRequest.Location?.UnitName)) { _logger.LogDebug("person and PointOfCare are nulls"); throw new ApiRequestException("person and PointOfCare are nulls"); } _logger.LogDebug("patientNumber: {apiRequestpatientNumber} location: {apiRequestlocation}", apiRequest.PatientNumber, apiRequest.Location); _logger.LogDebug("RequestType: {apiRequesttype}", apiRequest.Type); patient = await _patientService.Value.FindPatientByApiRequest(apiRequest); } if (patient == null) { // NO PATIENTS OR LOCATIONS WERE FOUND _logger.LogWarning( "Observation for patient: {apiRequestpatientNumber} with location: {apiRequestlocation} not found, ignoring", apiRequest.PatientNumber, apiRequest.Location); return; } var unitConfig = await _unitService.FindById(patient.UnitId); if (!Hl7Utils.ManageAutoAdt(unitConfig, null, _logger, "ORU Diagnosis")) return; switch (apiRequest.Type) { //* ORU_R01 - Unsolicited transmission of an observation message //* ORU_R40 - Unsolicited transmission of an alert observation message case "ORU_R01": case "ORU_R40": // OBSERVATIONS if (apiRequest.Observation != null && apiRequest.Observations?.FirstOrDefault() == null) apiRequest.Observations = [apiRequest.Observation]; if (apiRequest.Observations != null) { var obrcode = apiRequest.ObservationData?.Code; if (apiRequest.ObservationData?.Value != null) apiRequest.Observations.Add(new PatientObservation { Value = apiRequest.ObservationData.Value }); if (obrcode != null && _diagnosisCode.Contains(obrcode)) _ = ProcessDiagnosisObservation(apiRequest, patient); } break; default: _logger.LogWarning("ApiRequest type {apiRequesttype} sis not valid for Diagnosis", apiRequest.Type); throw new ApiRequestException("ApiRequest type " + apiRequest.Type + " is not valid for Diagnosis"); } } public async Task ProcessDiagnosis(List diagnosis, Patient patient, DateTime messageTime) { _logger.LogDebug("INSERT DiagnosisObservation from diagnosis"); foreach (var d in diagnosis) { d.PatientId = patient.Id; d.Time = messageTime; await InsertDiagnosis(d); } } public async Task UpdateManyObjectId(string nameId, ObjectId id, ObjectId oldId) { await _diagnosisRepository.UpdateManyObjectId(nameId, id, oldId); } public async Task> GetByPatient(ObjectId id) { return await _diagnosisRepository.GetByPatient(id); } public async Task Insert(PatientDiagnosis diagnosis) { _logger.LogDebug("Insert {diagnosis}", diagnosis); var diag = await MapDiagnosis(diagnosis); if (diag != null) { await _diagnosisRepository.InsertOneAsync(diag); await SendBroadcast(diag); } } private async Task MapDiagnosis(PatientDiagnosis diagnosis) { var diagnosis2 = await _calculatedObservations.Value.Map(diagnosis); if (diagnosis2 == null) _logger.LogDebug("Mapping {diagnosis}: Ignored", diagnosis2); return diagnosis2; } private async Task SendBroadcast(PatientDiagnosis diagnosis) { var patient = await _patientService.Value.FindById(diagnosis.PatientId); if (patient == null) return; var displaySubscribers = _subscribersService.GetSubscribers().Where(s => !s.Locations.IsNullOrEmpty() && s.Locations.Any(c => c.UnitName == patient.Location.UnitName && c.Bed == patient.Location.Bed && c.Room == patient.Location.Room )).ToList(); displaySubscribers.ForEach(Action); return; void Action(WsSubscriber subscriber) { _clientMessageService.SendAsync(subscriber.Id, OperationType.Diagnosis, diagnosis); } } public Task> FindByPatientIdAsync(ObjectId patientId) { return _diagnosisRepository.FindByPatientIdAsync(patientId); } public async Task InsertDiagnosis(PatientDiagnosis diagnosis) { try { _logger.LogDebug("Insert {diagnosis}", diagnosis); var diag = await MapDiagnosis(diagnosis); if (diag == null) { _logger.LogError("Mepped diagnosis is null. Diagnosis: {diagnosis}", diagnosis); } else { var dgdb = await _diagnosisRepository.FindByPatientIdAndCode(diag.PatientId, diag.Code, diag.CodingSystem); if (dgdb != null) { var auxDgdb = dgdb; diag.Id = dgdb.Id; diag.Time = dgdb.Time; diag.UpdateDate = diagnosis.Time; await _diagnosisRepository.UpdateOneAsync(diag.Id, diag); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, auxDgdb, dgdb); } else { await _diagnosisRepository.InsertOneAsync(diagnosis); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, diagnosis); } _ = SendBroadcast(diagnosis); } } catch (Exception ex) { _logger.LogError("Error inserting diagnosis. Diagnosis: {diagnosis}. Exception:{ex}", diagnosis, ex); } } }