using adas_core.Application.Exceptions; using adas_core.Application.Repositories.Interfaces; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Enums; using adas_core.Domain.Models; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.DTO; using adas_core.Domain.Models.Filter; using adas_core.Domain.Models.Masters; using adas_core.Domain.Models.MongoModels; using adas_core.Domain.Models.Responses; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; namespace adas_core.Application.Services; public class MasterListService : IMasterListService where T : MasterList, new() { private readonly Lazy _admissionService; private readonly string? _assetsDirectory; private readonly ILocalAuditService _auditService; private readonly Lazy _clientMessageService; private readonly Lazy _dischargeService; private readonly Lazy _displayService; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger> _logger; private readonly Lazy _patientService; private readonly IServiceProvider _serviceProvider; private readonly ISubscribersService _subscribersService; private readonly Lazy _unitService; public MasterListService( ILogger> logger, IServiceProvider serviceProvider, Lazy clientMessageService, ISubscribersService subscribersService, Lazy unitService, Lazy displayService, Lazy patientService, Lazy dischargeService, Lazy admissionService, IOptions apiSettings, IHttpContextAccessor httpContextAccessor, ILocalAuditService auditService) { _logger = logger; _serviceProvider = serviceProvider; _clientMessageService = clientMessageService; _subscribersService = subscribersService; _unitService = unitService; _displayService = displayService; _patientService = patientService; _dischargeService = dischargeService; _admissionService = admissionService; _httpContextAccessor = httpContextAccessor; _auditService = auditService; if (apiSettings.Value.PathToDisplayAssets != null) _assetsDirectory = Path.Combine(apiSettings.Value.PathToDisplayAssets); } public async Task DeleteMasterListById(ObjectId id) { var repository = GetRepository(); var masterList = await repository.FindById(id, LocaleEnum.Default); if (masterList == null) { _logger.LogInformation("Error deleting masterList not found, id: {id} ", id); return; } if (await IsInUseCount(masterList) > 0) throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed); await repository.Delete(id); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, masterList, null); await SendMasterListBroadcast(masterList, OperationType.DeleteMasterList); } // public async Task AddOptionToMasterList(ObjectId id, OptionList opt) // { // try // { // var repository = GetRepository(); // var result = await repository.AddOptionToMasterList(id, opt); // if (result) // { // var masterList = await repository.FindById(id); // await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList); // await SendMasterListItemBroadcast(masterList,opt, opt, OperationType.AddMasterListItem); // // } // // return result; // // } // catch (Exception ex) // { // _logger.LogError("Error AddOptionToMasterList {name}. Exception: {ex}", typeof(T).Name, ex); // return false; // } // } public async Task AddOptionToMasterList(ObjectId id, FilterOptionListElement opt) { try { var repository = GetRepository(); //TODO: LOCALE var oldMasterList = await repository.FindById(id, opt.Locale); var result = await repository.AddOptionToMasterList(id, opt); if (result != null) { //TODO: LOCALE var masterList = await repository.FindById(id); if (masterList == null) return null; await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList); await SendMasterListItemBroadcast(masterList, result, result, OperationType.AddMasterListItem); } return result; } catch (Exception ex) { _logger.LogError("Error AddOptionToMasterList {name}. Exception: {ex}", typeof(T).Name, ex); return null; } } public async Task UpdateMasterListOption(ObjectId id, OptionList opt, string typeName, LocaleEnum locale) { try { var repository = GetRepository(); //TODO: LOCALE var oldMasterList = await repository.FindById(id); var oldOption = oldMasterList?.Options.FirstOrDefault(o => o.Id == opt.Id); var result = await repository.UpdateMasterListOption(id, opt, locale); if (result != null) { //TODO: LOCALE var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T(); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem); await UpdatePatientItemList(id, new UpdateOptionMasterListDto { OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName); } return result; } catch (Exception ex) { _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return null; } } public async Task UpdateMasterListOption(ObjectId id, OptionList opt, string typeName) { try { var repository = GetRepository(); //TODO: LOCALE var oldMasterList = await repository.FindById(id); var oldOption = oldMasterList?.Options.FirstOrDefault(o => o.Id == opt.Id); var result = await repository.UpdateMasterListOption(id, opt); if (result != null) { //TODO: LOCALE var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T(); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem); await UpdatePatientItemList(id, new UpdateOptionMasterListDto { OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName); } return result; } catch (Exception ex) { _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return null; } } public async Task UpdateFullMasterListOption(ObjectId id, OptionList opt, string typeName) { try { var repository = GetRepository(); //TODO: LOCALE var oldMasterList = await repository.FindById(id); var oldOption = oldMasterList?.Options.FirstOrDefault(o => o.Id == opt.Id); var result = await repository.UpdateFullMasterListOption(id, opt); if (result != null) { //TODO: LOCALE var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T(); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem); await UpdatePatientItemList(id, new UpdateOptionMasterListDto { OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName); } return result; } catch (Exception ex) { _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return null; } } public async Task DeleteMasterListOption(ObjectId id, ObjectId optId, string typeName) { try { var repository = GetRepository(); var oldMasterList = await repository.FindById(id, LocaleEnum.Default); var opt = oldMasterList?.Options.First(c => c.Id == optId); var result = await repository.DeleteMasterListOption(id, optId); if (result && opt != null) { var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T(); await DeletePatientItemList(id, opt, typeName); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList); await SendMasterListItemBroadcast(masterList, opt, opt, OperationType.DeleteMasterListItem); return true; } return false; } catch (Exception ex) { _logger.LogError("Error DeleteMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return false; } } public async Task UpdateOptionDetailsToMasterList(ObjectId id, UpdateMasterListDetailsDto opt) { try { var repository = GetRepository(); var oldMasterList = await repository.FindById(id); var result = await repository.UpdateOptionDetailsToMasterList(id, opt); if (result != null) { var masterList = await repository.FindById(id) ?? new T(); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList); } return result; } catch (Exception ex) { _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return null; } } public async Task UpdateMasterListName(ObjectId id, string name) { try { var repository = GetRepository(); var oldMasterList = await repository.FindById(id, LocaleEnum.Default); var result = await repository.UpdateMasterListName(id, name); if (result) { var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T(); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList); } return result; } catch (Exception ex) { _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return false; } } public async Task UpdateMasterListDescription(ObjectId id, string name) { try { var repository = GetRepository(); var oldMasterList = await repository.FindById(id, LocaleEnum.Default); var result = await repository.UpdateMasterListDescription(id, name); if (result) { var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T(); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList); } return result; } catch (Exception ex) { _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return false; } } public async Task RemoveMasterListOption(ObjectId id, OptionList oldOpt) { try { var repository = GetRepository(); var oldMasterList = await repository.FindById(id, LocaleEnum.Default); var result = await repository.RemoveMasterListOption(id, oldOpt); if (result) { var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T(); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList, masterList); await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList); await SendMasterListItemBroadcast(masterList, oldOpt, oldOpt, OperationType.DeleteMasterListItem); } return result; } catch (Exception ex) { _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex); return false; } } public async Task> GetAllMasterList() { try { var repository = GetRepository(); return await repository.GetAll(); } catch (Exception ex) { _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex); return []; } } public async Task> GetAllMasterListWithoutOptions() { try { var repository = GetRepository(); return await repository.GetAllWithoutOptions(); } catch (Exception ex) { _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex); return []; } } public async Task FindOptionItemById(ObjectId masterId, ObjectId optionId, LocaleEnum locale) { try { var repository = GetRepository(); return await repository.FindOptionItemById(masterId, optionId, locale); } catch (Exception ex) { _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex); return null; } } public async Task> GetAllMasterListWithPaginatedOptions( PaginationFilter request) { try { var repository = GetRepository(); var masterLists = await repository.GetAll(); List results = []; foreach (var masterList in masterLists) { var units = await IsInUse(masterList); var list = new MasterListWithPaginatedOptionsDto(masterList, request.PageSize, units); results.Add(list); } return results; } catch (Exception ex) { _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex); return []; } } public async Task GetAllMasterListCount() { try { var repository = GetRepository(); return await repository.Count(); } catch (Exception ex) { _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex); return 0; } } public async Task GetMasterListById(ObjectId id, LocaleEnum? locale) { try { var repository = GetRepository(); return await repository.FindById(id, locale); } catch (Exception ex) { _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex); return null; } } public async Task GetMasterListByIdWithPaginatedOptions(ObjectId id, PaginationFilter request) { try { var repository = GetRepository(); var list = await repository.FindById(id); if (list != null) { var units = await IsInUse(list); return new MasterListWithPaginatedOptionsDto(list, request.PageSize, units); } return null; } catch (Exception ex) { _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex); return null; } } public async Task> GetMasterListOptionsNamesById(ObjectId id) { try { var repository = GetRepository(); var list = await repository.FindById(id, LocaleEnum.Default); var nameList = new List(); list?.Options.ForEach(o => nameList.Add(o.Name)); return nameList; } catch (Exception ex) { _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex); return []; } } public async Task> GetMasterListByIdAndTextSearch(ObjectId id, string? textSearch) { try { var repository = GetRepository(); return await repository.GetMasterListByIdAndTextSearchContaining(id, textSearch); } catch (Exception ex) { _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex); return []; } } public async Task> GetMasterListByIdAndSearchOptions(ObjectId id, FilterOptionListElement filterOption) { var repository = GetRepository(); return await repository.GetMasterListByIdAndSearchOptions(id, filterOption); } public async Task GetMasterListByName(string name) { try { var repository = GetRepository(); var result = await repository.FindByName(name); return result; } catch (Exception ex) { _logger.LogError("Error getting by name: {name}. Exception: {ex}", name, ex); return null; } } public async Task> GetPaginatedMasterList(PaginationFilter filter) { var repository = GetRepository(); var result = repository.GetPaginatedMasterList(filter); var count = await result.CountDocumentsAsync(); var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize) .Limit(filter.PageSize) .ToCursorAsync(); var dataList = await data.ToListAsync(); return new PaginationResponse(dataList, filter.PageNumber, filter.PageSize, count); } public async Task> GetPaginatedMasterListWithPaginatedOptions( PaginationFilter listFilter, PaginationFilter optionFilter) { var repository = GetRepository(); var result = repository.GetPaginatedMasterList(listFilter); var count = await result.CountDocumentsAsync(); var data = await result.Skip((listFilter.PageNumber - 1) * listFilter.PageSize) .Limit(listFilter.PageSize) .ToCursorAsync(); var dataList = await data.ToListAsync(); List results = []; foreach (var list in dataList) { var units = await IsInUse(list); var listDto = new MasterListWithPaginatedOptionsDto(list, optionFilter.PageSize, units); results.Add(listDto); } return new PaginationResponse(results, listFilter.PageNumber, listFilter.PageSize, count); } public async Task> GetPaginatedOptions(PaginationFilter filter, ObjectId listId) { var repository = GetRepository(); var result = await repository.GetPaginatedOptions(filter, listId); var count = result.Count; var data = result.Skip((filter.PageNumber - 1) * filter.PageSize) .Take(filter.PageSize) .ToList(); return new PaginationResponse(data, filter.PageNumber, filter.PageSize, count); } public async Task InsertMasterList(T item) { try { var repository = GetRepository(); await repository.InsertOneAsync(item); await SendMasterListBroadcast(item, OperationType.NewMasterList); var result = await repository.FindById(item.Id); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, result); return result; } catch (Exception ex) { _logger.LogError("Error inserting {name}: {item}. Exception: {ex}", typeof(T).Name, item, ex); return null; } } public async Task UpdateMasterList(T item) { try { var repository = GetRepository(); var oldItem = repository.FindById(item.Id, LocaleEnum.Default); await repository.Update(item); await SendMasterListBroadcast(item, OperationType.UpdateMasterList); var result = await repository.FindById(item.Id, LocaleEnum.Default); await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldItem, result); return result; } catch (Exception ex) { _logger.LogError("Error updating {name}: {item}. Exception: {ex}", typeof(T).Name, item, ex); return null; } } public async Task GetAssociatedList(ObjectId id, MasterListType masterListType1, MasterListType masterListType2) { var repository = GetRepository(); await repository.FindById(id, LocaleEnum.Default); var units = await _unitService.Value.FindUnitsByMasterListId(id, masterListType1); var unitArray = units as Unit[] ?? (units ?? []).ToArray(); if (!unitArray.Any()) return null; var unit = unitArray.First(); switch (masterListType2) { case MasterListType.AltableOptionList: return unit.AltableOptionListId; case MasterListType.AllergyList: return unit.AllergyListId; case MasterListType.DestinationList: return unit.DestinationListId; case MasterListType.DiagnosisList: return unit.DiagnosisListId; case MasterListType.DischargeStatusList: return unit.DischargeStatusListId; case MasterListType.DoctorList: return unit.DoctorListId; case MasterListType.DoctorTypeList: return unit.DoctorTypeListId; case MasterListType.InternalDestinationList: return unit.InternalDestinationListId; case MasterListType.InsulationList: return unit.InsulationListId; case MasterListType.LanguageBarrierList: return unit.LanguageBarrierListId; case MasterListType.PassiveSittingList: return unit.PassiveSittingListId; case MasterListType.GenericList: return unit.GenericListId; case MasterListType.MobilityOptionList: return unit.MobilityOptionListId; case MasterListType.OriginList: return unit.OriginListId; case MasterListType.PatientStatusList: return unit.PatientStatusListId; case MasterListType.ProcedureList: return unit.ProcedureListId; case MasterListType.TestList: return unit.TestListId; case MasterListType.ServiceList: return unit.ServiceListId; case MasterListType.TherapeuticCeilingList: return unit.TherapeuticCeilingListId; case MasterListType.TreatmentList: return unit.TreatmentListId; case MasterListType.VisitOptionList: return unit.VisitOptionListId; case MasterListType.AccessControlList: return unit.AccessControlListId; } return null; } public async Task> GetOptionsOfList(ObjectId id) { var repository = GetRepository(); var list = await repository.FindById(id, LocaleEnum.Default); List options = []; if (list == null) return options; foreach (var item in list.Options) options.Add(item.Name); return options; } private IMasterListRepository GetRepository() { return _serviceProvider.GetRequiredService>(); } private async Task UpdatePatientItemList(ObjectId id, UpdateOptionMasterListDto opt, string typeName) { // Necesito saber que unidades tienen el id de lista que estamos modificando var unitList = await _unitService.Value.FindUnitsByMasterListId(id); // Que pacientes dentro de esa/s unidades tienen el valor antiguo de la lista que estamos modificando var units = unitList as Unit[] ?? unitList.ToArray(); await _patientService.Value.UpdatePatientMasterListItemChange(opt, units, typeName); await _dischargeService.Value.UpdatePatientMasterListItemChange(opt, units, typeName); await _admissionService.Value.UpdatePatientMasterListItemChange(opt, units, typeName); } private async Task DeletePatientItemList(ObjectId id, OptionList opt, string typeName) { // Necesito saber que unidades tienen el id de lista que estamos modificando var unitList = await _unitService.Value.FindUnitsByMasterListId(id); // Que pacientes dentro de esa/s unidades tienen el valor antiguo de la lista que estamos modificando var units = unitList as Unit[] ?? unitList.ToArray(); await _patientService.Value.DeletePatientMasterListItem(opt, units, typeName); await _dischargeService.Value.DeletePatientMasterListItem(opt, units, typeName); await _admissionService.Value.DeletePatientMasterListItem(opt, units, typeName); } public Task SaveRequestAsync(ApiRequest apiRequest) { return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); })); } public Task SaveRequest(ApiRequest apiRequest) { return Task.CompletedTask; } private async Task SendMasterListBroadcast(T masterList, OperationType operation) { try { var type = masterList.GetType(); var masterListTypeName = type.Name; if (!Enum.TryParse(masterListTypeName, out var masterListType)) return; var units = await _unitService.Value.FindUnitsByMasterListId(masterList.Id, masterListType); if (units == null) return; List diplayIds = []; foreach (var unit in units) { var displayList = await _displayService.Value.GetByUnitId(unit.Id); displayList.ForEach(d => diplayIds.Add(d.Id)); } var subscribers = _subscribersService.GetSubscribers() .Where(s => s.DisplayId.HasValue && diplayIds.Contains(s.DisplayId.Value)).ToList(); foreach (var subscriber in subscribers) _ = _clientMessageService.Value.SendAsync(subscriber.Id, operation, masterList.ReturnMasterListOptionsInLocaleIfExist(subscriber.Locale)); } catch (Exception ex) { _logger.LogError("Exception sending masterList broadcast. Operation type: {op}. Exception: {ex}", operation.ToString(), ex); } } private async Task SendMasterListItemBroadcast(T masterList, OptionList? updatedItem, OptionList? oldItem, OperationType operation) { try { var type = masterList.GetType(); var masterListTypeName = type.Name; if (!Enum.TryParse(masterListTypeName, out var masterListType)) return; var units = await _unitService.Value.FindUnitsByMasterListId(masterList.Id, masterListType); if (units == null) return; List diplayIds = []; foreach (var unit in units) { var displayList = await _displayService.Value.GetByUnitId(unit.Id); displayList.ForEach(d => diplayIds.Add(d.Id)); } var subscribers = _subscribersService.GetSubscribers() .Where(s => s.DisplayId.HasValue && diplayIds.Contains(s.DisplayId.Value)).ToList(); object message; switch (operation) { case OperationType.AddMasterListItem: message = new { newItem = updatedItem, listType = masterListTypeName, name = masterList.Name }; break; case OperationType.DeleteMasterListItem: message = new { deletedItem = oldItem, listType = masterListTypeName, name = masterList.Name }; break; default: message = new { updatedItem, oldItem, listType = masterListTypeName, name = masterList.Name }; break; } foreach (var subscriber in subscribers) _ = _clientMessageService.Value.SendAsync(subscriber.Id, operation, message); } catch (Exception ex) { _logger.LogError("Exception sending masterList broadcast. Operation type: {op}. Exception: {ex}", operation.ToString(), ex); } } private async Task> IsInUse(T list) { var units = await _unitService.Value.FindUnitsByMasterListId(list.Id, list.ListType); List unitInfoDtos = []; var unitArray = units as Unit[] ?? (units ?? []).ToArray(); if (units != null && unitArray.Any()) foreach (var unit in unitArray) { var unitInfoDto = new UnitInfoDto(unit); unitInfoDtos.Add(unitInfoDto); } return unitInfoDtos; } private async Task IsInUseCount(T list) { return await _unitService.Value.CountUnitsByMasterListId(list.Id, list.ListType); } }