using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
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.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Newtonsoft.Json;
using Serilog;
using System.Text.RegularExpressions;

namespace adas_core.Infrastructure.Repositories;

/// <summary>
/// Repository implementation for managing DisplayConfig entities in MongoDB.
/// Provides CRUD operations and specialized queries for display configurations.
/// </summary>
public class DisplayConfigRepository : MongoRepository<DisplayConfig>, IDisplayConfigRepository
{
    private readonly ApiSettings _apiSettings;
    private readonly ILogger<DisplayConfigRepository> _logger;
    private readonly IUnitRepository _unitRepository;

    /// <summary>
    /// Initializes a new instance of the DisplayConfigRepository.
    /// </summary>
    /// <param name="database">The MongoDB database instance.</param>
    /// <param name="apiSettings">API settings containing collection names configuration.</param>
    /// <param name="logger">Logger for repository operations.</param>
    /// <param name="unitRepository">Repository for unit-related operations.</param>
    public DisplayConfigRepository(
        IMongoDatabase database,
        ApiSettings apiSettings,
        ILogger<DisplayConfigRepository> logger,
        IUnitRepository unitRepository) : base(database)
    {
        _apiSettings = apiSettings;
        _logger = logger;
        _unitRepository = unitRepository;
    }

    /// <summary>
    /// Gets the name of the collection for display configurations.
    /// </summary>
    /// <returns>The collection name from API settings.</returns>
    public override string GetCollectionName()
    {
        return _apiSettings.DisplaysConfig;
    }

    /// <summary>
    /// Retrieves all display configurations from the database.
    /// </summary>
    /// <returns>A list of all DisplayConfig entities.</returns>
    public async Task<List<DisplayConfig>> GetAll()
    {
        var result = await Collection.Find(Builders<DisplayConfig>.Filter.Empty).ToListAsync();
        return result;
    }

    /// <summary>
    /// Retrieves paginated display configurations with optional filtering.
    /// </summary>
    /// <param name="filter">The pagination and filtering parameters.</param>
    /// <returns>A fluent queryable for DisplayConfigSummary results.</returns>
    /// <exception cref="BadRequestException">Thrown when the text filter exceeds 100 characters.</exception>
    public IFindFluent<DisplayConfig, DisplayConfigSummary> GetAllPaginated(PaginationFilter filter)
    {
        var filterBuilder = Builders<DisplayConfig>.Filter;
        var sort = Builders<DisplayConfig>.Sort.Ascending("hospital");
        var filters = new List<FilterDefinition<DisplayConfig>>();

        if (filter.FilteredRequest == null)
            return CreateFindFluentMinimal(filters, sort);

        if (!string.IsNullOrWhiteSpace(filter.FilteredRequest.Text))
        {
            var textFilter = filter.FilteredRequest.Text;

            if (textFilter.Length > 100)
                throw new BadRequestException("Text filter too long");

            var textFilterEscaped = Regex.Escape(textFilter);

            var orFilters = new List<FilterDefinition<DisplayConfig>>
        {
            filterBuilder.Regex(p => p.Hospital, new BsonRegularExpression(textFilterEscaped, "i"))
        };

            // búsqueda por Id solo si es válido
            if (ObjectId.TryParse(textFilter, out var id))
            {
                orFilters.Add(filterBuilder.Eq("_id", id));
            }

            filters.Add(filterBuilder.Or(orFilters));
        }

        if (filter.FilteredRequest.DisplayType != null)
        {
            filters.Add(filterBuilder.Eq(d => d.Type, filter.FilteredRequest.DisplayType));
        }

        return CreateFindFluentMinimal(filters, sort);
    }

