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.DTO.Display;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.GroupedObservations;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using adas_core.Domain.Utils;
using Microsoft.AspNetCore.Http;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using Serilog;
using DisplayConfig = adas_core.Domain.Models.MongoModels.DisplayConfig;
namespace adas_core.Application.Services;
///
/// Implements to coordinate display configuration operations across multiple repositories and supporting services.
///
///
/// The service composes card, detail, and chart configuration repositories with display, subscriber, messaging, auditing, and master list services to manage display configuration workflows. It receives for accessing the current HTTP context and uses of to defer initialization of the display service dependency.
///
///
public class DisplayConfigService(
IDisplayConfigRepository displayConfigRepository,
Lazy displayService,
ISubscribersService subscribersService,
IClientMessageService clientMessageService,
IHttpContextAccessor httpContextAccessor,
ILocalAuditService auditService,
IMasterListServiceFactory masterListServiceFactory,
IDisplayCardConfigRepository displayCardConfigRepository,
IDisplayDetailConfigRepository displayDetailConfigRepository,
IDisplayChartConfigRepository displayChartRepository)
: IDisplayConfigService
{
///
/// Retrieves all display configurations from the repository and enriches each one with its minimal display section.
/// Configurations for which the minimal display section cannot be resolved are excluded from the result.
///
/// A task that represents the asynchronous operation. The task result contains a list of enriched items, omitting any entries that could not be enriched.
///
public async Task> GetAll()
{
var result = await displayConfigRepository.GetAll();
var resultToReturn = new List();
foreach (var config in result)
{
var c = await AddDisplaySectionMinimal(config);
if (c != null) resultToReturn.Add(c);
}
return resultToReturn;
}
///
/// Retrieves a paginated list of display configurations in a compact form, including a flag indicating whether each configuration is currently in use.
/// Applies server-side pagination using the provided filter and maps each result to a enriched with its usage status.
///
/// The pagination filter containing the page number, page size, and any filtering criteria used to retrieve and paginate the display configurations.
/// A task that represents the asynchronous operation, containing a with the compact display configurations, current page metadata, and total document count.
///
public async Task> GetAllCompactPaginated(PaginationFilter filter)
{
var result = displayConfigRepository.GetAllPaginated(filter);
var count = await result.CountDocumentsAsync();
var resultToReturn = new List();
var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize)
.Limit(filter.PageSize)
.ToCursorAsync();
var dataList = await data.ToListAsync();
foreach (var config in dataList)
{
var isInUse = await displayService.Value.IsDisplayConfigInUse(config.Id);
resultToReturn.Add(new DisplayConfigMinimalResponse(config, isInUse));
}
return new PaginationResponse(resultToReturn, filter.PageNumber, filter.PageSize,
count);
}
///
/// Retrieves display configurations matching the specified display type and enriches each one with its minimal display section. Configurations for which the enrichment returns null are excluded from the result list.
///
/// The display type used to filter the configurations.
/// A list of enriched display configurations; entries whose display section could not be resolved are omitted.
///
public async Task> GetByType(DisplayConfigEnums.DisplayType type)
{
var result = await displayConfigRepository.GetByType(type);
var resultToReturn = new List();
foreach (var config in result)
{
var c = await AddDisplaySectionMinimal(config);
if (c != null) resultToReturn.Add(c);
}
return resultToReturn;
}
///
/// Retrieves a by its identifier and enriches it with minimal display section data.
/// Throws a if no configuration is found for the given id.
///
/// The unique identifier of the display configuration to retrieve.
/// The display configuration with the minimal display section applied.
/// Thrown when no display configuration exists for the specified .
///
public async Task GetById(ObjectId id)
{
return await AddDisplaySectionMinimal(await displayConfigRepository.GetById(id)) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
}
///
/// Retrieves a by its identifier, falling back to the default configuration for the given unit and display type when the identifier is not provided or not found. When both a configuration by id and a default configuration are found, the two are merged and the merged result is returned.
///
/// The optional configuration identifier. When null, only the default configuration is returned.
/// The unit identifier used to look up the default configuration.
/// The display type used to look up the default configuration.
/// The current configuration, the default configuration, the merged configuration, or null when neither is available.
///
public async Task GetById(ObjectId? configId, ObjectId unitId,
DisplayConfigEnums.DisplayType displayType)
{
if (configId.HasValue)
{
DisplayConfig? currentConfig;
DisplayConfig? defaultConfig;
try
{
currentConfig = await GetById(configId.Value);
}
catch (NotFoundException)
{
currentConfig = null;
}
try
{
defaultConfig = await GetDefaultByUnitIdAndType(unitId, displayType);
}
catch (NotFoundException)
{
defaultConfig = null;
}
if (defaultConfig != null && currentConfig != null) return currentConfig.MergeConfig(defaultConfig);
return defaultConfig ?? currentConfig;
}
return await GetDefaultByUnitIdAndType(unitId, displayType);
}
///
/// Inserts a new display configuration into the repository while recording an audit log entry for the operation.
///
/// The display configuration to insert.
/// The inserted display configuration, or null if no entity was returned by the repository.
///
public async Task InsertOne(DisplayConfig config)
{
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, config);
return await displayConfigRepository.InsertOneAsyncAndReturn(config);
}
///
/// Inserts a minimal display configuration, creating a instance when the type is and a base otherwise, while recording an audit log for the creation.
///
/// The DTO containing the hospital and display type used to build the new configuration entity.
/// The inserted entity, or null if the repository did not return a result.
///
public async Task InsertOneMinimal(CreateDisplayConfigDto config)
{
DisplayConfig newDisplayConfig;
if (config.Type == DisplayConfigEnums.DisplayType.DisplayNurse)
newDisplayConfig = new DisplayNurse
{
Hospital = config.Hospital,
Type = config.Type
};
else
newDisplayConfig = new DisplayConfig
{
Hospital = config.Hospital,
Type = config.Type
};
newDisplayConfig.Id = ObjectId.GenerateNewId();
var result = await displayConfigRepository.InsertOneAsyncAndReturn(newDisplayConfig);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, newDisplayConfig);
return result;
}
///
/// Inserts test records for the DisplayNurse and SmartDisplay display configuration types into the repository and returns the inserted DisplayNurse record.
///
/// The inserted instance of type DisplayNurse.
///
public async Task InsertOneTest()
{
var d = new DisplayNurse
{
Type = DisplayConfigEnums.DisplayType.DisplayNurse
};
await displayConfigRepository.InsertOneAsyncAndReturn(d);
var e = new SmartDisplay
{
Type = DisplayConfigEnums.DisplayType.SmartDisplay
};
await displayConfigRepository.InsertOneAsyncAndReturn(e);
return d;
}
///
/// Updates a display configuration, dispatching to a type-specific update path for or , broadcasting the change and writing an audit log when the update succeeds.
///
/// The identifier of the display configuration to update.
/// The new configuration payload, deserialized internally into the appropriate DTO based on its Type.
/// The updated when the corresponding update succeeds; otherwise the method throws.
/// Thrown when the configuration's Type is not handled, or when the underlying update returns no result.
///
public async Task UpdateConfig(ObjectId displayConfigId, object newDisplayConfig)
{
var baseType = JsonConvert.DeserializeObject(newDisplayConfig.ToString()!);
// var cardConfigUpdate = await UpdateDisplayCardConfig(displayConfigId, baseType);
var oldDisplayConfig = await displayConfigRepository.GetById(displayConfigId);
switch (baseType!.Type)
{
case DisplayConfigEnums.DisplayType.SmartDisplay:
var smartConfigToReturn = await displayConfigRepository.UpdateSmartDisplay(displayConfigId,
JsonConvert.DeserializeObject(newDisplayConfig.ToString()!));
if (smartConfigToReturn != null)
{
// smartConfigToReturn.CardConfig = cardConfigUpdate;
SendSmartDisplayConfigBroadcast(displayConfigId, smartConfigToReturn,
oldDisplayConfig as SmartDisplay);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplayConfig,
newDisplayConfig);
return smartConfigToReturn;
}
break;
case DisplayConfigEnums.DisplayType.DisplayNurse:
var obsNurseList = masterListServiceFactory.StringNurseObs();
var displayConfigUpdate =
JsonConvert.DeserializeObject(newDisplayConfig.ToString()!);
var nurseConfigToReturn =
await displayConfigRepository.UpdateDisplayNurse(displayConfigId, displayConfigUpdate,
obsNurseList);
if (nurseConfigToReturn != null)
{
// nurseConfigToReturn.CardConfig = cardConfigUpdate;
SendDisplayConfigBroadcast(displayConfigId, OperationType.UpdateDisplayConfig, nurseConfigToReturn);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplayConfig,
nurseConfigToReturn);
return nurseConfigToReturn;
}
break;
default:
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundNoMatches);
}
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
}
///
/// Updates the list of fields associated with a display configuration identified by the specified object ID.
///
/// The unique identifier of the display configuration whose field list is being updated.
/// The collection of fields to be associated with the display configuration.
/// A task that represents the asynchronous operation, containing a boolean value indicating whether the update was successful.
///
public Task UpdateFieldList(ObjectId objectIdConfigDisplay, List fields)
{
return displayConfigRepository.UpdateFieldList(objectIdConfigDisplay, fields);
}
///
/// Updates the color configuration of a display, initializing an empty color configuration when none exists, and records an audit log of the change.
///
/// The identifier of the display configuration to update.
/// The new color configuration to apply to the display.
/// True if the color configuration was updated successfully; otherwise, false.
/// Thrown when the display configuration identified by cannot be found before or after the update.
///
public async Task UpdateConfigColor(ObjectId objectIdConfigDisplay, ColorConfig colorConfig)
{
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
if (oldDisplayConfig.ColorConfig == null)
{
oldDisplayConfig.ColorConfig = new ColorConfig();
await displayConfigRepository.UpdateOneAsync(objectIdConfigDisplay, oldDisplayConfig);
}
var result = await displayConfigRepository.UpdateConfigColor(objectIdConfigDisplay, colorConfig);
var newDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplayConfig,
newDisplayConfig);
return result;
}
///
/// Updates the header configuration of a display configuration record and creates an audit log entry
/// capturing the previous and updated values.
///
/// The unique identifier of the display configuration to update.
/// The new header configuration to apply to the display configuration.
/// A task that represents the asynchronous operation. The task result is true if the update was successful; otherwise, false.
/// Thrown when the display configuration is not found before the update, or when the updated display configuration cannot be retrieved afterwards.
///
public async Task UpdateHeaderConfig(ObjectId objectIdConfigDisplay, HeaderConfig headerConfig)
{
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var result = await displayConfigRepository.UpdateHeaderConfig(objectIdConfigDisplay, headerConfig);
var newDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplayConfig,
newDisplayConfig);
return result;
}
///
/// Updates the home banner configuration for the specified display configuration and records an audit log comparing the old and new states.
///
/// The identifier of the display configuration to update.
/// The list of banner items to set for the home banner.
/// A task that resolves to true if the update succeeds; otherwise, false.
/// Thrown when the display configuration cannot be found before or after the update.
///
public async Task UpdateSetHomeBanner(ObjectId objectIdConfigDisplay, List bannerItems)
{
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var result = await displayConfigRepository.UpdateSetHomeBanner(objectIdConfigDisplay, bannerItems);
var newDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplayConfig,
newDisplayConfig);
return result;
}
///
/// Updates the base display configuration and records an audit log comparing the previous and updated configurations.
///
/// The display configuration containing the updated values, identified by its .
/// A task that represents the asynchronous operation. The result indicates whether the update was successful.
/// Thrown when the display configuration with the specified identifier does not exist before or after the update.
///
public async Task UpdateBaseConfig(DisplayConfig baseConfig)
{
var oldDisplayConfig = await displayConfigRepository.GetById(baseConfig.Id) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var result = await displayConfigRepository.UpdateBaseConfig(baseConfig.Id, baseConfig);
var newDisplayConfig = await displayConfigRepository.GetById(baseConfig.Id) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplayConfig,
newDisplayConfig);
return result;
}
///
/// Updates the hospital name of an existing display configuration and records an audit log entry comparing the previous and updated values.
///
/// The identifier of the display configuration to update.
/// The new hospital name to apply to the display configuration.
/// A task that resolves to true if the update was successful; otherwise, false.
/// Thrown when the display configuration identified by cannot be found before or after the update.
///
public async Task UpdateDisplayConfigHospitalName(ObjectId objectIdConfigDisplay, string name)
{
var oldDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
var result = await displayConfigRepository.UpdateDisplayConfigHospitalName(objectIdConfigDisplay, name);
var newDisplayConfig = await displayConfigRepository.GetById(objectIdConfigDisplay) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldDisplayConfig,
newDisplayConfig);
return result;
}
///
/// Deletes a display configuration by its identifier. When displays are still associated with the configuration, they are reassigned to a default configuration for the same type before deletion, and an audit log entry is recorded on success.
///
/// The identifier of the display configuration to delete.
/// A task that resolves to true if the configuration was successfully deleted; otherwise, false.
/// Thrown when no default configuration is found for the related display type, or when reassigning a display to the default configuration fails.
///
public async Task DeleteDisplayConfig(ObjectId objectIdConfigDisplay)
{
var displays = await displayService.Value.GetByConfigId(objectIdConfigDisplay);
if (displays.Count > 0)
{
var type = displays.First().Type;
var defaultConfig = await GetDefaultConfig(type) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
foreach (var display in displays)
_ = await displayService.Value.UpdateConfigId(display, defaultConfig.Id) ??
throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
}
var result = await displayConfigRepository.DeleteDisplayConfig(objectIdConfigDisplay);
if (result != null)
{
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, result, null);
return true;
}
return false;
}
///
/// Retrieves the list of display configuration locations associated with the specified configuration display identifier.
///
/// The identifier of the configuration display whose locations are being retrieved.
/// A task that returns a list of items for the given configuration display.
///
public async Task> GetDisplayConfigLocations(ObjectId objectIdConfigDisplay)
{
return await displayService.Value.GetDisplayConfigLocations(objectIdConfigDisplay);
}
///
/// Asynchronously retrieves a compact list of all display configurations from the repository.
///
/// A task that represents the asynchronous operation, containing a list of objects representing the compact display configuration data.
///
public async Task> GetAllCompact()
{
return await displayConfigRepository.GetAllCompact();
}
///
/// Creates a new display configuration by cloning an existing template identified by its object ID, applying the specified hospital and display type, and inserting it into the data store. Returns null when the object ID cannot be parsed, the template cannot be retrieved, the retrieved template does not match the requested display type, or the display type is not one of the handled cases.
///
/// The object ID of the existing template used as the base for the new configuration.
/// The display type of the configuration to create; determines which template subtype is expected and produced.
/// The hospital to associate with the newly created configuration.
/// The inserted when the template is found and matches the requested type; otherwise, null.
///
public async Task InsertOneWithTemplate(string objectId,
DisplayConfigEnums.DisplayType configType, string? configHospital)
{
if (ObjectId.TryParse(objectId, out var objectIdConfigDisplay))
{
var template = await GetById(objectIdConfigDisplay);
switch (configType)
{
case DisplayConfigEnums.DisplayType.StandarDisplay:
if (template is StandarDisplay standardTemplate)
{
var standarConfigg = new StandarDisplay
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.StandarDisplay };
standarConfigg.MergeConfig(standardTemplate);
await InsertOne(standarConfigg);
return standarConfigg;
}
break;
case DisplayConfigEnums.DisplayType.DisplayNurse:
if (template is DisplayNurse nurseTemplate)
{
var nurseConfig = new DisplayNurse
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.DisplayNurse };
nurseConfig.MergeConfig(nurseTemplate);
await InsertOne(nurseConfig);
return nurseConfig;
}
break;
case DisplayConfigEnums.DisplayType.SmartDisplay:
if (template is SmartDisplay smartTemplate)
{
var smartConfig = new SmartDisplay
{ Hospital = configHospital, Type = DisplayConfigEnums.DisplayType.DisplayNurse };
smartConfig.MergeConfig(smartTemplate);
await InsertOne(smartConfig);
return smartConfig;
}
break;
default:
return null;
}
}
return null;
}
///
/// Updates an existing card configuration. When the update is successful, propagates the new configuration to related rotating display configs by refreshing their nurse data, and broadcasts the card display config update to all associated displays.
///
/// The card configuration to update.
/// True if the update was applied (repository reported changes); otherwise, false.
///
public async Task UpdateCardConfig(CardConfig baseConfig)
{
var result = await displayCardConfigRepository.UpdateOne(baseConfig);
if (result.Changes > 0)
{
var displayConfigs = await displayConfigRepository.GetAllByCardConfigIdAndRotating(baseConfig.Id);
foreach (var displayConfig in displayConfigs)
{
await displayConfigRepository.UpdateDisplayNurse(displayConfig,
new DisplayNurseDto() { CardConfig = result.Data }, masterListServiceFactory.StringNurseObs());
}
var displays = await displayConfigRepository.GetAllByCardConfigId(baseConfig.Id);
foreach (var display in displays)
SendDisplayConfigBroadcast(display, OperationType.UpdateCardDisplayConfig, result.Data);
return true;
}
return false;
}
///
/// Updates an existing card detail configuration and propagates the change by broadcasting an update event to all related displays.
/// Returns true if the update modified at least one record, otherwise false.
///
/// The card detail configuration to update, identified by its Id.
/// A task that resolves to true when the update affected one or more records; false when no changes were made.
///
public async Task UpdateDetailConfig(CardDetailsConfig baseConfig)
{
var result = await displayDetailConfigRepository.UpdateOne(baseConfig);
var displays = await displayConfigRepository.GetAllByCardDetailConfigId(baseConfig.Id);
foreach (var display in displays)
SendDisplayConfigBroadcast(display, OperationType.UpdateDetailDisplayConfig, result.Data);
if (result.Changes > 0) return true;
return false;
}
///
/// Updates an existing chart configuration in the repository and returns whether the operation modified any records.
///
/// The chart configuration to be updated.
/// A task that resolves to true if the update changed at least one record; otherwise, false.
///
public async Task UpdateChartConfig(ChartConfig baseConfig)
{
var result = await displayChartRepository.UpdateOne(baseConfig);
if (result.Changes > 0) return true;
return false;
}
///
/// Deletes the chart configuration identified by the given display object ID.
/// If the repository deletion succeeds, the deleted chart configuration is updated and the method returns true; otherwise, it returns false.
///
/// The object ID of the chart configuration display to delete.
/// true if the chart configuration was successfully deleted; false if no matching configuration was found.
///
public async Task DeleteChartConfig(ObjectId objectIdConfigDisplay)
{
var res = await displayChartRepository.DeleteOne(objectIdConfigDisplay);
if (res != null)
{
await UpdateDeletedChartConfig(objectIdConfigDisplay);
return true;
}
return false;
}
///
/// Retrieves a chart configuration by its unique identifier from the display chart repository.
///
/// The unique identifier of the chart configuration to retrieve.
/// A task that represents the asynchronous operation. The task result contains the if found; otherwise, null.
///
public async Task GetChartConfig(ObjectId objectIdConfigChart)
{
return await displayChartRepository.GetById(objectIdConfigChart);
}
///
/// Inserts a new card detail configuration and optionally links it to an existing display configuration, broadcasting the change when the link is successfully established.
///
/// The DTO containing the detail configuration to insert and, optionally, the ID of the display configuration to associate it with.
/// The newly inserted , or null when no detail configuration is provided in the DTO.
///
public async Task InsertCardDetailConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto)
{
if (updateDisplayConfigNameDto.DetailConfig == null) return null;
var result =
await displayDetailConfigRepository.InsertOneAsyncAndReturn(updateDisplayConfigNameDto.DetailConfig);
if (updateDisplayConfigNameDto.DisplayConfigId != null && result != null)
{
var res = await UpdateDetailConfigId(updateDisplayConfigNameDto.DisplayConfigId, result.Id);
if (res)
SendDisplayConfigBroadcast(updateDisplayConfigNameDto.DisplayConfigId.Value,
OperationType.UpdateDetailDisplayConfig, result);
}
return result;
}
///
/// Inserts a new chart configuration and, when a display configuration identifier is provided, links the inserted chart to that display configuration and broadcasts the update.
///
/// The data transfer object containing the chart configuration to insert and, optionally, the target display configuration identifier.
/// The inserted , or null if the supplied chart configuration is null.
///
public async Task InsertChartConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto)
{
if (updateDisplayConfigNameDto.ChartConfig == null) return null;
var result = await displayChartRepository.InsertOneAsyncAndReturn(updateDisplayConfigNameDto.ChartConfig);
if (updateDisplayConfigNameDto.DisplayConfigId != null && result != null)
{
var res = await AddChartId(updateDisplayConfigNameDto.DisplayConfigId, result.Id);
if (res)
SendDisplayConfigBroadcast(updateDisplayConfigNameDto.DisplayConfigId.Value,
OperationType.UpdateChartConfig, result);
}
return result;
}
///
/// Inserts a new card configuration and optionally links it to an existing display configuration by updating the card config id and broadcasting the change.
///
/// The DTO containing the card configuration to insert and, optionally, the display configuration id to associate it with.
/// The inserted , or null when the provided card configuration is null.
///
public async Task InsertCardConfig(CreateDisplayConfigCardDto updateDisplayConfigNameDto)
{
if (updateDisplayConfigNameDto.CardConfig == null) return null;
var result = await displayCardConfigRepository.InsertOneAsyncAndReturn(updateDisplayConfigNameDto.CardConfig);
if (updateDisplayConfigNameDto.DisplayConfigId != null && result != null)
{
var res = await UpdateCardConfigId(updateDisplayConfigNameDto.DisplayConfigId, result.Id);
if (res)
SendDisplayConfigBroadcast(updateDisplayConfigNameDto.DisplayConfigId.Value,
OperationType.UpdateDetailDisplayConfig, result);
}
return result;
}
///
/// Asynchronously retrieves all card configurations from the display card config repository.
///
/// A task that represents the asynchronous operation, containing a list of all entities.
///
public async Task> GetCardConfigAll()
{
return await displayCardConfigRepository.GetAll();
}
///
/// Retrieves a card configuration by its unique identifier from the display card configuration repository.
///
/// The unique identifier of the card configuration to retrieve.
/// The matching if found; otherwise, null.
///
public async Task GetCardConfigById(ObjectId id)
{
return await displayCardConfigRepository.GetById(id);
}
///
/// Retrieves the default for the specified display type by delegating to the underlying repository.
/// Returns when no default configuration exists for the given type.
///
/// The display type used to look up the default configuration.
/// A representing the default configuration for the specified type, or if no default is found.
///
public async Task GetDefaultConfig(DisplayConfigEnums.DisplayType type)
{
return await displayConfigRepository.GetDefault(type);
}
///
/// Retrieves the default for the specified unit and display type by delegating to the repository.
/// Returns null when no matching default configuration exists, as the not-found exception is currently commented out.
///
/// The identifier of the unit whose default display configuration is being requested.
/// The display type used to filter the default configuration lookup.
/// A instance if a default is found; otherwise, null.
///
private async Task GetDefaultByUnitIdAndType(ObjectId unitId,
DisplayConfigEnums.DisplayType displayType)
{
return
await displayConfigRepository.GetDefaultByUnitIdAndType(unitId,
displayType); //?? throw new NotFoundException(ErrorMessage.NotFound_ResourceMissing);
}
///
/// Enriches the given with minimal display section information when it is a SmartDisplay and has associated display section IDs, and records an audit log for the change.
///
/// The display configuration to augment with minimal display section data, or .
/// The updated , or if the input was .
/// Thrown when the display configuration cannot be found in the repository by its identifier.
///
private async Task AddDisplaySectionMinimal(DisplayConfig? displayConfig)
{
if (displayConfig == null) return null;
var displayConfigAux = displayConfigRepository.GetById(displayConfig.Id) ??
throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
if (displayConfig.Type == DisplayConfigEnums.DisplayType.SmartDisplay &&
!displayConfig.DisplaySectionIdList.IsNullOrEmpty())
{
foreach (var displayId in displayConfig.DisplaySectionIdList)
{
var d = await displayService.Value.GetById(displayId);
if (d != null)
{
var minimalDisplay = new MinimalDisplaySection
{
Id = displayId,
Name = d.Name
};
displayConfig.DisplaySectionList.Add(minimalDisplay);
}
}
await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, displayConfigAux,
displayConfig);
}
return displayConfig;
}
///
/// Updates the chart configuration to mark the specified entry as deleted by delegating to the display configuration repository.
///
/// The identifier of the chart configuration entry to mark as deleted.
///
private async Task UpdateDeletedChartConfig(ObjectId deletedId)
{
await displayConfigRepository.UpdateDeletedChartConfig(deletedId);
}
///
/// Adds a chart ID to the specified display configuration by delegating the operation to the display configuration repository.
///
/// The identifier of the display configuration to which the chart ID will be associated. May be null.
/// The identifier of the chart result to add to the display configuration.
/// A task that represents the asynchronous operation. The task result contains a boolean indicating whether the chart ID was successfully added.
///
private async Task AddChartId(ObjectId? displayConfigId, ObjectId resultId)
{
var result = await displayConfigRepository.AddChartId(displayConfigId, resultId);
return result;
}
///
/// Updates the card configuration identifier by delegating the operation to the display configuration repository.
///
/// The identifier of the display configuration to update, or null to skip.
/// The identifier of the result to associate with the card configuration, or null to skip.
/// A task that resolves to true if the update was successful; otherwise, false.
///
private async Task UpdateCardConfigId(ObjectId? displayConfigId, ObjectId? resultId)
{
var result = await displayConfigRepository.UpdateCardConfigId(displayConfigId, resultId);
return result;
}
///
/// Updates the detail configuration identifier for the specified result by delegating the operation to the display configuration repository.
///
/// The display configuration identifier to associate with the result, or null if not specified.
/// The result identifier whose detail configuration should be updated, or null if not specified.
/// A task that resolves to true if the update was successful; otherwise, false.
///
private async Task UpdateDetailConfigId(ObjectId? displayConfigId, ObjectId? resultId)
{
var result = await displayConfigRepository.UpdateDetailConfigId(displayConfigId, resultId);
return result;
}
///
/// Broadcasts a display configuration change to all subscribers whose display is linked to the given configuration.
/// Looks up displays by the configuration id, filters subscribers matching those display ids, and sends the operation and new configuration to each subscriber asynchronously.
/// Logs and swallows any errors that occur while sending the broadcast.
///
/// The identifier of the display configuration whose change should be broadcast to related subscribers.
/// The type of operation performed on the configuration (e.g., create, update, delete) to convey to subscribers.
/// The new display configuration payload to send to subscribers, or null if not applicable for the operation.
///
private async void SendDisplayConfigBroadcast(ObjectId displayConfigId, OperationType operationType,
object? newDisplayConfig)
{
try
{
var listDisplayId = await displayService.Value.GetByConfigId(displayConfigId);
var listDisplayIdList = listDisplayId.Select(c => c.Id).ToList();
var subscribers = subscribersService.GetSubscribers().Where(s =>
s.DisplayId != null && listDisplayIdList.Contains((ObjectId)s.DisplayId)).ToList();
foreach (var sub in subscribers)
_ = clientMessageService.SendAsync(sub.Id, operationType,
newDisplayConfig);
}
catch (Exception e)
{
Log.Error("Error sending update for DisplayNurse config: {message}", e.Message);
//throw new ConflictException(ErrorMessage.Conflict_UpdateFailed, e);
}
}
///
/// Broadcasts a SmartDisplay configuration update to the relevant subscribers and notifies all clients of the latest display configuration.
/// When both the new and old configurations are provided, the update is dispatched to the matching subscribers; otherwise, an error is logged.
/// Regardless of the outcome, a global update message is sent to all clients so they can refresh the display configuration.
///
/// The identifier of the display configuration used to look up the associated displays and to broadcast the global update.
/// The new SmartDisplay configuration to propagate to subscribers, or null when not available.
/// The previous SmartDisplay configuration used to build the update payload, or null when not available.
///
private async void SendSmartDisplayConfigBroadcast(ObjectId displayConfigId, SmartDisplay? newDisplayConfig,
SmartDisplay? oldDisplayConfig)
{
try
{
var listDisplayId = await displayService.Value.GetByConfigId(displayConfigId);
var listDisplayIdList = listDisplayId.Select(c => c.Id).ToList();
var subscribers = subscribersService.GetSubscribers().Where(s =>
s.DisplayId != null && listDisplayIdList.Contains((ObjectId)s.DisplayId)).ToList();
if (newDisplayConfig != null && oldDisplayConfig != null)
SendSmartDisplayConfigUpdate(subscribers, oldDisplayConfig, newDisplayConfig);
else Log.Error("Error sending update for SmartDisplay config config is null");
}
catch (Exception e)
{
Log.Error("Error sending update for SmartDisplay config: {message}", e.Message);
//throw new ConflictException(ErrorMessage.Conflict_UpdateFailed, e);
}
finally
{
// Enviar un mensaje a todos los clientes para que actualicen la configuraci�n del display
var displayConfig = await displayConfigRepository.GetById(displayConfigId);
if (displayConfig != null)
_ = clientMessageService.SendToAllAsync(OperationType.UpdateDisplayConfig, displayConfig);
}
}
///
/// Sends updated display configuration values to all WebSocket subscribers for each property that has changed between the old and new configurations. If the old configuration is null, no update messages are sent.
///
/// The list of WebSocket subscribers that will receive the display configuration update messages.
/// The previous smart display configuration, or null if there is no prior configuration to compare against.
/// The new smart display configuration whose values will be sent to subscribers.
///
private void SendSmartDisplayConfigUpdate(List subscribers,
SmartDisplay? oldDisplayDisplayConfig, SmartDisplay? newDisplayDisplayConfig)
{
// Obtener las propiedades que han cambiado
var differentProperties = oldDisplayDisplayConfig?.GetDifferentProperties(newDisplayDisplayConfig);
// Enviar un mensaje a los clientes por cada propiedad que haya cambiado
if (differentProperties != null)
foreach (var property in differentProperties)
foreach (var sub in subscribers)
_ = clientMessageService.SendAsync(sub.Id, OperationType.UpdateDisplayConfig,
newDisplayDisplayConfig?.GetType().GetProperty(property)?.GetValue(newDisplayDisplayConfig));
}
}