using System.Reflection; 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.DTO; using adas_core.Domain.Models.Masters; using adas_core.Domain.Models.MongoModels; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using MongoDB.Bson; namespace adas_core.Application.Services; public class DischargeService : IDischargeService { private readonly ILocalAuditService _auditService; private readonly IClientMessageService _clientMessageService; private readonly IDischargeRepository _dischargeRepository; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; private readonly IMasterListServiceFactory _masterListServiceFactory; private readonly Lazy _patientServiceLazy; private readonly IPointOfCareService _pointOfCareService; private readonly ISubscribersService _subscribersService; private readonly IUnitService _unitService; public DischargeService(ILogger logger, ISubscribersService subscribersService, IDischargeRepository dischargeRepository, Lazy patientServiceLazy, IClientMessageService clientMessageService, IPointOfCareService pointOfCareService, IHttpContextAccessor httpContextAccessor, ILocalAuditService auditService, IUnitService unitService, IMasterListServiceFactory masterListServiceFactory) { _logger = logger; _subscribersService = subscribersService; _dischargeRepository = dischargeRepository; _patientServiceLazy = patientServiceLazy; _clientMessageService = clientMessageService; _pointOfCareService = pointOfCareService; _pointOfCareService = pointOfCareService; _httpContextAccessor = httpContextAccessor; _auditService = auditService; _unitService = unitService; _masterListServiceFactory = masterListServiceFactory; } public async Task DeleteDischargeAsync(Discharge discharge) { //var patient = await _patientServiceLazy.Value.FindById(discharge.PatientId); //if (!discharge.MedicalDischarge.HasValue || !discharge.AdminDischarge.HasValue // // || DischargeStatusType.NoAltable.ToString().Equals(patient?.DischargeStatus?.OptionType) // ) //{ // _logger.LogError("The patient cannot be discharged"); // return; //} await DeleteDischargeByIdAsync(discharge.Id); } public async Task DeleteDischargeByIdAsync(ObjectId dischargeId) { try { var dischargeAux = await _dischargeRepository.FindById(dischargeId); if (dischargeAux == null) { _logger.LogError("Error Discharge not found, id: {discharge} ", dischargeId); return; } await _dischargeRepository.Delete(dischargeId); _logger.LogInformation("Discharge id: {dischargeId} DELETED ", dischargeId); //var patient = await _patientServiceLazy.Value.FindById(dischargeAux.PatientId); // if (patient != null) // await _patientServiceLazy.Value.ArchivePatient(patient); // else // _logger.LogError("Patient not found on Discharge id: {discharge} ", dischargeId); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, dischargeAux, null); SendDischargeBroadcast(dischargeAux, OperationType.DeleteDischarge); } catch (Exception ex) { _logger.LogError("Exception deleting discharge id:{admission} . Exception: {ex}", dischargeId, ex); } } public async Task CountDischargesByUnitId(ObjectId unitId) { return await _dischargeRepository.CountByUnitId(unitId); } public async Task GetDischargeByIdAsync(ObjectId dischargeId) { var result = await _dischargeRepository.FindById(dischargeId) ?? throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing); if (result.PointOfCareId == null) return result; var poc = await _pointOfCareService.GetInfo(result.PointOfCareId.Value, null,false); result.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room); return result; } public async Task> GetDischargesAsync() { return await _dischargeRepository.FindAll(); } public async Task GetDischargeByPatientId(ObjectId patientId) { try { var discharge = await _dischargeRepository.GetByPatientId(patientId); if (discharge == null) _logger.LogError("Discharge not found by patient Id {id}", patientId); if (discharge is { PointOfCareId: not null }) { var poc = await _pointOfCareService.GetInfo(discharge.PointOfCareId.Value, null,false); discharge.PatientLocation = new PatientLocation(poc?.UnitName, poc?.Bed, poc?.Room); } return discharge; } catch (Exception ex) { _logger.LogError("Exception getting discharge by patient id: {id} . Exception: {ex}", patientId, ex); return null; } } public async Task InsertDischarge(Discharge discharge) { await _dischargeRepository.InsertOneAsync(discharge); var dischargeAux = await _dischargeRepository.FindById(discharge.Id) ?? throw new ConflictException(HttpEnum.ErrorMessage.ConflictCreationFailed); _logger.LogInformation("Discharge: {discharge} INSERTED", discharge); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, dischargeAux); SendDischargeBroadcast(discharge, OperationType.NewDischarge); return dischargeAux; } public async Task GetDischargeByLocation(PatientLocation location) { try { return await _dischargeRepository.GetDischargeByLocation(location); } catch (Exception e) { _logger.LogError($"Unable to get discharge by location on service Exception: {e}"); return null; } } public async Task GetDischargeByPointOfCareId(ObjectId poc) { try { var discharge = await _dischargeRepository.GetDischargeByPointOfCareId(poc); if (discharge == null) _logger.LogInformation("Discharge not found by PointOfCareId {id}", poc); if (discharge is { PointOfCareId: not null }) { var pocInfo = await _pointOfCareService.GetInfo(discharge.PointOfCareId.Value, null,false); discharge.PatientLocation = new PatientLocation(pocInfo?.UnitName, pocInfo?.Bed, pocInfo?.Room); } return discharge; } catch (Exception e) { _logger.LogError($"Unable to get discharge by location on service Exception: {e}"); return null; } } public async Task GetDischargeByPointOfCareIdWithLocale(ObjectId location, LocaleEnum dataLocale) { var discharge = await GetDischargeByPointOfCareId(location); if (discharge == null) return null; var unit = await _unitService.FindById(discharge.UnitId); if (unit == null) return discharge; var dischargeWithLocale = await GetDischargeWithLocale(unit, discharge, dataLocale); return dischargeWithLocale; } public async Task UpdateDischargeAsync(Discharge discharge) { var oldDischarge = await GetDischargeByIdAsync(discharge.Id) ?? throw new ConflictException(HttpEnum.ErrorMessage.ConflictUpdateFailed); await _dischargeRepository.Update(discharge); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldDischarge, discharge); SendDischargeBroadcast(discharge, OperationType.UpdateDischarge); _logger.LogInformation("Discharge: {discharge} UPDATED", discharge); } public async Task SaveRequest(ApiRequest apiRequest) { try { if (apiRequest.Discharge?.Patient == null) return; var patientId = apiRequest.Discharge.Patient.Id; var patient = await _patientServiceLazy.Value.FindById(patientId); if (patient == null) { _logger.LogError("Error discharging patient id: {id} NOT FOUND", patientId); return; } await _patientServiceLazy.Value.Update(apiRequest.Discharge.Patient); switch (apiRequest.Type) { case "NewDischarge": { //TODO: ver qué tipos llegan if (!"Altable".Equals(apiRequest.Discharge.Patient.DischargeStatus?.OptionType)) { _logger.LogError("Error discharging. Patient not altable: {patient}", patient); return; } await _dischargeRepository.InsertOneAsync(apiRequest.Discharge); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, apiRequest.Discharge); SendDischargeBroadcast(apiRequest.Discharge, OperationType.NewDischarge); break; } case "UpdateDischarge": { await GetDischargeByIdAsync(apiRequest.Discharge.Id); await UpdateDischargeAsync(apiRequest.Discharge); break; } case "DeleteDischarge": { if (StatusEnum.Discharge.NoAltable.ToString().Equals(patient.DischargeStatus?.OptionType)) { _logger.LogError("Error deleting discharge. Patient altable: {patient}", patient); return; } await DeleteDischargeAsync(apiRequest.Discharge); break; } } } catch (Exception ex) { _logger.LogError("Exception processing discharge api request {discharge} . Exception: {ex}", apiRequest.Discharge, ex); } } public Task SaveRequestAsync(ApiRequest apiRequest) { return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); })); } // Revisar public async void SendDischargeBroadcast(Discharge discharge, OperationType operation) { try { if (discharge.PointOfCareId == null) { _logger.LogError("Error sending discharge broadcast. Unit name is null or empty {discharge} .", discharge); return; } var subscribersGroup = _subscribersService.GetSubscribers() .Where(s => s.LocationIds.Any(c => c == discharge.PointOfCareId)).GroupBy(h => h.Locale) .ToList(); var unit = await _unitService.FindById(discharge.UnitId); foreach (var group in subscribersGroup) { var locale = group.Key ?? LocaleEnum.Default; IEnumerable subscribers = group; foreach (var subscriber in subscribers) { var dischargeWithLocale = await GetDischargeWithLocale(unit, discharge, locale); _ = _clientMessageService.SendAsync(subscriber.Id, operation, dischargeWithLocale); } } } catch (Exception ex) { _logger.LogError("Exception sending discharge broadcast. Operation type: {op}. Exception: {ex}", operation.ToString(), ex); } } public async Task UpdatePatientMasterListItemChange(UpdateOptionMasterListDto opt, IEnumerable unitList, string typeName) { var unitIds = unitList.Select(x => x.Id).ToList(); await _dischargeRepository.GetDischargesByUnitIds(unitIds); var dischargeUpdatedList = await _dischargeRepository.UpdateMasterListOption(unitIds, opt, typeName); var updatedList = dischargeUpdatedList as Discharge[] ?? dischargeUpdatedList.ToArray(); foreach (var discharge in updatedList) { var oldDischarge = updatedList.FirstOrDefault(dis => dis.Id == discharge.Id); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldDischarge!, discharge); SendDischargeBroadcast(discharge, OperationType.UpdateDischarge); } } public async Task DeletePatientMasterListItem(OptionList opt, IEnumerable unitList, string typeName) { var unitIds = unitList.Select(x => x.Id).ToList(); var patientUpdatedList = await _dischargeRepository.DeleteMasterListOption(unitIds, opt, typeName); foreach (var discharge in patientUpdatedList) { var dischargeUpdated = await GetDischargeByIdAsync(discharge.Id); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, discharge, null); if (dischargeUpdated != null) SendDischargeBroadcast(discharge, OperationType.UpdateDischarge); } } public async Task DeleteDischargesByUnitId(ObjectId unitId) { await _dischargeRepository.DeleteByUnitId(unitId); } private async Task GetDischargeWithLocale(Unit? unit, Discharge discharge, LocaleEnum locale) { if (unit == null) return discharge; if (locale == LocaleEnum.Default) return discharge; // Campos del discharge que deben traducirse var listMap = new List<(string field, ObjectId? listId, MasterListType type)> { ("serviceOption", unit.ServiceListId, MasterListType.ServiceList), ("destinationOption", unit.DestinationListId, MasterListType.DestinationList) }; foreach (var (field, listId, masterListType) in listMap) { if (listId == null) continue; // Obtener propiedad desde DISCHARGE, no Patient var prop = typeof(Discharge).GetProperty( field, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if (prop == null) continue; var propValue = prop.GetValue(discharge); if (propValue == null) continue; // Cargar master list traducida según locale var listObj = await _masterListServiceFactory .GetMasterListById(masterListType, listId.Value, locale); if (listObj == null) continue; var master = listObj as dynamic; IEnumerable masterOptions = master.Options; // El campo puede ser OptionList simple if (propValue is OptionList { Id: not null } option) { var translated = masterOptions.FirstOrDefault(o => o.Id == option.Id); if (translated != null) option.Name = translated.Name; // Solo traducimos el nombre } } return discharge; } }