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;

public class DisplayConfigService(
    IDisplayConfigRepository displayConfigRepository,
    Lazy<IDisplayService> displayService,
    ISubscribersService subscribersService,
    IClientMessageService clientMessageService,
    IHttpContextAccessor httpContextAccessor,
    ILocalAuditService auditService,
    IMasterListServiceFactory masterListServiceFactory,
    IDisplayCardConfigRepository displayCardConfigRepository,
    IDisplayDetailConfigRepository displayDetailConfigRepository,
    IDisplayChartConfigRepository displayChartRepository)
    : IDisplayConfigService
{
    /// <summary>
    /// 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.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation. The task result contains a list of enriched <see cref="DisplayConfig"/> items, omitting any entries that could not be enriched.</returns>
    public async Task<List<DisplayConfig>> GetAll()
    {
        var result = await displayConfigRepository.GetAll();
        var resultToReturn = new List<DisplayConfig>();
        foreach (var config in result)
        {
            var c = await AddDisplaySectionMinimal(config);
            if (c != null) resultToReturn.Add(c);
        }

        return resultToReturn;
    }

    /// <summary>
    /// 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 <see cref="DisplayConfigMinimalResponse"/> enriched with its usage status.
    /// </summary>
    /// <param name="filter">The pagination filter containing the page number, page size, and any filtering criteria used to retrieve and paginate the display configurations.</param>
    /// <returns>A task that represents the asynchronous operation, containing a <see cref="PaginationResponse{T}"/> with the compact display configurations, current page metadata, and total document count.</returns>
    public async Task<PaginationResponse<DisplayConfigMinimalResponse>> GetAllCompactPaginated(PaginationFilter filter)
    {
        var result = displayConfigRepository.GetAllPaginated(filter);
        var count = await result.CountDocumentsAsync();
        var resultToReturn = new List<DisplayConfigMinimalResponse>();

        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<DisplayConfigMinimalResponse>(resultToReturn, filter.PageNumber, filter.PageSize,
            count);
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="type">The display type used to filter the configurations.</param>
    /// <returns>A list of enriched display configurations; entries whose display section could not be resolved are omitted.</returns>
    public async Task<List<DisplayConfig>> GetByType(DisplayConfigEnums.DisplayType type)
    {
        var result = await displayConfigRepository.GetByType(type);
        var resultToReturn = new List<DisplayConfig>();
        foreach (var config in result)
        {
            var c = await AddDisplaySectionMinimal(config);
            if (c != null) resultToReturn.Add(c);
        }

        return resultToReturn;
    }

    /// <summary>
    /// Retrieves a <see cref="DisplayConfig"/> by its identifier and enriches it with minimal display section data.
    /// Throws a <see cref="NotFoundException"/> if no configuration is found for the given id.
    /// </summary>
    /// <param name="id">The unique identifier of the display configuration to retrieve.</param>
    /// <returns>The display configuration with the minimal display section applied.</returns>
    /// <exception cref="NotFoundException">Thrown when no display configuration exists for the specified <paramref name="id"/>.</exception>
    public async Task<DisplayConfig> GetById(ObjectId id)
    {
        return await AddDisplaySectionMinimal(await displayConfigRepository.GetById(id)) ??
               throw new NotFoundException(HttpEnum.ErrorMessage.NotFoundResourceMissing);
    }

    /// <summary>
    /// Retrieves a <see cref="DisplayConfig"/> 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.
    /// </summary>
    /// <param name="configId">The optional configuration identifier. When null, only the default configuration is returned.</param>
    /// <param name="unitId">The unit identifier used to look up the default configuration.</param>
    /// <param name="displayType">The display type used to look up the default configuration.</param>
    /// <returns>The current configuration, the default configuration, the merged configuration, or null when neither is available.</returns>
    public async Task<DisplayConfig?> 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);
    }

    /// <summary>
    /// Inserts a new display configuration into the repository while recording an audit log entry for the operation.
    /// </summary>
    /// <param name="config">The display configuration to insert.</param>
    /// <returns>The inserted display configuration, or null if no entity was returned by the repository.</returns>
    public async Task<DisplayConfig?> InsertOne(DisplayConfig config)
    {
        await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, config);
        return await displayConfigRepository.InsertOneAsyncAndReturn(config);
    }

    /// <summary>
    /// Inserts a minimal display configuration, creating a <see cref="DisplayNurse"/> instance when the type is <see cref="DisplayConfigEnums.DisplayType.DisplayNurse"/> and a base <see cref="DisplayConfig"/> otherwise, while recording an audit log for the creation.
    /// </summary>
    /// <param name="config">The DTO containing the hospital and display type used to build the new configuration entity.</param>
    /// <returns>The inserted <see cref="DisplayConfig"/> entity, or <c>null</c> if the repository did not return a result.</returns>
    public async Task<DisplayConfig?> 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;
    }

    /// <summary>
    /// Inserts test records for the DisplayNurse and SmartDisplay display configuration types into the repository and returns the inserted DisplayNurse record.
    /// </summary>
    /// <returns>The inserted <see cref="DisplayConfig"/> instance of type DisplayNurse.</returns>
    public async Task<DisplayConfig> 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;
    }

    /// <summary>
    /// Updates a display configuration, dispatching to a type-specific update path for <see cref="DisplayConfigEnums.DisplayType.SmartDisplay"/> or <see cref="DisplayConfigEnums.DisplayType.DisplayNurse"/>, broadcasting the change and writing an audit log when the update succeeds.
    /// </summary>
    /// <param name="displayConfigId">The identifier of the display configuration to update.</param>
    /// <param name="newDisplayConfig">The new configuration payload, deserialized internally into the appropriate DTO based on its <c>Type</c>.</param>
    /// <returns>The updated <see cref="DisplayConfig"/> when the corresponding update succeeds; otherwise the method throws.</returns>
    /// <exception cref="NotFoundException">Thrown when the configuration's <c>Type</c> is not handled, or when the underlying update returns no result.</exception>
    public async Task<DisplayConfig?> UpdateConfig(ObjectId displayConfigId, object newDisplayConfig)
    {
        var baseType = JsonConvert.DeserializeObject<DisplayConfigDto>(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<SmartDisplay>(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<DisplayNurseDto>(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);
    }

    /// <summary>
    /// Updates the list of fields associated with a display configuration identified by the specified object ID.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The unique identifier of the display configuration whose field list is being updated.</param>
    /// <param name="fields">The collection of fields to be associated with the display configuration.</param>
    /// <returns>A task that represents the asynchronous operation, containing a boolean value indicating whether the update was successful.</returns>
    public Task<bool> UpdateFieldList(ObjectId objectIdConfigDisplay, List<Field> fields)
    {
        return displayConfigRepository.UpdateFieldList(objectIdConfigDisplay, fields);
    }

    /// <summary>
    /// Updates the color configuration of a display, initializing an empty color configuration when none exists, and records an audit log of the change.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The identifier of the display configuration to update.</param>
    /// <param name="colorConfig">The new color configuration to apply to the display.</param>
    /// <returns>True if the color configuration was updated successfully; otherwise, false.</returns>
    /// <exception cref="NotFoundException">Thrown when the display configuration identified by <paramref name="objectIdConfigDisplay"/> cannot be found before or after the update.</exception>
    public async Task<bool> 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;
    }

    /// <summary>
    /// Updates the header configuration of a display configuration record and creates an audit log entry
    /// capturing the previous and updated values.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The unique identifier of the display configuration to update.</param>
    /// <param name="headerConfig">The new header configuration to apply to the display configuration.</param>
    /// <returns>A task that represents the asynchronous operation. The task result is <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
    /// <exception cref="NotFoundException">Thrown when the display configuration is not found before the update, or when the updated display configuration cannot be retrieved afterwards.</exception>
    public async Task<bool> 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;
    }

    /// <summary>
    /// Updates the home banner configuration for the specified display configuration and records an audit log comparing the old and new states.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The identifier of the display configuration to update.</param>
    /// <param name="bannerItems">The list of banner items to set for the home banner.</param>
    /// <returns>A task that resolves to <c>true</c> if the update succeeds; otherwise, <c>false</c>.</returns>
    /// <exception cref="NotFoundException">Thrown when the display configuration cannot be found before or after the update.</exception>
    public async Task<bool> UpdateSetHomeBanner(ObjectId objectIdConfigDisplay, List<BannerItem> 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;
    }

    /// <summary>
    /// Updates the base display configuration and records an audit log comparing the previous and updated configurations.
    /// </summary>
    /// <param name="baseConfig">The display configuration containing the updated values, identified by its <see cref="DisplayConfig.Id"/>.</param>
    /// <returns>A task that represents the asynchronous operation. The result indicates whether the update was successful.</returns>
    /// <exception cref="NotFoundException">Thrown when the display configuration with the specified identifier does not exist before or after the update.</exception>
    public async Task<bool> 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;
    }

    /// <summary>
    /// Updates the hospital name of an existing display configuration and records an audit log entry comparing the previous and updated values.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The identifier of the display configuration to update.</param>
    /// <param name="name">The new hospital name to apply to the display configuration.</param>
    /// <returns>A task that resolves to <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
    /// <exception cref="NotFoundException">Thrown when the display configuration identified by <paramref name="objectIdConfigDisplay"/> cannot be found before or after the update.</exception>
    public async Task<bool> 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;
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The identifier of the display configuration to delete.</param>
    /// <returns>A task that resolves to <c>true</c> if the configuration was successfully deleted; otherwise, <c>false</c>.</returns>
    /// <exception cref="ConflictException">Thrown when no default configuration is found for the related display type, or when reassigning a display to the default configuration fails.</exception>
    public async Task<bool> 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;
    }

    /// <summary>
    /// Retrieves the list of display configuration locations associated with the specified configuration display identifier.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The identifier of the configuration display whose locations are being retrieved.</param>
    /// <returns>A task that returns a list of <see cref="DisplayConfigLocationDto"/> items for the given configuration display.</returns>
    public async Task<List<DisplayConfigLocationDto>> GetDisplayConfigLocations(ObjectId objectIdConfigDisplay)
    {
        return await displayService.Value.GetDisplayConfigLocations(objectIdConfigDisplay);
    }

    /// <summary>
    /// Asynchronously retrieves a compact list of all display configurations from the repository.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation, containing a list of <see cref="DisplayConfigMinimalResponse"/> objects representing the compact display configuration data.</returns>
    public async Task<List<DisplayConfigMinimalResponse>> GetAllCompact()
    {
        return await displayConfigRepository.GetAllCompact();
    }

    /// <summary>
    /// 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 <c>null</c> 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.
    /// </summary>
    /// <param name="objectId">The object ID of the existing template used as the base for the new configuration.</param>
    /// <param name="configType">The display type of the configuration to create; determines which template subtype is expected and produced.</param>
    /// <param name="configHospital">The hospital to associate with the newly created configuration.</param>
    /// <returns>The inserted <see cref="DisplayConfig"/> when the template is found and matches the requested type; otherwise, <c>null</c>.</returns>
    public async Task<DisplayConfig?> 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;
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="baseConfig">The card configuration to update.</param>
    /// <returns>True if the update was applied (repository reported changes); otherwise, false.</returns>
    public async Task<bool> 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;
    }

    /// <summary>
    /// Updates an existing card detail configuration and propagates the change by broadcasting an update event to all related displays.
    /// Returns <c>true</c> if the update modified at least one record, otherwise <c>false</c>.
    /// </summary>
    /// <param name="baseConfig">The card detail configuration to update, identified by its <c>Id</c>.</param>
    /// <returns>A task that resolves to <c>true</c> when the update affected one or more records; <c>false</c> when no changes were made.</returns>
    public async Task<bool> 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;
    }

    /// <summary>
    /// Updates an existing chart configuration in the repository and returns whether the operation modified any records.
    /// </summary>
    /// <param name="baseConfig">The chart configuration to be updated.</param>
    /// <returns>A task that resolves to <c>true</c> if the update changed at least one record; otherwise, <c>false</c>.</returns>
    public async Task<bool> UpdateChartConfig(ChartConfig baseConfig)
    {
        var result = await displayChartRepository.UpdateOne(baseConfig);
        if (result.Changes > 0) return true;
        return false;
    }

    /// <summary>
    /// 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 <c>true</c>; otherwise, it returns <c>false</c>.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The object ID of the chart configuration display to delete.</param>
    /// <returns><c>true</c> if the chart configuration was successfully deleted; <c>false</c> if no matching configuration was found.</returns>
    public async Task<bool> DeleteChartConfig(ObjectId objectIdConfigDisplay)
    {
        var res = await displayChartRepository.DeleteOne(objectIdConfigDisplay);
        if (res != null)
        {
            await UpdateDeletedChartConfig(objectIdConfigDisplay);
            return true;
        }

        return false;
    }

    /// <summary>
    /// Retrieves a chart configuration by its unique identifier from the display chart repository.
    /// </summary>
    /// <param name="objectIdConfigChart">The unique identifier of the chart configuration to retrieve.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="ChartConfig"/> if found; otherwise, <c>null</c>.</returns>
    public async Task<ChartConfig?> GetChartConfig(ObjectId objectIdConfigChart)
    {
        return await displayChartRepository.GetById(objectIdConfigChart);
    }

    /// <summary>
    /// Inserts a new card detail configuration and optionally links it to an existing display configuration, broadcasting the change when the link is successfully established.
    /// </summary>
    /// <param name="updateDisplayConfigNameDto">The DTO containing the detail configuration to insert and, optionally, the ID of the display configuration to associate it with.</param>
    /// <returns>The newly inserted <see cref="CardDetailsConfig"/>, or <c>null</c> when no detail configuration is provided in the DTO.</returns>
    public async Task<CardDetailsConfig?> 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;
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="updateDisplayConfigNameDto">The data transfer object containing the chart configuration to insert and, optionally, the target display configuration identifier.</param>
    /// <returns>The inserted <see cref="ChartConfig"/>, or <c>null</c> if the supplied chart configuration is <c>null</c>.</returns>
    public async Task<ChartConfig?> 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;
    }

    /// <summary>
    /// Inserts a new card configuration and optionally links it to an existing display configuration by updating the card config id and broadcasting the change.
    /// </summary>
    /// <param name="updateDisplayConfigNameDto">The DTO containing the card configuration to insert and, optionally, the display configuration id to associate it with.</param>
    /// <returns>The inserted <see cref="CardConfig"/>, or <c>null</c> when the provided card configuration is null.</returns>
    public async Task<CardConfig?> 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;
    }

    /// <summary>
    /// Asynchronously retrieves all card configurations from the display card config repository.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation, containing a list of all <see cref="CardConfig"/> entities.</returns>
    public async Task<List<CardConfig>> GetCardConfigAll()
    {
        return await displayCardConfigRepository.GetAll();
    }

    /// <summary>
    /// Retrieves a card configuration by its unique identifier from the display card configuration repository.
    /// </summary>
    /// <param name="id">The unique identifier of the card configuration to retrieve.</param>
    /// <returns>The matching <see cref="CardConfig"/> if found; otherwise, <c>null</c>.</returns>
    public async Task<CardConfig?> GetCardConfigById(ObjectId id)
    {
        return await displayCardConfigRepository.GetById(id);
    }

    /// <summary>
    /// Retrieves the default <see cref="DisplayConfig"/> for the specified display type by delegating to the underlying repository.
    /// Returns <see langword="null"/> when no default configuration exists for the given type.
    /// </summary>
    /// <param name="type">The display type used to look up the default configuration.</param>
    /// <returns>A <see cref="DisplayConfig"/> representing the default configuration for the specified type, or <see langword="null"/> if no default is found.</returns>
    public async Task<DisplayConfig?> GetDefaultConfig(DisplayConfigEnums.DisplayType type)
    {
        return await displayConfigRepository.GetDefault(type);
    }

    /// <summary>
    /// Retrieves the default <see cref="DisplayConfig"/> for the specified unit and display type by delegating to the repository.
    /// Returns <c>null</c> when no matching default configuration exists, as the not-found exception is currently commented out.
    /// </summary>
    /// <param name="unitId">The identifier of the unit whose default display configuration is being requested.</param>
    /// <param name="displayType">The display type used to filter the default configuration lookup.</param>
    /// <returns>A <see cref="DisplayConfig"/> instance if a default is found; otherwise, <c>null</c>.</returns>
    private async Task<DisplayConfig?> GetDefaultByUnitIdAndType(ObjectId unitId,
            DisplayConfigEnums.DisplayType displayType)
    {
        return
            await displayConfigRepository.GetDefaultByUnitIdAndType(unitId,
                displayType); //?? throw new NotFoundException(ErrorMessage.NotFound_ResourceMissing);
    }

    /// <summary>
    /// Enriches the given <see cref="DisplayConfig"/> with minimal display section information when it is a SmartDisplay and has associated display section IDs, and records an audit log for the change.
    /// </summary>
    /// <param name="displayConfig">The display configuration to augment with minimal display section data, or <see langword="null"/>.</param>
    /// <returns>The updated <see cref="DisplayConfig"/>, or <see langword="null"/> if the input was <see langword="null"/>.</returns>
    /// <exception cref="NotFoundException">Thrown when the display configuration cannot be found in the repository by its identifier.</exception>
    private async Task<DisplayConfig?> 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;
    }

    /// <summary>
    /// Updates the chart configuration to mark the specified entry as deleted by delegating to the display configuration repository.
    /// </summary>
    /// <param name="deletedId">The identifier of the chart configuration entry to mark as deleted.</param>
    private async Task UpdateDeletedChartConfig(ObjectId deletedId)
    {
        await displayConfigRepository.UpdateDeletedChartConfig(deletedId);
    }

    /// <summary>
    /// Adds a chart ID to the specified display configuration by delegating the operation to the display configuration repository.
    /// </summary>
    /// <param name="displayConfigId">The identifier of the display configuration to which the chart ID will be associated. May be null.</param>
    /// <param name="resultId">The identifier of the chart result to add to the display configuration.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains a boolean indicating whether the chart ID was successfully added.</returns>
    private async Task<bool> AddChartId(ObjectId? displayConfigId, ObjectId resultId)
    {
        var result = await displayConfigRepository.AddChartId(displayConfigId, resultId);
        return result;
    }

    /// <summary>
    /// Updates the card configuration identifier by delegating the operation to the display configuration repository.
    /// </summary>
    /// <param name="displayConfigId">The identifier of the display configuration to update, or null to skip.</param>
    /// <param name="resultId">The identifier of the result to associate with the card configuration, or null to skip.</param>
    /// <returns>A task that resolves to true if the update was successful; otherwise, false.</returns>
    private async Task<bool> UpdateCardConfigId(ObjectId? displayConfigId, ObjectId? resultId)
    {
        var result = await displayConfigRepository.UpdateCardConfigId(displayConfigId, resultId);
        return result;
    }

    /// <summary>
    /// Updates the detail configuration identifier for the specified result by delegating the operation to the display configuration repository.
    /// </summary>
    /// <param name="displayConfigId">The display configuration identifier to associate with the result, or <c>null</c> if not specified.</param>
    /// <param name="resultId">The result identifier whose detail configuration should be updated, or <c>null</c> if not specified.</param>
    /// <returns>A task that resolves to <c>true</c> if the update was successful; otherwise, <c>false</c>.</returns>
    private async Task<bool> UpdateDetailConfigId(ObjectId? displayConfigId, ObjectId? resultId)
    {
        var result = await displayConfigRepository.UpdateDetailConfigId(displayConfigId, resultId);
        return result;
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="displayConfigId">The identifier of the display configuration whose change should be broadcast to related subscribers.</param>
    /// <param name="operationType">The type of operation performed on the configuration (e.g., create, update, delete) to convey to subscribers.</param>
    /// <param name="newDisplayConfig">The new display configuration payload to send to subscribers, or null if not applicable for the operation.</param>
    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);
        }
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="displayConfigId">The identifier of the display configuration used to look up the associated displays and to broadcast the global update.</param>
    /// <param name="newDisplayConfig">The new SmartDisplay configuration to propagate to subscribers, or null when not available.</param>
    /// <param name="oldDisplayConfig">The previous SmartDisplay configuration used to build the update payload, or null when not available.</param>
    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);
        }
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="subscribers">The list of WebSocket subscribers that will receive the display configuration update messages.</param>
    /// <param name="oldDisplayDisplayConfig">The previous smart display configuration, or null if there is no prior configuration to compare against.</param>
    /// <param name="newDisplayDisplayConfig">The new smart display configuration whose values will be sent to subscribers.</param>
    private void SendSmartDisplayConfigUpdate(List<WsSubscriber> 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));
    }
}