using System.Text;
using System.Text.RegularExpressions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.Masters;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Serilog;

namespace adas_core.Infrastructure.Repositories;

/// <summary>
/// Generic repository implementation for managing MasterList entities in MongoDB.
/// Provides CRUD operations and specialized queries for various master list types including
/// options lists, diagnoses, allergies, procedures, treatments, and other reference data.
/// </summary>
/// <typeparam name="T">The type of MasterList entity to manage.</typeparam>
/// <!-- aidoc:v1 sig=f958de2 -->
public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository<T> where T : MasterList
{
    private readonly ApiSettings _apiSettings;

    /// <summary>
    /// Initializes a new instance of the MasterListRepository.
    /// </summary>
    /// <param name="apiSettings">API settings containing collection names configuration.</param>
    /// <param name="database">The MongoDB database instance.</param>
    /// <exception cref="ArgumentNullException">Thrown when apiSettings is null.</exception>
    /// <!-- aidoc:v1 sig=800d538 body=d2b18a3 -->
    public MasterListRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
    {
        if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
        _apiSettings = apiSettings.Value;
    }

    /// <summary>
    /// Gets the name of the collection based on the entity type T.
    /// Maps different MasterList subtypes to their corresponding MongoDB collection names.
    /// </summary>
    /// <returns>The collection name for the current MasterList type.</returns>
    /// <!-- aidoc:v1 sig=94e22ff body=64e6c55 -->
    public override string GetCollectionName()
    {
        return typeof(T) switch
        {
            { } t when t == typeof(AltableOptionList) => _apiSettings.AltableOptionList,
            { } t when t == typeof(AllergyList) => _apiSettings.AllergyList,
            { } t when t == typeof(DestinationList) => _apiSettings.DestinationList,
            { } t when t == typeof(DiagnosisList) => _apiSettings.DiagnosisList,
            { } t when t == typeof(DischargeStatusList) => _apiSettings.DischargeStatusList,
            { } t when t == typeof(DoctorList) => _apiSettings.DoctorList,
            { } t when t == typeof(DoctorTypeList) => _apiSettings.DoctorTypeList,
            { } t when t == typeof(InternalDestinationList) => _apiSettings.InternalDestinationList,
            { } t when t == typeof(InsulationList) => _apiSettings.InsulationList,
            { } t when t == typeof(LanguageBarrierList) => _apiSettings.LanguageBarrierList,
            { } t when t == typeof(MobilityOptionList) => _apiSettings.MobilityOptionList,
            { } t when t == typeof(OriginList) => _apiSettings.OriginList,
            { } t when t == typeof(PatientStatusList) => _apiSettings.PatientStatusList,
            { } t when t == typeof(ProcedureList) => _apiSettings.ProcedureList,
            { } t when t == typeof(TestList) => _apiSettings.TestList,
            { } t when t == typeof(ServiceList) => _apiSettings.ServiceList,
            { } t when t == typeof(TherapeuticCeilingList) => _apiSettings.TherapeuticCeilingList,
            { } t when t == typeof(TreatmentList) => _apiSettings.TreatmentList,
            { } t when t == typeof(VisitOptionList) => _apiSettings.VisitOptionList,
            { } t when t == typeof(AccessControlList) => _apiSettings.AccessControlList,
            { } t when t == typeof(PassiveSittingList) => _apiSettings.PassiveSittingList,
            { } t when t == typeof(GenericList) => _apiSettings.GenericList,
            _ => "list_master"
        };
    }

    /// <summary>
    /// Inserts a new master list entity into the database.
    /// </summary>
    /// <param name="entity">The entity to insert.</param>
    /// <exception cref="Exception">Throws and re-throws exceptions after logging.</exception>
    /// <!-- aidoc:v1 sig=c7a7ad8 body=26b3581 -->
    public override async Task InsertOneAsync(T entity)
    {
        try
        {
            await base.InsertOneAsync(entity);
        }
        catch (Exception e)
        {
            Log.Error("Exception trying to insert entity: {entity}. Exception: {e}", entity, e);
            throw;
        }
    }

    /// <summary>
    /// Deletes a master list entity by its ID.
    /// </summary>
    /// <param name="id">The ObjectId of the entity to delete.</param>
    /// <exception cref="Exception">Throws and re-throws exceptions after logging.</exception>
    /// <!-- aidoc:v1 sig=3de1ad6 body=fe0ba8c -->
    public async Task Delete(ObjectId id)
    {
        try
        {
            var filter = Builders<T>.Filter.Eq(doc => doc.Id, id);
            await Collection.DeleteOneAsync(filter);
        }
        catch (Exception e)
        {
            Log.Error("Exception deleting entity {entity}: {id}. Exception {e}", typeof(T).Name, id, e);
            throw;
        }
    }

    /// <summary>
    /// Updates an existing master list entity with full replacement.
    /// </summary>
    /// <param name="entity">The entity with updated values.</param>
    /// <exception cref="Exception">Throws and re-throws exceptions after logging.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=wrong_summary
    ///      "Summary states 'Updates an existing' entity, but the ReplaceOptions { IsUpsert = true } means the entity will be inserted if it does not already exist, so the 'existing' qualifier is contradicted by the code's actual behavior." -->
    public async Task Update(T entity)
    {
        try
        {
            var filter = Builders<T>.Filter.Eq(doc => doc.Id, entity.Id);
            await Collection.ReplaceOneAsync(filter, entity, new ReplaceOptions { IsUpsert = true });
        }
        catch (Exception e)
        {
            Log.Error("Exception trying to update entity: {entity}. Exception: {e}", entity, e);
            throw;
        }
    }

    /// <summary>
    /// Updates a specific option within a master list with full property replacement.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="newOpt">The OptionList with updated values.</param>
    /// <returns>The updated OptionList if found; otherwise, null.</returns>
    /// <!-- aidoc:v1 sig=64ca4dd body=a7714e6 -->
    public async Task<OptionList?> UpdateFullMasterListOption(ObjectId id, OptionList newOpt)
    {
        var filter = Builders<T>.Filter.Where(o => o.Id == id && o.Options.Any(opt => opt.Id == newOpt.Id)
        );
        var update = Builders<T>.Update
                .Set("options.$.name", newOpt.Name)
                .Set("options.$.description", newOpt.Description)
                .Set("options.$.bgColor", newOpt.BgColor)
                .Set("options.$.color", newOpt.Color)
                .Set("options.$.endDate", newOpt.EndDate)
                .Set("options.$.optionType", newOpt.OptionType)
                .Set("options.$.isDefault", newOpt.IsDefault)
                .Set("options.$.initDate", newOpt.InitDate)
                .Set("options.$.iconDefault", newOpt.IconDefault)
                .Set("options.$.iconCategory", newOpt.IconCategory)
                .Set("options.$.iconColor", newOpt.IconColor)
                .Set("options.$.localeItems", newOpt.LocaleItems)
            ;
        var options = new FindOneAndUpdateOptions<T, OptionList>
        {
            ReturnDocument = ReturnDocument.After
        };
        var updatedList = await Collection.FindOneAndUpdateAsync(filter, update, options);

        return updatedList;
    }

    /// <summary>
    /// Finds a master list entity by its ID with options projection limited to 100 items.
    /// </summary>
    /// <param name="id">The ObjectId of the entity to retrieve.</param>
    /// <returns>The MasterList entity if found; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=high kind=wrong_summary
    ///      "Method is generic (returns Task<T?>) and works on any entity type T, not specifically a 'master list entity'." -->
    /// <!-- aidoc-review:v1 severity=high kind=wrong_returns
    ///      "Documentation says it returns 'The MasterList entity' but the method actually returns Task<T?>, a generic type." -->
    /// <!-- aidoc-review:v1 severity=high kind=wrong_exception
    ///      "Documents <exception cref=\"Exception\"> but the catch block swallows all exceptions and never rethrows, so the method never throws." -->
    public async Task<T?> FindById(ObjectId id)
    {
        try
        {
            var filter = Builders<T>.Filter.Eq(p => p.Id, id);
            var projection = Builders<T>.Projection.Slice("options", 100); // Limita a 100 elementos en 'options'

            var options = new FindOptions<T, T>
            {
                Projection = projection
            };

            var result = await Collection.FindAsync(filter, options);
            return await result.FirstOrDefaultAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching entity by id: {id}. Exception: {ex}", id, ex);
            return null;
        }
    }

    /// <summary>
    /// Finds a specific option within a master list by master and option IDs with locale translation.
    /// Uses MongoDB aggregation to apply translations and return the translated option.
    /// </summary>
    /// <param name="masterId">The ObjectId of the master list.</param>
    /// <param name="optionId">The ObjectId of the option to retrieve.</param>
    /// <param name="locale">The locale for translation.</param>
    /// <returns>The OptionList with translated fields if found; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all exceptions internally (try/catch) and never throws; documenting <exception cref=\"Exception\"> is misleading. Callers will not receive any exception to handle." -->
    public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId, LocaleEnum locale)
    {
        try
        {
            var localeCode = locale == LocaleEnum.Default
                ? "default"
                : locale.ToString().ToLower();

            var pipeline = new[]
            {
                // 1) Filtrar documento
                new BsonDocument("$match", new BsonDocument("_id", masterId)),

                // 2) Desanidar opciones
                new BsonDocument("$unwind", "$options"),

                // 3) Filtrar la opción concreta
                new BsonDocument("$match", new BsonDocument("options._id", optionId)),

                // 4) Aplicar traducciones
                new BsonDocument("$project", new BsonDocument
                {
                    {
                        "_id", "$options._id" // este sí es el id final que queremos
                    },

                    {
                        "name",
                        new BsonDocument("$cond", new BsonArray
                        {
                            // IF: locale = default
                            new BsonDocument("$or", new BsonArray
                            {
                                new BsonDocument("$eq", new BsonArray { localeCode, "default" }),
                                new BsonDocument("$eq", new BsonArray
                                {
                                    new BsonDocument("$toLower", "$defaultLocale"),
                                    localeCode
                                })
                            }),

                            // THEN: nombre principal
                            "$options.name",

                            // ELSE: traducción
                            new BsonDocument("$ifNull", new BsonArray
                            {
                                new BsonDocument("$let", new BsonDocument
                                {
                                    {
                                        "vars",
                                        new BsonDocument
                                        {
                                            {
                                                "localeItem",
                                                new BsonDocument("$first",
                                                    new BsonDocument("$filter",
                                                        new BsonDocument
                                                        {
                                                            {
                                                                "input",
                                                                new BsonDocument("$map",
                                                                    new BsonDocument
                                                                    {
                                                                        {
                                                                            "input",
                                                                            new BsonDocument("$objectToArray",
                                                                                "$options.localeItems")
                                                                        },
                                                                        { "as", "pair" },
                                                                        {
                                                                            "in",
                                                                            new BsonDocument("$cond",
                                                                                new BsonArray
                                                                                {
                                                                                    new BsonDocument("$eq",
                                                                                        new BsonArray
                                                                                            { "$$pair.k", localeCode }),
                                                                                    "$$pair.v",
                                                                                    BsonNull.Value
                                                                                })
                                                                        }
                                                                    })
                                                            },
                                                            { "as", "v" },
                                                            {
                                                                "cond",
                                                                new BsonDocument("$ne",
                                                                    new BsonArray { "$$v", BsonNull.Value })
                                                            }
                                                        })
                                                )
                                            }
                                        }
                                    },
                                    {
                                        "in",
                                        new BsonDocument("$ifNull",
                                            new BsonArray
                                            {
                                                "$$localeItem.name",
                                                "$options.name"
                                            })
                                    }
                                }),

                                "$options.name"
                            })
                        })
                    },

                    { "optionType", "$options.optionType" },
                    { "description", "$options.description" },
                    { "color", "$options.color" },
                    { "bgColor", "$options.bgColor" },
                    { "iconDefault", "$options.iconDefault" },
                    { "iconCategory", "$options.iconCategory" },
                    { "iconColor", "$options.iconColor" },
                    { "initDate", "$options.initDate" },
                    { "endDate", "$options.endDate" },
                    { "isDefault", "$options.isDefault" },
                    { "localeItems", "$options.localeItems" }
                })
            };

            var result = await Collection.Aggregate<BsonDocument>(pipeline).FirstOrDefaultAsync();
            if (result == null)
                return null;

            return BsonSerializer.Deserialize<OptionList>(result);
        }
        catch (Exception ex)
        {
            Log.Error("Error FindOptionItemById: {ex}", ex);
            return null;
        }
    }

    /// <summary>
    /// Finds a specific option within a master list by master and option IDs without locale translation.
    /// </summary>
    /// <param name="masterId">The ObjectId of the master list.</param>
    /// <param name="optionId">The ObjectId of the option to retrieve.</param>
    /// <returns>The OptionList if found; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method contains a catch-all for Exception and never lets it propagate to callers, so documenting <exception cref=\"Exception\"> is misleading; the tag should be removed since no exception escapes this method." -->
    public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId)
    {
        try
        {
            var filter = Builders<T>.Filter.And(
                Builders<T>.Filter.Eq("_id", masterId),
                Builders<T>.Filter.Eq("options._id", optionId)
            );

            var projection = Builders<T>.Projection.Include("options.$");

            // Esto devuelve el documento padre pero SOLO con la opción que coincide en el array
            var master = await Collection.Find(filter).Project<T>(projection).FirstOrDefaultAsync();

            // Accedemos a la primera (y única) opción del array filtrado
            return master?.Options?.FirstOrDefault();
        }
        catch (Exception ex)
        {
            Log.Error("Error FindOptionItemById: {ex}", ex);
            return null;
        }
    }

    /// <summary>
    /// Finds a master list entity by its ID with optional locale translation for options.
    /// Uses MongoDB aggregation to unwind options and apply translations.
    /// </summary>
    /// <param name="id">The ObjectId of the entity to retrieve.</param>
    /// <param name="locale">Optional locale for translated option names.</param>
    /// <returns>The MasterList entity with translated options if found; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=high kind=extra_exception
    ///      "The <exception cref=\"Exception\"> tag is misleading: the catch block handles all exceptions internally and the method returns null, so no Exception is ever thrown to callers. Listing Exception implies callers must handle it, which they cannot." -->
    /// <!-- aidoc-review:v1 severity=high kind=wrong_returns
    ///      "Documentation says 'The MasterList entity' but the method signature is generic Task<T?>, so the returned type is T, not necessarily a MasterList." -->
    public async Task<T?> FindById(ObjectId id, LocaleEnum? locale)
    {
        try
        {
            var localeCode = locale.HasValue && locale.Value != LocaleEnum.Default
                ? locale.Value.ToString().ToLower()
                : "default";

            var pipeline = new[]
            {
                // 1. Filtrar documento
                new BsonDocument("$match", new BsonDocument("_id", id)),

                // 2. Desanidar options
                new BsonDocument("$unwind",
                    new BsonDocument
                    {
                        { "path", "$options" },
                        { "preserveNullAndEmptyArrays", true }
                    }),


                // 3. Proyección
                new BsonDocument("$project", new BsonDocument
                {
                    { "_id", "$_id" },
                    { "name", "$name" },
                    { "defaultLocale", "$defaultLocale" },
                    { "listType", "$listType" },
                    { "canAddElement", "$canAddElement" },
                    { "optionListDetails", "$optionListDetails" },
                    { "autoObservationName", "$autoObservationName" },
                    { "description", "$description" },

                    {
                        "options",
                        new BsonDocument
                        {
                            { "_id", "$options._id" },
                            { "optionType", "$options.optionType" },
                            { "description", "$options.description" },
                            { "color", "$options.color" },
                            { "bgColor", "$options.bgColor" },
                            { "iconDefault", "$options.iconDefault" },
                            { "iconCategory", "$options.iconCategory" },
                            { "iconColor", "$options.iconColor" },
                            { "initDate", "$options.initDate" },
                            { "endDate", "$options.endDate" },
                            { "isDefault", "$options.isDefault" },

                            // ------------------------------------------------------------------
                            //  NAME traducido con fallback (misma lógica que antes, corregida)
                            // ------------------------------------------------------------------
                            {
                                "name",
                                new BsonDocument("$cond", new BsonArray
                                {
                                    // IF: locale == default OR defaultLocale == localeCode
                                    new BsonDocument("$or", new BsonArray
                                    {
                                        new BsonDocument("$eq", new BsonArray { localeCode, "default" }),
                                        new BsonDocument("$eq", new BsonArray
                                        {
                                            new BsonDocument("$toLower", "$defaultLocale"),
                                            localeCode
                                        })
                                    }),

                                    // THEN = nombre original
                                    "$options.name",

                                    // ELSE = nombre traducido (si existe) sino original
                                    new BsonDocument("$ifNull", new BsonArray
                                    {
                                        new BsonDocument("$let", new BsonDocument
                                        {
                                            {
                                                "vars",
                                                new BsonDocument
                                                {
                                                    {
                                                        "localeItem",
                                                        new BsonDocument("$first",
                                                            new BsonDocument("$filter",
                                                                new BsonDocument
                                                                {
                                                                    {
                                                                        "input",
                                                                        new BsonDocument("$map",
                                                                            new BsonDocument
                                                                            {
                                                                                {
                                                                                    "input",
                                                                                    new BsonDocument("$objectToArray",
                                                                                        "$options.localeItems")
                                                                                },
                                                                                { "as", "pair" },
                                                                                {
                                                                                    "in",
                                                                                    new BsonDocument("$cond",
                                                                                        new BsonArray
                                                                                        {
                                                                                            new BsonDocument("$eq",
                                                                                                new BsonArray
                                                                                                {
                                                                                                    "$$pair.k",
                                                                                                    localeCode
                                                                                                }),
                                                                                            "$$pair.v",
                                                                                            BsonNull.Value
                                                                                        })
                                                                                }
                                                                            })
                                                                    },
                                                                    { "as", "v" },
                                                                    {
                                                                        "cond",
                                                                        new BsonDocument("$ne",
                                                                            new BsonArray { "$$v", BsonNull.Value })
                                                                    }
                                                                })
                                                        )
                                                    }
                                                }
                                            },
                                            {
                                                "in",
                                                new BsonDocument("$ifNull", new BsonArray
                                                {
                                                    "$$localeItem.name",
                                                    "$options.name"
                                                })
                                            }
                                        }),
                                        "$options.name"
                                    })
                                })
                            },

                            // ------------------------------------------------------------------
                            // SIEMPRE eliminar localeItems del resultado final
                            // ------------------------------------------------------------------
                            {
                                "localeItems",
                                new BsonDocument("$cond",
                                    new BsonArray { true, "$$REMOVE", "$$REMOVE" })
                            }
                        }
                    }
                }),

                // 4. Agrupar de vuelta
                new BsonDocument("$group", new BsonDocument
                {
                    { "_id", "$_id" },
                    { "name", new BsonDocument("$first", "$name") },
                    { "defaultLocale", new BsonDocument("$first", "$defaultLocale") },
                    { "listType", new BsonDocument("$first", "$listType") },
                    { "canAddElement", new BsonDocument("$first", "$canAddElement") },
                    { "optionListDetails", new BsonDocument("$first", "$optionListDetails") },
                    { "autoObservationName", new BsonDocument("$first", "$autoObservationName") },
                    { "description", new BsonDocument("$first", "$description") },
                    { "options", new BsonDocument("$push", "$options") }
                })
            };

            var result = await Collection.Aggregate<T>(pipeline).FirstOrDefaultAsync();
            return result;
        }
        catch (Exception ex)
        {
            Log.Error("Error searching entity by id: {id}. Exception: {ex}", id, ex);
            return null;
        }
    }

    /// <summary>
    /// Finds a master list entity by its name.
    /// </summary>
    /// <param name="name">The name of the master list to retrieve.</param>
    /// <returns>The MasterList entity if found; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=high kind=wrong_exception
    ///      "The <exception> tag documents Exception being thrown, but the method catches all exceptions internally and returns null instead of letting any exception propagate to the caller." -->
    /// <!-- aidoc-review:v1 severity=high kind=wrong_summary
    ///      "The summary specifies a 'master list entity', but the method is generic (T) and could operate on any type with a Name property; the documentation should not assume a specific type." -->
    /// <!-- aidoc-review:v1 severity=high kind=wrong_returns
    ///      "The <returns> tag describes a MasterList entity, but the method returns Task<T?> where T is a generic type parameter, not necessarily MasterList." -->
    public async Task<T?> FindByName(string name)
    {
        try
        {
            var filter = Builders<T>.Filter.Eq(p => p.Name, name);
            var projection = Builders<T>.Projection.Slice("options", 100); // Limita a 100 elementos en 'options'

            var options = new FindOptions<T, T>
            {
                Projection = projection
            };

            var result = await Collection.FindAsync(filter, options);
            return await result.FirstOrDefaultAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error searching entity by name: {name}. Exception: {ex}", name, ex);
            return null;
        }
    }

    /// <summary>
    /// Gets options from a master list filtered by text search.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="textSearch">Optional text to search within options.</param>
    /// <returns>A list of matching OptionList items.</returns>
    /// <!-- aidoc:v1 sig=569d223 body=bb7f2c6 -->
    public async Task<List<OptionList>> GetMasterListByIdAndTextSearchContaining(ObjectId id, string? textSearch)
    {
        return await GetOptionsByTextSearch(textSearch, id);
    }

    /// <summary>
    /// Retrieves paginated master lists with optional text filtering.
    /// </summary>
    /// <param name="filter">The pagination and filtering parameters.</param>
    /// <returns>A fluent queryable for MasterList results.</returns>
    /// <!-- aidoc-review:v1 severity=high kind=wrong_summary
    ///      "Summary claims the method 'retrieves paginated master lists', but the code only builds filters/sort and returns an IFindFluent without any pagination logic (no Skip/Limit)." -->
    /// <!-- aidoc-review:v1 severity=high kind=wrong_returns
    ///      "Returns tag specifies 'MasterList results', but the method is generic and returns IFindFluent<T, T>, not specifically MasterList." -->
    public IFindFluent<T, T> GetPaginatedMasterList(PaginationFilter filter)
    {
        var filterBuilder = Builders<T>.Filter;
        var sort = Builders<T>.Sort.Ascending("name");
        var filters = new List<FilterDefinition<T>>();

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

        // Verifica si el nombre contiene el valor de FilteredRequest.Text
        if (!string.IsNullOrEmpty(filter.FilteredRequest?.Text))
        {
            var textFilter = filter.FilteredRequest.Text;
            var escapedTextFilter = Regex.Escape(textFilter); // Escapa caracteres especiales para evitar problemas en la regex

            filters.Add(filterBuilder.Regex(d => d.Name,
                new BsonRegularExpression(escapedTextFilter, "i"))); // Case-insensitive regex match for title
        }

        return CreateFindFluent(filters, sort);
    }

    /// <summary>
    /// Retrieves paginated options within a master list with optional text filtering.
    /// </summary>
    /// <param name="filter">The pagination and filtering parameters.</param>
    /// <param name="listId">The ObjectId of the master list.</param>
    /// <returns>A list of filtered OptionList items.</returns>
    /// <!-- aidoc-review:v1 severity=high kind=wrong_summary
    ///      "Summary and method name claim 'paginated' results, but the code applies only text filtering with no skip/take/page logic — the PaginationFilter's pagination fields are unused." -->
    public async Task<List<OptionList>> GetPaginatedOptions(PaginationFilter filter, ObjectId listId)
    {
        //TODO: LOCALE
        var masterList = await FindById(listId);
        if (masterList == null) return [];
        var options = masterList.Options.AsQueryable();

        if (filter.FilteredRequest != null && !string.IsNullOrEmpty(filter.FilteredRequest.Text))
        {
            var textFilter = filter.FilteredRequest.Text.ToLower();
            options = options.Where(o => o.Name.ToLower().Contains(textFilter)
                                         || (o.Description != null && o.Description.ToLower().Contains(textFilter))
                                         || (o.OptionType != null && o.OptionType.ToLower().Contains(textFilter)));
        }

        return options.ToList();
    }

    /// <summary>
    /// Adds a new option to a master list.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="opt">The option element to add.</param>
    /// <returns>The newly created OptionList if successful; otherwise, null if duplicate exists.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all exceptions internally and returns null; it never throws an Exception to the caller, so documenting <exception cref=\"Exception\"> is misleading." -->
    public async Task<OptionList?> AddOptionToMasterList(ObjectId id, FilterOptionListElement opt)
    {
        var exist = await GetMasterListByIdAndSearchOptions(id, opt);
        if (exist.Count > 0) return null;
        var currentList = await FindById(id);
        var filter = Builders<T>.Filter.Eq(p => p.Id, id);
        var localeList = currentList?.DefaultLocale ?? LocaleEnum.Es;
        var newLocale = opt.IsFromAdmPanel.HasValue && opt.IsFromAdmPanel.Value
            ? opt.LocaleItems
            : GetNewItemLocale(localeList, opt.Name!);
        var option = new OptionList
        {
            Id = ObjectId.GenerateNewId(),
            Name = opt.Name!,
            OptionType = opt.OptionType ?? null,
            Description = opt.Description ?? null,
            Color = opt.Color ?? null,
            BgColor = opt.BgColor ?? null,
            IconCategory = opt.IconCategory ?? null,
            IconColor = opt.IconColor ?? null,
            IconDefault = opt.IconDefault ?? null,
            LocaleItems = newLocale
        };
        var update = Builders<T>.Update.Push(m => m.Options, option);

        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            return result.ModifiedCount > 0 ? option : null;
        }
        catch (Exception ex)
        {
            // Manejo de excepciones adecuado
            Log.Error("Error ADDING option to master list: {name}. Exception: {ex}", id, ex);
            return null;
        }
    }

    /// <summary>
    /// Retrieves all master list entities.
    /// </summary>
    /// <returns>An enumerable of all MasterList entities.</returns>
    /// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
    /// <!-- aidoc-review:v1 severity=high kind=wrong_summary
    ///      "Summary says 'Retrieves all master list entities' but the method is generic over T and operates on the configured Collection, not specifically MasterList entities." -->
    /// <!-- aidoc-review:v1 severity=high kind=wrong_returns
    ///      "Returns tag says 'An enumerable of all MasterList entities' but the method returns Task<IEnumerable<T>>, where T is a generic type parameter." -->
    /// <!-- aidoc-review:v1 severity=high kind=extra_exception
    ///      "<exception cref=\"Exception\"> implies the method may throw Exception, but the catch block swallows all exceptions and returns an empty list; the method does not throw Exception." -->
    public async Task<IEnumerable<T>> GetAll()
    {
        try
        {
            var result = await Collection.FindAsync(_ => true);
            return await result.ToListAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error getting all entities. Exception: {ex}", ex);
            return [];
        }
    }

    /// <summary>
    /// Retrieves all master list entities without options, returning only metadata.
    /// </summary>
    /// <returns>An enumerable of MasterListDto containing id, name, description, listType, and options count.</returns>
    /// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all exceptions internally (try/catch around the entire body) and returns an empty list; it never throws Exception to the caller, so the <exception cref=\"Exception\"> tag is misleading." -->
    public async Task<IEnumerable<MasterListDto>> GetAllWithoutOptions()
    {
        try
        {
            return await Collection
                .Find(FilterDefinition<T>.Empty)
                .Project(ml => new MasterListDto
                {
                    Id = ml.Id,
                    Name = ml.Name,
                    Description = ml.Description,
                    ListType = ml.ListType,
                    Options = ml.Options.Count
                })
                .ToListAsync();
        }
        catch (Exception ex)
        {
            Log.Error("Error getting all entities with no options. Exception: {ex}", ex);
            return [];
        }
    }

    /// <summary>
    /// Counts the total number of master list entities in the collection.
    /// </summary>
    /// <returns>The total count of entities.</returns>
    /// <exception cref="Exception">Logs errors and returns 0 on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=wrong_exception
    ///      "The method catches all Exception internally and returns 0, so it never throws Exception to callers. The <exception cref=\"Exception\"> tag misleads readers into expecting exceptions to propagate." -->
    public async Task<int> Count()
    {
        try
        {
            var result = await Collection.CountDocumentsAsync(_ => true);
            return (int)result;
        }
        catch (Exception ex)
        {
            Log.Error("Error counting all entities. Exception: {ex}", ex);
            return 0;
        }
    }

    /// <summary>
    /// Searches for options within a master list using multiple filter criteria.
    /// Uses MongoDB aggregation pipeline to apply filters and locale translations.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="filters">The filter criteria including text, name, description, and optionType.</param>
    /// <returns>A list of matching OptionList items ordered by name.</returns>
    /// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all Exception instances and logs them without rethrowing, so no Exception is propagated to the caller; documenting <exception cref=\"Exception\"> is misleading." -->
    public async Task<List<OptionList>> GetMasterListByIdAndSearchOptions(ObjectId id, FilterOptionListElement? filters)
    {
        try
        {
            if (filters == null)
                return [];

            // -----------------------------------------
            // Construcción de las condiciones de búsqueda
            // -----------------------------------------
            var conditions = new List<string>();

            if (!string.IsNullOrWhiteSpace(filters.OptionType))
                conditions.Add($@"{{ $eq: ['$$option.optionType', '{filters.OptionType}'] }}");

            if (!string.IsNullOrWhiteSpace(filters.Text))
            {
                var normalized = filters.Text.ToLowerInvariant();
                var pattern = BuildRegexPattern(normalized);

                conditions.Add($@"
                {{
                    $or: [
                        {{ $regexMatch: {{ input: '$$option.name', regex: '{pattern}', options: 'i' }} }},
                        {{ $regexMatch: {{ input: '$$option.description', regex: '{pattern}', options: 'i' }} }}
                    ]
                }}");
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(filters.Name))
                    conditions.Add($@"{{ $eq: ['$$option.name', '{filters.Name}'] }}");

                if (!string.IsNullOrWhiteSpace(filters.Description))
                    conditions.Add($@"{{ $eq: ['$$option.description', '{filters.Description}'] }}");
            }

            var filterCondition = conditions.Count == 0
                ? "true"
                : "$and: [" + string.Join(",", conditions) + "]";

            var localeCode = filters.Locale?.ToString().ToLower() ?? "default";

            // -----------------------------------------
            // Pipeline de agregación
            // -----------------------------------------
            var aggregationPipeline = new[]
            {
                // 0) MATCH
                BsonDocument.Parse(@$"{{ $match: {{ ""_id"": ObjectId(""{id}"") }} }}"),

                // 1) Primer $project: construye originalName / translatedName
                BsonDocument.Parse($@"
                {{
                    $project: {{
                        defaultLocale: '$defaultLocale',
                        options: {{
                            $map: {{
                                input: '$options',
                                as: 'o',
                                in: {{
                                    _id: '$$o._id',
                                    optionType: '$$o.optionType',
                                    description: '$$o.description',
                                    color: '$$o.color',
                                    bgColor: '$$o.bgColor',
                                    iconDefault: '$$o.iconDefault',
                                    iconCategory: '$$o.iconCategory',
                                    iconColor: '$$o.iconColor',
                                    initDate: '$$o.initDate',
                                    endDate: '$$o.endDate',
                                    isDefault: '$$o.isDefault',
                                    originalName: '$$o.name',
                                    originalDescription: '$$o.description',
                                    translatedName: {{
                                        $let: {{
                                            vars: {{
                                                li: {{
                                                    $first: {{
                                                        $filter: {{
                                                            input: {{
                                                                $map: {{
                                                                    input: {{ $objectToArray: '$$o.localeItems' }},
                                                                    as: 'pair',
                                                                    in: {{
                                                                        $cond: [
                                                                            {{ $eq: ['$$pair.k', '{localeCode}'] }},
                                                                            '$$pair.v',
                                                                            null
                                                                        ]
                                                                    }}
                                                                }}
                                                            }},
                                                            as: 'v',
                                                            cond: {{ $ne: ['$$v', null] }}
                                                        }}
                                                    }}
                                                }}
                                            }},
                                            in: {{ $ifNull: ['$$li.name', null] }}
                                        }}
                                    }},
                                    translatedDescription: {{
                                        $let: {{
                                            vars: {{
                                                li: {{
                                                    $first: {{
                                                        $filter: {{
                                                            input: {{
                                                                $map: {{
                                                                    input: {{ $objectToArray: '$$o.localeItems' }},
                                                                    as: 'pair',
                                                                    in: {{
                                                                        $cond: [
                                                                            {{ $eq: ['$$pair.k', '{localeCode}'] }},
                                                                            '$$pair.v',
                                                                            null
                                                                        ]
                                                                    }}
                                                                }}
                                                            }},
                                                            as: 'v',
                                                            cond: {{ $ne: ['$$v', null] }}
                                                        }}
                                                    }}
                                                }}
                                            }},
                                            in: {{ $ifNull: ['$$li.description', null] }}
                                        }}
                                    }}
                                }}
                            }}
                        }}
                    }}
                }}"),

                // 2) Segundo $project: aplica lógica real de locale y sobrescribe name/description
                BsonDocument.Parse($@"
                {{
                    $project: {{
                        defaultLocale: '$defaultLocale',
                        options: {{
                            $map: {{
                                input: '$options',
                                as: 'o',
                                in: {{
                                    _id: '$$o._id',
                                    optionType: '$$o.optionType',
                                    color: '$$o.color',
                                    bgColor: '$$o.bgColor',
                                    iconDefault: '$$o.iconDefault',
                                    iconCategory: '$$o.iconCategory',
                                    iconColor: '$$o.iconColor',
                                    initDate: '$$o.initDate',
                                    endDate: '$$o.endDate',
                                    isDefault: '$$o.isDefault',
                                    name: {{
                                        $cond: [
                                            {{ $eq: ['{localeCode}', '$defaultLocale'] }},
                                            '$$o.originalName',
                                            {{ $ifNull: ['$$o.translatedName', '$$o.originalName'] }}
                                        ]
                                    }},
                                    description: {{
                                        $cond: [
                                            {{ $eq: ['{localeCode}', '$defaultLocale'] }},
                                            '$$o.originalDescription',
                                            {{ $ifNull: ['$$o.translatedDescription', '$$o.originalDescription'] }}
                                        ]
                                    }}
                                }}
                            }}
                        }}
                    }}
                }}"),

                // 3) FILTRO FINAL
                BsonDocument.Parse($@"
                {{
                    $project: {{
                        options: {{
                            $filter: {{
                                input: '$options',
                                as: 'option',
                                cond: {{
                                    {filterCondition}
                                }}
                            }}
                        }}
                    }}
                }}")
            };

            // Ejecuta pipeline
            var result = await Collection.AggregateAsync<T>(aggregationPipeline);
            var documents = await result.ToListAsync();

            var optionsList = documents.FirstOrDefault()?.Options;
            return optionsList?.OrderBy(o => o.Name).ToList() ?? [];
        }
        catch (Exception ex)
        {
            Log.Error("Error searching entity by filters: {filters}. Exception: {ex}", filters, ex);
            return [];
        }
    }

    /// <summary>
    /// Updates a specific option within a master list with locale-aware field updates.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="newOpt">The OptionList with updated values.</param>
    /// <param name="locale">The locale for translation updates.</param>
    /// <returns>The updated OptionList if successful; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all exceptions internally and returns null; it does not propagate any exception to callers, so the <exception cref=\"Exception\"/> tag is misleading." -->
    public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList newOpt, LocaleEnum locale)
    {
        // 1. Evitar duplicados
        var exist = await GetMasterListByIdAndTextSearch(id, newOpt.Name, locale);
        if (exist.Count > 0)
            return exist.FirstOrDefault();

        // 2. Obtener master para reglas
        var master = await FindById(id);
        var defaultLocale = master?.DefaultLocale ?? LocaleEnum.Es;

        // 3. Filtro
        var filter = Builders<T>.Filter.And(
            Builders<T>.Filter.Eq("_id", id),
            Builders<T>.Filter.ElemMatch("options", Builders<T>.Filter.Eq("_id", newOpt.Id))
        );

        var updates = new List<UpdateDefinition<T>>();

        var isDefaultLocale = locale == defaultLocale;

        // ==================================================
        // NAME
        // ==================================================
        if (!string.IsNullOrEmpty(newOpt.Name) &&
            (master?.OptionListDetails?.Name?.IsRequired ?? false))
        {
            if (isDefaultLocale)
            {
                // actualizar nombre principal
                updates.Add(Builders<T>.Update.Set("options.$.name", newOpt.Name));
            }
            else
            {
                // actualizar traducción
                var localePath = $"options.$.localeItems.{locale.ToString().ToLower()}";
                updates.Add(Builders<T>.Update.Set($"{localePath}.name", newOpt.Name));
            }
        }

        // ==================================================
        // OTHER FIELDS
        // ==================================================
        if (newOpt.IconDefault != null)
            updates.Add(Builders<T>.Update.Set("options.$.iconDefault", newOpt.IconDefault));

        if (newOpt.IconCategory != null)
            updates.Add(Builders<T>.Update.Set("options.$.iconCategory", newOpt.IconCategory));
        if (newOpt.IconColor != null)
            updates.Add(Builders<T>.Update.Set("options.$.iconColor", newOpt.IconColor));

        if (newOpt.Color != null)
            updates.Add(Builders<T>.Update.Set("options.$.color", newOpt.Color));

        if (newOpt.BgColor != null)
            updates.Add(Builders<T>.Update.Set("options.$.bgColor", newOpt.BgColor));

        if (newOpt.Description != null)
            updates.Add(Builders<T>.Update.Set("options.$.description", newOpt.Description));

        if (newOpt.InitDate.HasValue)
            updates.Add(Builders<T>.Update.Set("options.$.initDate", newOpt.InitDate));

        if (newOpt.EndDate.HasValue)
            updates.Add(Builders<T>.Update.Set("options.$.endDate", newOpt.EndDate));

        if (newOpt.OptionType != null &&
            (master?.OptionListDetails?.OptionType?.IsRequired ?? false))
            updates.Add(Builders<T>.Update.Set("options.$.optionType", newOpt.OptionType));

        if (!updates.Any())
            return null;

        var update = Builders<T>.Update.Combine(updates);

        try
        {
            // 4. Ejecutar actualización
            var result = await Collection.UpdateOneAsync(filter, update);

            if (result.ModifiedCount == 0)
                return null;
            var updatedOpt = await FindOptionItemById(id, newOpt.Id!.Value, locale);

            return updatedOpt;
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateMasterListOption: {name}. Exception: {ex}", id, ex);
            return null;
        }
    }

    /// <summary>
    /// Updates a specific option within a master list with full replacement.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="newOpt">The OptionList with updated values.</param>
    /// <returns>The updated OptionList if successful; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The <exception cref=\"Exception\"> tag is misleading because the method catches Exception rather than throwing it. The described behavior (log and return null) matches the catch block, but this is not an exception thrown by the method." -->
    public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList newOpt)
    {
        var master = await FindById(id);
        if (master == null) return null;
        var filter = Builders<T>.Filter.And(
            Builders<T>.Filter.Eq("_id", id),
            Builders<T>.Filter.ElemMatch("options", Builders<T>.Filter.Eq("_id", newOpt.Id))
        );

        var update = Builders<T>.Update.Set("options.$", newOpt);

        try
        {
            // 4. Ejecutar actualización
            var result = await Collection.UpdateOneAsync(filter, update);

            if (result.ModifiedCount == 0)
                return null;
            var updatedOpt = await FindOptionItemById(id, newOpt.Id!.Value);

            return updatedOpt;
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateMasterListOption: {name}. Exception: {ex}", id, ex);
            return null;
        }
    }

    /// <summary>
    /// Deletes a specific option from a master list.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="deleteOptId">The ObjectId of the option to delete.</param>
    /// <returns>True if the option was deleted; otherwise, false.</returns>
    /// <!-- aidoc:v1 sig=e663f16 body=75627bc -->
    public async Task<bool> DeleteMasterListOption(ObjectId id, ObjectId deleteOptId)
    {
        // Define el filtro para encontrar el documento por su _id
        var filter = Builders<T>.Filter.Eq("_id", id);

        // Define la actualización con $pull para eliminar el objeto correspondiente en el array "options"
        var update = Builders<T>.Update.PullFilter("options", Builders<T>.Filter.And(
            Builders<T>.Filter.Eq("_id", deleteOptId)
        ));

        // Ejecuta la operación de actualización
        var result = await Collection.UpdateOneAsync(filter, update);
        if (result.ModifiedCount > 0) return true;

        return false;
    }

    /// <summary>
    /// Updates the metadata details for a master list.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="opt">The UpdateMasterListDetailsDto with updated values.</param>
    /// <returns>The updated UpdateMasterListDetailsDto if successful; otherwise, null.</returns>
    /// <exception cref="Exception">Logs errors and returns null on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all exceptions internally and never throws them, so documenting <exception cref=\"Exception\"> is misleading" -->
    /// <!-- aidoc-review:v1 severity=medium kind=wrong_summary
    ///      "Summary says 'metadata details' but the method updates option details (CanAddElement, OptionListDetails)" -->
    public async Task<UpdateMasterListDetailsDto?> UpdateOptionDetailsToMasterList(ObjectId id,
        UpdateMasterListDetailsDto opt)
    {
        var filter = Builders<T>.Filter.And(
            Builders<T>.Filter.Eq("_id", id));

        // Construir el update, especificando cada campo que se debe actualizar
        var updateDefinition = new List<UpdateDefinition<T>>();
        if (opt.CanAddElement != null)
            updateDefinition.Add(Builders<T>.Update.Set(c => c.CanAddElement, opt.CanAddElement));
        if (opt.OptionListDetails != null)
            updateDefinition.Add(Builders<T>.Update.Set(c => c.OptionListDetails, opt.OptionListDetails));

        var update = Builders<T>.Update.Combine(updateDefinition);

        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            if (result.ModifiedCount > 0) return opt; // Retorna el objeto actualizado si la modificación fue exitosa
            return null; // Retorna nulo si no se encontró el documento o no se modificó
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateMasterListOption: {name}. Exception: {ex}", id, ex);
            return null;
        }
    }

    /// <summary>
    /// Updates the name of a master list.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="name">The new name.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    /// <exception cref="Exception">Logs errors and returns false on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all exceptions internally and never rethrows them, so <exception cref=\"Exception\"> is misleading; callers cannot actually receive an exception from this method." -->
    public async Task<bool> UpdateMasterListName(ObjectId id, string name)
    {
        var filter = Builders<T>.Filter.And(
            Builders<T>.Filter.Eq("_id", id));

        var update = Builders<T>.Update.Set(c => c.Name, name);

        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            return result.ModifiedCount > 0;
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateMasterListOption: {name}. Exception: {ex}", id, ex);
            return false;
        }
    }

    /// <summary>
    /// Updates the description of a master list.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="description">The new description.</param>
    /// <returns>True if the update was successful; otherwise, false.</returns>
    /// <exception cref="Exception">Logs errors and returns false on failure.</exception>
    /// <!-- aidoc-review:v1 severity=high kind=extra_exception
    ///      "The method catches all exceptions internally and returns false; it never throws an Exception, so the <exception cref=\"Exception\"> tag is incorrect." -->
    public async Task<bool> UpdateMasterListDescription(ObjectId id, string description)
    {
        var filter = Builders<T>.Filter.And(
            Builders<T>.Filter.Eq("_id", id));

        var update = Builders<T>.Update.Set(c => c.Description, description);

        try
        {
            var result = await Collection.UpdateOneAsync(filter, update);
            return result.ModifiedCount > 0;
        }
        catch (Exception ex)
        {
            Log.Error("Error UpdateMasterListDescription {id} : {description}. Exception: {ex}", id, description, ex);
            return false;
        }
    }

    /// <summary>
    /// Removes an option from a master list by matching all its properties.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="oldOpt">The OptionList to remove.</param>
    /// <returns>True if the option was removed; otherwise, false.</returns>
    /// <exception cref="Exception">Logs errors and returns false on failure.</exception>
    /// <!-- aidoc-review:v1 severity=high kind=extra_exception
    ///      "The method catches all exceptions in a try/catch and returns false; it does not throw Exception, so the <exception> tag is misleading." -->
    public async Task<bool> RemoveMasterListOption(ObjectId id, OptionList oldOpt)
    {
        var filter = Builders<T>.Filter.Eq("_id", id);
        var pull = Builders<T>.Update.PullFilter("options", Builders<T>.Filter.And(
            Builders<T>.Filter.Eq("name", oldOpt.Name),
            Builders<T>.Filter.Eq("optionType", oldOpt.OptionType),
            Builders<T>.Filter.Eq("iconDefault", oldOpt.IconDefault),
            Builders<T>.Filter.Eq("iconCategory", oldOpt.IconCategory),
            Builders<T>.Filter.Eq("iconColor", oldOpt.IconColor),
            Builders<T>.Filter.Eq("color", oldOpt.Color),
            Builders<T>.Filter.Eq("bgColor", oldOpt.BgColor),
            Builders<T>.Filter.Eq("isDefault", oldOpt.IsDefault),
            Builders<T>.Filter.Eq("description", oldOpt.Description)
        ));

        try
        {
            var result = await Collection.UpdateOneAsync(filter, pull);
            return result.ModifiedCount > 0; // Retorna true si se modificó el documento (elemento eliminado)
        }
        catch (Exception ex)
        {
            Log.Error("Error removing option from MasterList: {name}. Exception: {ex}", id, ex);
            return false; // Retorna false si ocurre un error
        }
    }

    /// <summary>
    /// Creates necessary indexes for the MasterList collection.
    /// Currently creates text indexes for DiagnosisList on options.name, options.description, and options._id.
    /// </summary>
    /// <!-- aidoc-review:v1 severity=high kind=wrong_summary
    ///      "Documentation claims 'text indexes' are created, but the code uses Builders<T>.IndexKeys.Ascending(), which creates ascending (non-text) indexes." -->
    public override async Task CreateIndexes()
    {
        if (typeof(T) == typeof(DiagnosisList))
        {
            var options = new CreateIndexOptions
            { Background = true, Unique = false, LanguageOverride = "spanish", DefaultLanguage = "spanish" };

            var optionsIndex = new CreateIndexModel<T>(
                Builders<T>.IndexKeys.Ascending("options.name"), options);
            await Collection.Indexes.CreateOneAsync(optionsIndex);

            var optionsIndex2 = new CreateIndexModel<T>(
                Builders<T>.IndexKeys.Ascending("options.description"), options);
            await Collection.Indexes.CreateOneAsync(optionsIndex2);
            var optionsIndex3 = new CreateIndexModel<T>(
                Builders<T>.IndexKeys.Ascending("options._id"), options);
            await Collection.Indexes.CreateOneAsync(optionsIndex3);
        }
    }

    /// <summary>
    /// Searches for options across all master lists using text search.
    /// </summary>
    /// <param name="textSearch">Optional text to search within options.</param>
    /// <returns>A list of matching OptionList items.</returns>
    /// <!-- aidoc:v1 sig=484b092 body=48e3496 -->
    public async Task<List<OptionList>> GetMasterListByTextSearch(string? textSearch)
    {
        return await GetOptionsByTextSearch(textSearch);
    }

    /// <summary>
    /// Searches for options within a master list using text search with accent-aware regex.
    /// </summary>
    /// <param name="textSearch">Optional text to search within options.</param>
    /// <param name="id">Optional master list ObjectId to filter results.</param>
    /// <returns>A list of matching OptionList items ordered by name.</returns>
    /// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The method catches all exceptions internally (catch (Exception ex)) and never throws to the caller; the <exception cref=\"Exception\"> tag is misleading and should be removed. The catch-block description belongs in the summary or remarks instead." -->
    private async Task<List<OptionList>> GetOptionsByTextSearch(string? textSearch, ObjectId? id = null)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(textSearch))
                return [];

            // Normaliza el texto de búsqueda a minúsculas
            var normalizedTextSearch = textSearch.ToLowerInvariant();
            var regexPattern = BuildRegexPattern(normalizedTextSearch);
            // foreach (var c in normalizedTextSearch)
            // {
            //     switch (c)
            //     {
            //         case 'a':
            //             regexPattern.Append("[aáàäâ]");
            //             break;
            //         case 'e':
            //             regexPattern.Append("[eéèëê]");
            //             break;
            //         case 'i':
            //             regexPattern.Append("[iíìïî]");
            //             break;
            //         case 'o':
            //             regexPattern.Append("[oóòöô]");
            //             break;
            //         case 'u':
            //             regexPattern.Append("[uúùüû]");
            //             break;
            //         default:
            //             regexPattern.Append(c);
            //             break;
            //     }
            // }

            var aggregationPipeline = new[]
            {
                BsonDocument.Parse($"{{ $match: {{ \"_id\": ObjectId('{id}') }} }}"),
                BsonDocument.Parse($@"
                                        {{
                                            $project: {{
                                                options: {{
                                                    $filter: {{
                                                        input: '$options',
                                                        as: 'option',
                                                        cond: {{
                                                            $or: [
                                                                {{ $regexMatch: {{ input: '$$option.name', regex: '{regexPattern}', options: 'i' }} }},
                                                                {{ $regexMatch: {{ input: '$$option.description', regex: '{regexPattern}', options: 'i' }} }}
                                                            ]
                                                        }}
                                                    }}
                                                }}
                                            }}
                                        }}")
            };
            var result = await Collection.AggregateAsync<T>(aggregationPipeline);
            var documents = await result.ToListAsync();

            var optionsList = documents.FirstOrDefault()?.Options;
            optionsList = optionsList != null ? optionsList.OrderBy(o => o.Name).ToList() : [];

            return optionsList;
        }
        catch (Exception ex)
        {
            Log.Error("Error searching entity by textSearch: {name}. Exception: {ex}", textSearch, ex);
            return [];
        }
    }

    /// <summary>
    /// Creates a fluent query for paginated results with combined 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 T results.</returns>
    /// <!-- aidoc:v1 sig=704d715 body=2a35652 -->
    private IFindFluent<T, T> CreateFindFluent(List<FilterDefinition<T>> filters, SortDefinition<T> sort)
    {
        var combinedFilter = filters.Any()
            ? Builders<T>.Filter.And(filters)
            : Builders<T>.Filter.Empty; // Filtra todo si no hay filtros
        return Collection.Find(combinedFilter).Sort(sort);
    }

    /// <summary>
    /// Generates new locale items for a master list option based on a default locale.
    /// Creates LocaleItem entries for all locales except the specified default.
    /// </summary>
    /// <param name="localeList">The default locale to exclude from translations.</param>
    /// <param name="opt">The option name to use as default translation.</param>
    /// <returns>A Locale object with translations for all other locales.</returns>
    /// <!-- aidoc-review:v1 severity=medium kind=stale_summary
    ///      "Summary says items are created for all locales except 'the specified default', but the code also always skips LocaleEnum.Default in addition to the localeList parameter; only one exclusion is documented when two occur." -->
    private Locale GetNewItemLocale(LocaleEnum localeList, string opt)
    {
        var newLocale = new Locale();
        var localeType = typeof(Locale);
        var localeEnumType = typeof(LocaleEnum);

        // Recorremos todos los valores del enum excepto Default
        foreach (var enumValue in Enum.GetValues(localeEnumType).Cast<LocaleEnum>())
        {
            if (enumValue == LocaleEnum.Default)
                continue;

            // Si coincide con el locale a omitir → skip
            if (enumValue == localeList)
                continue;

            // Nombre de la propiedad del objeto Locale (Es, Pt, Ca, Eng, Zh...)
            var propertyName = enumValue.ToString();

            // Buscar la propiedad en el tipo Locale
            var prop = localeType.GetProperty(propertyName);
            if (prop != null)
                // Asignar dinámicamente nuevo LocaleItem
                prop.SetValue(newLocale, new LocaleItem
                {
                    Name = opt
                });
        }

        return newLocale;
    }

    // Método auxiliar para construir patrones regex con soporte de acentos

    /// Builds a regex pattern that matches accented and non-accented versions of vowels.
    /// Supports Spanish accent handling (á, é, í, ó, ú) and digits.
    /// </summary>
    /// <param name="input">The input string to build the pattern from.</param>
    /// <returns>A regex-compatible pattern string.</returns>
    /// <!-- aidoc:v1 sig=2992c00 body=ae88daf -->
    private static string BuildRegexPattern(string input)
    {
        var regexPattern = new StringBuilder();
        foreach (var c in input)
            switch (c)
            {
                case 'a':
                    regexPattern.Append("[aáàäâ]");
                    break;
                case 'e':
                    regexPattern.Append("[eéèëê]");
                    break;
                case 'i':
                    regexPattern.Append("[iíìïî]");
                    break;
                case 'o':
                    regexPattern.Append("[oóòöô]");
                    break;
                case 'u':
                    regexPattern.Append("[uúùüû]");
                    break;
                default:
                    if (char.IsDigit(c)) // Detectar si es un número
                        regexPattern.Append($"[{c}]"); // Tratar como un número literal
                    else
                        regexPattern.Append(c); // Escapar otros caracteres especiales
                    break;
            }

        return regexPattern.ToString();
    }

    /// <summary>
    /// Searches for options within a master list by name with locale translation.
    /// Uses MongoDB aggregation pipeline to apply locale-aware filtering.
    /// </summary>
    /// <param name="id">The ObjectId of the master list.</param>
    /// <param name="newOptName">The option name to search for.</param>
    /// <param name="locale">The locale for translation.</param>
    /// <returns>A list of matching OptionList items ordered by name.</returns>
    /// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
    /// <!-- aidoc-review:v1 severity=medium kind=extra_exception
    ///      "The <exception cref=\"Exception\"> tag documents an exception that is never thrown to the caller: the method catches all exceptions internally, logs them, and returns an empty list. The descriptive text 'Logs errors and returns empty list on failure' is accurate but should not be tagged as an exception." -->
    private async Task<List<OptionList>> GetMasterListByIdAndTextSearch(
        ObjectId id, string newOptName, LocaleEnum locale)
    {
        try
        {
            var localeCode = locale.ToString().ToLower();

            // -------------------------------------------------
            // Pipeline: obtener el documento y reconstruir
            // name/description según locale
            // -------------------------------------------------
            var pipeline = new[]
            {
                // 1) MATCH id
                BsonDocument.Parse($@"{{ $match: {{ ""_id"": ObjectId(""{id}"") }} }}"),

                // 2) Proyectar opciones con nombre traducido
                BsonDocument.Parse($@"
                {{
                    $project: {{
                        defaultLocale: '$defaultLocale',
                        options: {{
                            $map: {{
                                input: '$options',
                                as: 'o',
                                in: {{
                                    _id: '$$o._id',
                                    optionType: '$$o.optionType',
                                    description: '$$o.description',
                                    color: '$$o.color',
                                    bgColor: '$$o.bgColor',
                                    iconDefault: '$$o.iconDefault',
                                    iconCategory: '$$o.iconCategory',
                                    iconColorIconColor: '$$o.iconColor',
                                    initDate: '$$o.initDate',
                                    endDate: '$$o.endDate',
                                    isDefault: '$$o.isDefault',
                                    originalName: '$$o.name',
                                    originalDescription: '$$o.description',
                                    translatedName: {{
                                        $let: {{
                                            vars: {{
                                                li: {{
                                                    $first: {{
                                                        $filter: {{
                                                            input: {{
                                                                $map: {{
                                                                    input: {{ $objectToArray: '$$o.localeItems' }},
                                                                    as: 'pair',
                                                                    in: {{
                                                                        $cond: [
                                                                            {{ $eq: ['$$pair.k', '{localeCode}'] }},
                                                                            '$$pair.v',
                                                                            null
                                                                        ]
                                                                    }}
                                                                }}
                                                            }},
                                                            as: 'v',
                                                            cond: {{ $ne: ['$$v', null] }}
                                                        }}
                                                    }}
                                                }}
                                            }},
                                            in: {{ $ifNull: ['$$li.name', null] }}
                                        }}
                                    }}
                                }}
                            }}
                        }}
                    }}
                }}"),

                // 3) Segundo project: asignar Name final según locale
                BsonDocument.Parse($@"
                {{
                    $project: {{
                        defaultLocale: 1,
                        options: {{
                            $map: {{
                                input: '$options',
                                as: 'o',
                                in: {{
                                    _id: '$$o._id',
                                    optionType: '$$o.optionType',
                                    description: '$$o.originalDescription',
                                    color: '$$o.color',
                                    bgColor: '$$o.bgColor',
                                    iconDefault: '$$o.iconDefault',
                                    iconCategory: '$$o.iconCategory',
                                    iconColor: '$$o.iconColor',
                                    initDate: '$$o.initDate',
                                    endDate: '$$o.endDate',
                                    isDefault: '$$o.isDefault',
                                    name: {{
                                        $cond: [
                                            {{ $eq: ['{localeCode}', '$defaultLocale'] }},
                                            '$$o.originalName',
                                            {{ $ifNull: ['$$o.translatedName', '$$o.originalName'] }}
                                        ]
                                    }}
                                }}
                            }}
                        }}
                    }}
                }}"),

                // 4) Filtro final por nombre resultante
                BsonDocument.Parse($@"
                {{
                    $project: {{
                        options: {{
                            $filter: {{
                                input: '$options',
                                as: 'option',
                                cond: {{
                                    $eq: ['$$option.name', '{newOptName}']
                                }}
                            }}
                        }}
                    }}
                }}")
            };

            var result = await Collection.AggregateAsync<T>(pipeline);
            var docs = await result.ToListAsync();

            var opts = docs.FirstOrDefault()?.Options;
            return opts != null ? opts.OrderBy(o => o.Name).ToList() : [];
        }
        catch (Exception ex)
        {
            Log.Error("Error searching entity by textSearch: {name}. Exception: {ex}", newOptName, ex);
            return [];
        }
    }
}