    /// <summary>
    /// Retrieves all display configurations of a specific type.
    /// </summary>
    /// <param name="type">The display type to filter by.</param>
    /// <returns>A list of DisplayConfig entities matching the specified type.</returns>
    public async Task<List<DisplayConfig>> GetByType(DisplayConfigEnums.DisplayType type)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(p => p.Type, type);
        var result = await Collection.Find(filter).ToListAsync();
        return result;
    }

    /// <summary>
    /// Retrieves a display configuration by its ID with related configurations populated.
    /// Performs aggregation to include CardConfig, DetailConfig, ChartConfig, and rotating layout data.
    /// </summary>
    /// <param name="id">The ObjectId of the display configuration to retrieve.</param>
    /// <returns>The DisplayConfig with related data, or null if not found.</returns>
    public async Task<DisplayConfig?> GetById(ObjectId id)
    {
        try
        {
            var aggregate = Collection.Aggregate()
                .Match(Builders<DisplayConfig>.Filter.Eq(p => p.Id, id))

                // CardConfig
                .Lookup(
                    _apiSettings.DisplayCardConfig,
                    "cardConfigId",
                    "_id",
                    "cardConfig"
                )
                .Unwind("cardConfig", new AggregateUnwindOptions<BsonDocument>
                {
                    PreserveNullAndEmptyArrays = true
                })

                // DetailConfig
                .Lookup(
                    _apiSettings.DisplayDetailConfig,
                    "detailConfigId",
                    "_id",
                    "detailConfig"
                )
                .Unwind("detailConfig", new AggregateUnwindOptions<BsonDocument>
                {
                    PreserveNullAndEmptyArrays = true
                })
                // ChartConfig 
                .Lookup(
                    _apiSettings.DisplayChartConfig,
                    "chartConfigIdList",
                    "_id",
                    "chartConfig"
                )
                // Lookup para cardRotatingLayout.dataId
                .Lookup(
                    _apiSettings.DisplayCardConfig,
                    "cardRotatingLayout.dataId",
                    "_id",
                    "rotatingLayoutData"
                )

                // Enriquecer cada elemento del array
                .AppendStage<BsonDocument>(new BsonDocument("$addFields",
                    new BsonDocument("cardRotatingLayout",
                        new BsonDocument("$map",
                            new BsonDocument
                            {
                                { "input", "$cardRotatingLayout" },
                                { "as", "item" },
                                {
                                    "in",
                                    new BsonDocument("$mergeObjects", new BsonArray
                                    {
                                        "$$item",
                                        new BsonDocument("data",
                                            new BsonDocument("$arrayElemAt", new BsonArray
                                            {
                                                new BsonDocument("$filter", new BsonDocument
                                                {
                                                    { "input", "$rotatingLayoutData" },
                                                    { "as", "d" },
                                                    {
                                                        "cond",
                                                        new BsonDocument("$eq", new BsonArray
                                                        {
                                                            "$$d._id",
                                                            "$$item.dataId"
                                                        })
                                                    }
                                                }),
                                                0
                                            })
                                        )
                                    })
                                }
                            })
                    )
                ))

                // limpiar auxiliar
                .AppendStage<BsonDocument>(new BsonDocument("$unset", "rotatingLayoutData"))
                .As<DisplayConfig>();

            return await aggregate.FirstOrDefaultAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError("Error on config display repository on GetById Exception: {ex}", ex.Message);
            return null;
        }
    }

    /// <summary>
    /// Retrieves the default display configuration for a given type.
    /// Default configurations are identified by having "Default" as the hospital name.
    /// </summary>
    /// <param name="type">The display type to search for.</param>
    /// <returns>The default DisplayConfig for the specified type, or null if not found.</returns>
    public async Task<DisplayConfig?> GetDefault(DisplayConfigEnums.DisplayType type)
    {
        var filter = Builders<DisplayConfig>.Filter.And(
            Builders<DisplayConfig>.Filter.Eq(p => p.Type, type),
            Builders<DisplayConfig>.Filter.Eq(p => p.Hospital, "Default")
        );

        var result = await Collection.FindAsync(filter);
        return result.FirstOrDefault();
    }

    /// <summary>
    /// Inserts a new display configuration and returns the inserted document.
    /// </summary>
    /// <param name="config">The DisplayConfig to insert.</param>
    /// The inserted DisplayConfig, or null if insertion fails.
    /// <returns></returns>
    public async Task<DisplayConfig?> InsertOneAsyncAndReturn(DisplayConfig config)
    {
        try
        {
            await Collection.InsertOneAsync(config);
            return config;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.Message);
            return null;
        }
    }

    /// <summary>
    /// Updates the smart display configuration for a specific display.
    /// </summary>
    /// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
    /// <param name="newDisplayConfig">The new SmartDisplay configuration to apply.</param>
    /// <returns>The updated SmartDisplay configuration, or null if update fails.</returns>
    public async Task<SmartDisplay?> UpdateSmartDisplay(ObjectId displayConfigId, SmartDisplay? newDisplayConfig)
    {
        try
        {
            if (newDisplayConfig == null) return null;

            var filter = Builders<DisplayConfig>.Filter.Eq("Id", displayConfigId);
            var update = Builders<DisplayConfig>.Update
                    .Set(c => ((SmartDisplay)c).Pumps, newDisplayConfig.Pumps)
                    .Set(c => ((SmartDisplay)c).HasCameras, newDisplayConfig.HasCameras)
                    .Set(c => ((SmartDisplay)c).HasSound, newDisplayConfig.HasSound)
                    .Set(c => ((SmartDisplay)c).CamerasAreActive, newDisplayConfig.CamerasAreActive)
                    .Set(c => ((SmartDisplay)c).IsRotationEnabled, newDisplayConfig.IsRotationEnabled)
                    .Set(c => ((SmartDisplay)c).CanChangeCameraMode, newDisplayConfig.CanChangeCameraMode)
                    .Set(c => ((SmartDisplay)c).FieldList, newDisplayConfig.FieldList)
                    .Set(c => ((SmartDisplay)c).ChartConfig, newDisplayConfig.ChartConfig)
                    .Set(c => ((SmartDisplay)c).GraphLayout, newDisplayConfig.GraphLayout)
                    .Set(c => ((SmartDisplay)c).SensorList, newDisplayConfig.SensorList)
                    .Set(c => ((SmartDisplay)c).AlarmFieldList, newDisplayConfig.AlarmFieldList)
                    .Set(c => c.HomeConfig, newDisplayConfig.HomeConfig)
                    .Set(c => c.DetailConfigId, newDisplayConfig.DetailConfigId)
                    .Set(c => ((SmartDisplay)c).CameraStreamType, newDisplayConfig.CameraStreamType)
                    .Set(c => ((SmartDisplay)c).ColorConfig, newDisplayConfig.ColorConfig)
                    .Set(c => ((SmartDisplay)c).RequestGroupedFieldList, newDisplayConfig.RequestGroupedFieldList)
                    .Set(c => c.Hospital, newDisplayConfig.Hospital)
                    // .Set(c => c.CardConfig, newDisplayConfig.CardConfig)
                    .Set(c => c.DisplaySectionIdList, newDisplayConfig.DisplaySectionIdList)
                ;

            var c = await Collection.FindOneAndUpdateAsync(filter, update,
                new FindOneAndUpdateOptions<DisplayConfig, DisplayConfig> { ReturnDocument = ReturnDocument.After });
            return c as SmartDisplay;
        }
        catch (Exception e)
        {
            _logger.LogError($"Error on config display repository on UpdateSmartDisplay Exception: {e.Message}");
            return null;
        }
    }

    /// <summary>
    /// Updates the color configuration for a specific display.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
    /// <param name="colorConfig">The new ColorConfig to apply.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    /// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
    public async Task<bool> UpdateConfigColor(ObjectId objectIdConfigDisplay, ColorConfig colorConfig)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
        var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
        if (colorConfig.Level != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.level", colorConfig.Level));
        if (colorConfig.Text != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.text", colorConfig.Text));
        if (colorConfig.Arrow != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.arrow", colorConfig.Arrow));
        if (colorConfig.Indicator != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.indicator", colorConfig.Indicator));
        if (colorConfig.Graph != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.graph", colorConfig.Graph));
        if (colorConfig.BoxNumber != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.boxNumber", colorConfig.BoxNumber));
        if (colorConfig.BoxStatusColor != null)
            updateDefinition.Add(
                Builders<DisplayConfig>.Update.Set("colorConfig.boxStatusColor", colorConfig.BoxStatusColor));
        if (colorConfig.Test != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.test", colorConfig.Test));
        if (colorConfig.Therapy != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.therapy", colorConfig.Therapy));
        if (colorConfig.Procedure != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("colorConfig.procedure", colorConfig.Procedure));
        var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);
        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            if (result.ModifiedCount > 0) return true; // Retorna el objeto actualizado si la modificación fue exitosa

            return false; // Retorna nulo si no se encontró el documento o no se modificó
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateConfigColor: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
            return false;
        }
    }

    /// <summary>
    /// Updates the header configuration for a specific display.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
    /// <param name="headerConfig">The new HeaderConfig to apply.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    /// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
    public async Task<bool> UpdateHeaderConfig(ObjectId objectIdConfigDisplay, HeaderConfig headerConfig)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
        var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
        if (headerConfig.PartnerLogo != null)
            updateDefinition.Add(
                Builders<DisplayConfig>.Update.Set("headerConfig.partnerLogo", headerConfig.PartnerLogo));
        if (headerConfig.CompanyLogo != null)
            updateDefinition.Add(
                Builders<DisplayConfig>.Update.Set("headerConfig.companyLogo", headerConfig.CompanyLogo));
        if (headerConfig.CenterLogo != null)
            updateDefinition.Add(
                Builders<DisplayConfig>.Update.Set("headerConfig.centerLogo", headerConfig.CenterLogo));
        if (headerConfig.MeddisLogo != null)
            updateDefinition.Add(
                Builders<DisplayConfig>.Update.Set("headerConfig.meddisLogo", headerConfig.MeddisLogo));
        if (headerConfig.UnitName != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.unitName", headerConfig.UnitName));
        if (headerConfig.Cameras != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.cameras", headerConfig.Cameras));
        if (headerConfig.Sensors != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.sensors", headerConfig.Sensors));
        if (headerConfig.Fullscreen != null)
            updateDefinition.Add(
                Builders<DisplayConfig>.Update.Set("headerConfig.fullscreen", headerConfig.Fullscreen));
        if (headerConfig.Sounds != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.sounds", headerConfig.Sounds));
        if (headerConfig.Sidebar != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.sidebar", headerConfig.Sidebar));
        if (headerConfig.CurrentDateTime != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set("headerConfig.currentDateTime",
                headerConfig.CurrentDateTime));
        if (headerConfig.SectionTitle != null)
            updateDefinition.Add(
                Builders<DisplayConfig>.Update.Set("headerConfig.sectionTitle", headerConfig.SectionTitle));
        var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);
        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            if (result.ModifiedCount > 0) return true;

            return false;
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateConfigColor: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
            return false;
        }
    }

    /// <summary>
    /// Updates the home banner configuration for a specific display.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
    /// <param name="bannerItems">The list of banner items to set.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    /// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
    public async Task<bool> UpdateSetHomeBanner(ObjectId objectIdConfigDisplay, List<BannerItem> bannerItems)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
        var update = Builders<DisplayConfig>.Update
            .Set(c => ((DisplayNurse)c).HomeBanner, bannerItems);
        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            if (result.ModifiedCount > 0) return true; // Retorna el objeto actualizado si la modificación fue exitosa

            return false; // Retorna nulo si no se encontró el documento o no se modificó
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateConfigColor: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
            return false;
        }
    }

    /// <summary>
    /// Updates the base configuration for a specific display.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
    /// <param name="baseConfig">The base DisplayConfig with updated values.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    /// <exception cref="Exception">Throws an exception if MongoDB update fails.</exception>
    public async Task<bool> UpdateBaseConfig(ObjectId objectIdConfigDisplay, DisplayConfig baseConfig)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq("Id", objectIdConfigDisplay);
        // TODO NO ESPERA EL CARDCONFIG
        var (updateDefinition, _) = GetBaseUpdateDefinition(baseConfig);
        var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);

        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            if (result.ModifiedCount > 0)
                // await UpdateFieldList(objectIdConfigDisplay, GenerateFieldListFromStrig(fieldList));
                return true; // Retorna el objeto actualizado si la modificación fue exitosa

            return false; // Retorna nulo si no se encontró el documento o no se modificó
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateBaseConfig: {name}. Exception: {ex}", objectIdConfigDisplay, ex);
            return false;
        }
    }

    /// <summary>
    /// Updates the nurse display configuration with additional observation fields.
    /// </summary>
    /// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
    /// <param name="newDisplayConfig">The new DisplayNurseDto configuration to apply.</param>
    /// <param name="nurseObs">List of observation names to mark as "last".</param>
    /// <returns>The updated DisplayNurse configuration, or null if update fails.</returns>
    public async Task<DisplayNurse?> UpdateDisplayNurse(ObjectId displayConfigId, DisplayNurseDto? newDisplayConfig,
        List<string> nurseObs)
    {
        try
        {
            if (newDisplayConfig == null) return null;
            var filter = Builders<DisplayConfig>.Filter.Eq("Id", displayConfigId);
            var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
            if (newDisplayConfig.CardConfigId != null)
                updateDefinition.Add(
                    Builders<DisplayConfig>.Update.Set(c => c.CardConfigId, newDisplayConfig.CardConfigId));
            if (newDisplayConfig.DetailConfigId != null)
                updateDefinition.Add(
                    Builders<DisplayConfig>.Update.Set(c => c.DetailConfigId, newDisplayConfig.DetailConfigId));
            if (newDisplayConfig.HomeConfig != null)
                updateDefinition.Add(
                    Builders<DisplayConfig>.Update.Set(c => c.HomeConfig, newDisplayConfig.HomeConfig));
            if (newDisplayConfig.Hospital != null)
                updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.Hospital, newDisplayConfig.Hospital));
            if (newDisplayConfig.HeaderConfig != null)
                updateDefinition.Add(
                    Builders<DisplayConfig>.Update.Set(c => c.HeaderConfig, newDisplayConfig.HeaderConfig));
            if (newDisplayConfig.HomeBanner != null && newDisplayConfig.HomeBanner.Count != 0)
                updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).HomeBanner,
                    newDisplayConfig.HomeBanner));
            if (newDisplayConfig.ColorConfig != null)
                updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).ColorConfig,
                    newDisplayConfig.ColorConfig));
            if (newDisplayConfig.FormConfig != null)
                updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).FormConfig,
                    newDisplayConfig.FormConfig));

            var originalList = ExtractObservationFields(newDisplayConfig, nurseObs);
            if (originalList.Count > 0)
                updateDefinition.Add(
                    Builders<DisplayConfig>.Update.Set(c => ((DisplayNurse)c).FieldList, originalList));

            var update = Builders<DisplayConfig>.Update.Combine(updateDefinition);
            var c = await Collection.FindOneAndUpdateAsync(filter, update,
                new FindOneAndUpdateOptions<DisplayConfig, DisplayConfig> { ReturnDocument = ReturnDocument.After });
            if (c == null)
            {
                _logger.LogError("Error on config display repository on UpdateDisplayNurse unable to FindOneAndUpdate");
                return null;
            }

            return c as DisplayNurse;
        }
        catch (Exception e)
        {
            _logger.LogError("Error on config display repository on UpdateDisplayNurse Exception: {eMessage}",
                e.Message);
            return null;
        }
    }

    /// <summary>
    /// Updates the hospital name for a specific display configuration.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
    /// <param name="name">The new hospital name.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    public async Task<bool> UpdateDisplayConfigHospitalName(ObjectId objectIdConfigDisplay, string name)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(c => c.Id, objectIdConfigDisplay);
        var update = Builders<DisplayConfig>.Update.Set(c => c.Hospital, name);

        var updatedConfig = await Collection.FindOneAndUpdateAsync(
            filter,
            update,
            new FindOneAndUpdateOptions<DisplayConfig> { ReturnDocument = ReturnDocument.After }
        );

        if (updatedConfig == null)
        {
            _logger.LogError("Error in UpdateDisplayConfigHospitalName: Unable to find and update DisplayConfig.");
            return false;
        }

        return true;
    }

    /// <summary>
    /// Updates the field list for a specific display configuration.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to update.</param>
    /// <param name="fields">The new list of fields to set.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    public async Task<bool> UpdateFieldList(ObjectId objectIdConfigDisplay, List<Field> fields)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, objectIdConfigDisplay);
        var update = Builders<DisplayConfig>.Update.Set(x => x.FieldList, fields);

        var result = await Collection.UpdateOneAsync(filter, update);
        return result.ModifiedCount > 0;
    }

    /// <summary>
    /// Gets the default display configuration for a unit based on its ID and display type.
    /// </summary>
    /// <param name="unitId">The ObjectId of the unit.</param>
    /// <param name="displayType">The type of display configuration to retrieve.</param>
    /// <returns>The default DisplayConfig for the unit, or null if not found.</returns>
    public async Task<DisplayConfig?> GetDefaultByUnitIdAndType(ObjectId unitId,
        DisplayConfigEnums.DisplayType displayType)
    {
        var unit = await _unitRepository.FindById(unitId);
        switch (displayType)
        {
            case DisplayConfigEnums.DisplayType.DisplayNurse:
                if (unit?.Configuration.PlanDisplayConfiguration == null) return null;
                return await GetById(unit.Configuration.PlanDisplayConfiguration.Value) as DisplayNurse;
            case DisplayConfigEnums.DisplayType.SmartDisplay:
                if (unit?.Configuration.SmartDisplayConfiguration == null) return null;
                return await GetById(unit.Configuration.SmartDisplayConfiguration.Value) as SmartDisplay;
            case DisplayConfigEnums.DisplayType.StandarDisplay:
                if (unit?.Configuration.StandarDisplayConfiguration == null) return null;
                return await GetById(unit.Configuration.StandarDisplayConfiguration.Value) as StandarDisplay;
            default: return null;
        }
    }

    /// <summary>
    /// Deletes a display configuration by its ID.
    /// </summary>
    /// <param name="objectIdConfigDisplay">The ObjectId of the display configuration to delete.</param>
    /// <returns>The deleted DisplayConfig, or null if not found.</returns>
    public async Task<DisplayConfig?> DeleteDisplayConfig(ObjectId objectIdConfigDisplay)
    {
        return await DeleteAsync(objectIdConfigDisplay);
    }

    /// <summary>
    /// Retrieves all display configurations in compact format (minimal response).
    /// </summary>
    /// <returns>A list of DisplayConfigMinimalResponse containing Id, Hospital, and Type.</returns>
    public Task<List<DisplayConfigMinimalResponse>> GetAllCompact()
    {
        var filter = Builders<DisplayConfig>.Filter.Empty;
        return Collection
            .Find(filter)
            .Project(d => new DisplayConfigMinimalResponse
            {
                Id = d.Id,
                Hospital = d.Hospital ?? "",
                Type = d.Type
            }).ToListAsync();
    }

    /// <summary>
    /// Retrieves all display configuration IDs associated with a specific card configuration.
    /// </summary>
    /// <param name="cardConfigId">The ObjectId of the card configuration.</param>
    /// <returns>A list of ObjectIds for display configurations using the specified card config.</returns>
    public async Task<List<ObjectId>> GetAllByCardConfigId(ObjectId cardConfigId)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(d => d.CardConfigId, cardConfigId);

        var result = await Collection.Find(filter)
            .Project(d => d.Id)
            .ToListAsync();

        return result;
    }

    /// <summary>
    /// Retrieves all display configuration IDs associated with a card config, including rotating layouts.
    /// </summary>
    /// <param name="cardConfigId">The ObjectId of the card configuration.</param>
    /// <returns>A list of ObjectIds for display configurations using the specified card config in main or rotating layout.</returns>
    public async Task<List<ObjectId>> GetAllByCardConfigIdAndRotating(ObjectId cardConfigId)
    {
        var mainFilter = Builders<DisplayConfig>.Filter.Eq(d => d.CardConfigId, cardConfigId);

        var rotatingFilter = Builders<DisplayConfig>.Filter.ElemMatch(
            "cardRotatingLayout",
            Builders<BsonDocument>.Filter.Eq("dataId", cardConfigId)
        );

        var combinedFilter = Builders<DisplayConfig>.Filter.Or(mainFilter, rotatingFilter);

        var result = await Collection.Find(combinedFilter)
            .Project(d => d.Id)
            .ToListAsync();

        return result;
    }

    /// <summary>
    /// Updates the card configuration ID for a specific display.
    /// </summary>
    /// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
    /// <param name="resultId">The new card configuration ID to set.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    public async Task<bool> UpdateCardConfigId(ObjectId? displayConfigId, ObjectId? resultId)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, displayConfigId);
        var update = Builders<DisplayConfig>.Update.Set(x => x.CardConfigId, resultId);

        var result = await Collection.UpdateOneAsync(filter, update);
        return result.ModifiedCount > 0;
    }

    /// <summary>
    /// Adds a new chart configuration ID to a smart display's chart config list.
    /// </summary>
    /// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
    /// <param name="newChartIdToAdd">The new chart configuration ID to add.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    public async Task<bool> AddChartId(ObjectId? displayConfigId, ObjectId newChartIdToAdd)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, displayConfigId);
        var update = Builders<DisplayConfig>.Update
            .AddToSet(c => ((SmartDisplay)c).ChartConfigIdList, newChartIdToAdd);

        var result = await Collection.UpdateOneAsync(filter, update);
        return result.ModifiedCount > 0;
    }

    /// <summary>
    /// Removes a deleted chart configuration ID from all display configurations.
    /// </summary>
    /// <param name="deletedId">The ObjectId of the chart configuration that was deleted.</param>
    /// <returns>The MongoDB UpdateResult indicating the number of modified documents.</returns>
    /// <exception cref="Exception">Throws an exception if MongoDB operation fails.</exception>
    public async Task<UpdateResult> UpdateDeletedChartConfig(ObjectId deletedId)
    {
        try
        {
            var filter = Builders<DisplayConfig>.Filter.AnyEq("chartConfigIdList", deletedId);

            var update = Builders<DisplayConfig>.Update.Pull("chartConfigIdList", deletedId);

            return await Collection.UpdateManyAsync(filter, update);
        }
        catch (Exception ex)
        {
            _logger.LogError("Error actualizando referencias de ChartConfig borrado: {ex}", ex.Message);
            throw;
        }
    }

    /// <summary>
    /// Gets a chart configuration by ID. This method is not implemented.
    /// </summary>
    /// <param name="objectIdConfigChart">The ObjectId of the chart configuration.</param>
    /// <returns>Always throws NotImplementedException.</returns>
    /// <exception cref="NotImplementedException">This method is not implemented.</exception>
    public Task<ChartConfig> GetChartConfig(ObjectId objectIdConfigChart)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Updates the detail configuration ID for a specific display.
    /// </summary>
    /// <param name="displayConfigId">The ObjectId of the display configuration to update.</param>
    /// <param name="resultId">The new detail configuration ID to set.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    public async Task<bool> UpdateDetailConfigId(ObjectId? displayConfigId, ObjectId? resultId)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(x => x.Id, displayConfigId);
        var update = Builders<DisplayConfig>.Update.Set(x => x.DetailConfigId, resultId);

        var result = await Collection.UpdateOneAsync(filter, update);
        return result.ModifiedCount > 0;
    }

    /// <summary>
    /// Retrieves all display configuration IDs associated with a specific detail configuration.
    /// </summary>
    /// <param name="baseConfigId">The ObjectId of the detail configuration.</param>
    /// <returns>A list of ObjectIds for display configurations using the specified detail config.</returns>
    public async Task<List<ObjectId>> GetAllByCardDetailConfigId(ObjectId baseConfigId)
    {
        var filter = Builders<DisplayConfig>.Filter.Eq(d => d.DetailConfigId, baseConfigId);

        var result = await Collection.Find(filter)
            .Project(d => d.Id)
            .ToListAsync();

        return result;
    }

    /// <summary>
    /// Performs initial data load by creating default configurations for each display type if they don't exist.
    /// </summary>
    /// <remarks>
    /// This method runs on application startup to ensure default configurations exist for:
    /// - DisplayNurse
    /// - SmartDisplay
    /// - StandarDisplay
    /// </remarks>
    public sealed override async Task InsertInitialLoad()
    {
        // 1. Obtener todos los valores y castear al tipo IEnumerable<DisplayType>
        var allTypes = (DisplayConfigEnums.DisplayType[])Enum.GetValues(typeof(DisplayConfigEnums.DisplayType));

        // 2. Usar LINQ para filtrar y convertir de nuevo a un array (o lista)
        var displayTypesToIterate = allTypes
            .Where(dt => dt != DisplayConfigEnums.DisplayType.Unknown) // Filtra el valor 'Unknown'
            .ToArray();
        foreach (var type in displayTypesToIterate)
        {
            var defaultConfigByType = await GetDefault(type);
            if (defaultConfigByType == null)
                switch (type)
                {
                    case DisplayConfigEnums.DisplayType.DisplayNurse:
                        var nurse = new DisplayNurse
                        {
                            Hospital = "Default",
                            Type = DisplayConfigEnums.DisplayType.DisplayNurse,
                            ColorConfig = new ColorConfig(),
                            FormConfig = new FormConfig
                            {
                                Admission = new FormItemOverview { Nhc = true },
                                Demographic = new FormItemOverview { Nhc = true },
                                Discharge = new FormItemOverview { Nhc = true },
                                IncomeInfo = new FormItemOverview { Nhc = true }
                            },
                            HomeBanner = []
                        };
                        await InsertOneAsyncAndReturn(nurse);
                        break;
                    case DisplayConfigEnums.DisplayType.SmartDisplay:
                        var smart = new SmartDisplay
                        {
                            Hospital = "Default",
                            Type = DisplayConfigEnums.DisplayType.SmartDisplay,
                            ColorConfig = new ColorConfig()
                        };
                        await InsertOneAsyncAndReturn(smart);
                        break;
                    case DisplayConfigEnums.DisplayType.StandarDisplay:
                        var standar = new StandarDisplay
                        {
                            Type = DisplayConfigEnums.DisplayType.StandarDisplay,
                            Hospital = "Default"
                        };
                        await InsertOneAsyncAndReturn(standar);
                        break;
                }
        }
        // Si alguno no existe crearlos
    }

    /// <summary>
    /// Extracts observation field names from a DisplayNurseDto configuration.
    /// </summary>
    /// <param name="newDisplayConfig">The DisplayNurseDto configuration to extract fields from.</param>
    /// <param name="nurseObs">List of observation names to mark as "last" priority.</param>
    /// <returns>A list of Field objects with extracted observation names.</returns>
    private List<Field> ExtractObservationFields(DisplayNurseDto newDisplayConfig, List<string> nurseObs)
    {
        var fieldSet = new HashSet<string>();
        var regex = new Regex(@"""ManualObservationName""\s*:\s*\[\s*((?:""[^""]*""\s*,?\s*)+)\]",
            RegexOptions.Compiled);

        // --- 1. CardConfig ---
        if (newDisplayConfig.CardConfig?.Rows is { Count: > 0 })
            FillHashSet(JsonConvert.SerializeObject(newDisplayConfig.CardConfig.Rows), regex, fieldSet);
        if (newDisplayConfig.CardConfig?.Rows is { Count: > 0 })
            foreach (var row in newDisplayConfig.CardConfig.Rows)
                ExtractFromCells(row.Cells, fieldSet);
        // --- 2. DetailConfig ---
        if (newDisplayConfig.DetailConfig?.NurseRows is { Count: > 0 })
            FillHashSet(JsonConvert.SerializeObject(newDisplayConfig.DetailConfig?.NurseRows), regex, fieldSet);
        if (newDisplayConfig.DetailConfig?.NurseRows is { Count: > 0 })
            foreach (var row in newDisplayConfig.DetailConfig.NurseRows)
                ExtractFromDetailsCells(row.Cells, fieldSet);
        // --- Result: convert to List<Field> ---
        return fieldSet
            .Distinct()
            .Select(name => new Field { Name = name, Last = nurseObs.Contains(name) ? 1 : 2 })
            .ToList();
    }

    /// <summary>
    /// Fills a hash set with observation names extracted from JSON using regex.
    /// </summary>
    /// <param name="newDisplayConfig">The JSON string to search for observation names.</param>
    /// <param name="regex">The regex pattern to match observation name arrays.</param>
    /// <param name="fieldSet">The hash set to populate with field names.</param>
    private void FillHashSet(string newDisplayConfig, Regex regex, HashSet<string> fieldSet)
    {
        var matches = regex.Matches(newDisplayConfig);

        foreach (Match match in matches)
            if (match.Groups.Count > 1)
            {
                var arrayContent = match.Groups[1].Value;
                var items = Regex.Matches(arrayContent, @"""([^""]+)""");
                foreach (Match item in items) fieldSet.Add(item.Groups[1].Value);
            }
    }

    /// <summary>
    /// Recursively extracts observation names from cell configurations.
    /// </summary>
    /// <param name="cells">The list of cells to extract from.</param>
    /// <param name="fieldSet">The hash set to populate with field names.</param>
    private void ExtractFromCells(List<Cell>? cells, HashSet<string> fieldSet)
    {
        if (cells is null)
            return;

        foreach (var cell in cells)
        {
            // 1. Extraer ObservationName
            if (cell.ObservationName is { Count: > 0 })
                foreach (var obsName in cell.ObservationName)
                    fieldSet.Add(obsName);

            // 2. Recursión: sub-observaciones
            if (cell.SubObs is { Count: > 0 })
                ExtractFromCells(cell.SubObs, fieldSet);
        }
    }

    /// <summary>
    /// Recursively extracts observation names from detail cell configurations.
    /// </summary>
    /// <param name="cells">The list of detail cells to extract from.</param>
    /// <param name="fieldSet">The hash set to populate with field names.</param>
    private void ExtractFromDetailsCells(List<CellDetails>? cells, HashSet<string> fieldSet)
    {
        if (cells is null)
            return;

        foreach (var cell in cells)
        {
            // 1. Extraer ObservationName
            if (cell.ObservationName is { Count: > 0 })
                foreach (var obsName in cell.ObservationName)
                    fieldSet.Add(obsName);

            // 2. Recursión: sub-observaciones
            if (cell.Cells is { Count: > 0 })
                ExtractFromDetailsCells(cell.Cells, fieldSet);
        }
    }

    /// <summary>
    /// Creates a minimal fluent query for paginated display configs with optional filters.
    /// </summary>
    /// <param name="filters">List of filter definitions to apply.</param>
    /// <param name="sort">Sort definition for the query results.</param>
    /// <returns>A fluent queryable for DisplayConfigSummary.</returns>
    private IFindFluent<DisplayConfig, DisplayConfigSummary> CreateFindFluentMinimal(
        List<FilterDefinition<DisplayConfig>> filters,
        SortDefinition<DisplayConfig> sort)
    {
        var combinedFilter = filters.Any()
            ? Builders<DisplayConfig>.Filter.And(filters)
            : Builders<DisplayConfig>.Filter.Empty;

        return Collection
            .Find(combinedFilter)
            .Sort(sort)
            .Project(d => new DisplayConfigSummary
            {
                Id = d.Id,
                Name = d.Hospital ?? "",
                Type = d.Type
            });
    }

    /// <summary>
    /// Generates update definitions from a base DisplayConfig.
    /// </summary>
    /// <param name="baseConfig">The base DisplayConfig with values to update.</param>
    /// <returns>A tuple containing list of update definitions and list of field names.</returns>
    private (List<UpdateDefinition<DisplayConfig>> Updates, List<string> Fields) GetBaseUpdateDefinition(
        DisplayConfig baseConfig)
    {
        var fieldList = new List<string>();
        var updateDefinition = new List<UpdateDefinition<DisplayConfig>>();
        // if (baseConfig.CardConfig != null)
        // {
        //     updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.CardConfig, baseConfig.CardConfig));
        //     fieldList.AddRange(baseConfig.CardConfig.GetAllObservationNames());
        // }
        if (baseConfig.DetailConfig != null)
        {
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.DetailConfig, baseConfig.DetailConfig));
            fieldList.AddRange(baseConfig.DetailConfig.GetAllObservationNames());
        }

        if (baseConfig.HomeConfig != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.HomeConfig, baseConfig.HomeConfig));
        if (baseConfig.Hospital != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.Hospital, baseConfig.Hospital));
        if (baseConfig.HeaderConfig != null)
            updateDefinition.Add(Builders<DisplayConfig>.Update.Set(c => c.HeaderConfig, baseConfig.HeaderConfig));
        return (updateDefinition, fieldList);
    }
}

#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member