using adas_core.Application.Exceptions;
using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.DTO;
using adas_core.Domain.Models.Filter;
using adas_core.Domain.Models.Masters;
using adas_core.Domain.Models.MongoModels;
using adas_core.Domain.Models.Responses;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;

namespace adas_core.Application.Services;

public class MasterListService<T> : IMasterListService<T> where T : MasterList, new()
{
    private readonly Lazy<IAdmissionService> _admissionService;
    private readonly string? _assetsDirectory;
    private readonly ILocalAuditService _auditService;
    private readonly Lazy<IClientMessageService> _clientMessageService;
    private readonly Lazy<IDischargeService> _dischargeService;
    private readonly Lazy<IDisplayService> _displayService;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly ILogger<MasterListService<T>> _logger;
    private readonly Lazy<IPatientService> _patientService;
    private readonly IServiceProvider _serviceProvider;
    private readonly ISubscribersService _subscribersService;
    private readonly Lazy<IUnitService> _unitService;

    public MasterListService(
        ILogger<MasterListService<T>> logger,
        IServiceProvider serviceProvider,
        Lazy<IClientMessageService> clientMessageService,
        ISubscribersService subscribersService,
        Lazy<IUnitService> unitService,
        Lazy<IDisplayService> displayService,
        Lazy<IPatientService> patientService,
        Lazy<IDischargeService> dischargeService,
        Lazy<IAdmissionService> admissionService,
        IOptions<ApiSettings> apiSettings,
        IHttpContextAccessor httpContextAccessor,
        ILocalAuditService auditService)
    {
        _logger = logger;
        _serviceProvider = serviceProvider;
        _clientMessageService = clientMessageService;
        _subscribersService = subscribersService;
        _unitService = unitService;
        _displayService = displayService;
        _patientService = patientService;
        _dischargeService = dischargeService;
        _admissionService = admissionService;
        _httpContextAccessor = httpContextAccessor;
        _auditService = auditService;
        if (apiSettings.Value.PathToDisplayAssets != null)
            _assetsDirectory = Path.Combine(apiSettings.Value.PathToDisplayAssets);
    }

    public async Task DeleteMasterListById(ObjectId id)
    {
        var repository = GetRepository();
        var masterList = await repository.FindById(id, LocaleEnum.Default);
        if (masterList == null)
        {
            _logger.LogInformation("Error deleting masterList not found, id: {id} ", id);
            return;
        }

        if (await IsInUseCount(masterList) > 0) throw new ConflictException(HttpEnum.ErrorMessage.ConflictDeleteFailed);
        await repository.Delete(id);
        await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, masterList, null);
        await SendMasterListBroadcast(masterList, OperationType.DeleteMasterList);
    }

    // public async Task<bool> AddOptionToMasterList(ObjectId id, OptionList opt)
    // {
    //     try
    //     {
    //         var repository = GetRepository();
    //         var result = await repository.AddOptionToMasterList(id, opt);
    //         if (result)
    //         {
    //             var masterList = await repository.FindById(id);
    //             await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList);
    //             await SendMasterListItemBroadcast(masterList,opt, opt, OperationType.AddMasterListItem);
    //
    //         }
    //
    //         return result;
    //
    //     }
    //     catch (Exception ex)
    //     {
    //         _logger.LogError("Error AddOptionToMasterList {name}. Exception: {ex}", typeof(T).Name, ex);
    //         return false;
    //     }
    // }
    public async Task<OptionList?> AddOptionToMasterList(ObjectId id, FilterOptionListElement opt)
    {
        try
        {
            var repository = GetRepository();
            //TODO: LOCALE
            var oldMasterList = await repository.FindById(id, opt.Locale);
            var result = await repository.AddOptionToMasterList(id, opt);

            if (result != null)
            {
                //TODO: LOCALE
                var masterList = await repository.FindById(id);
                if (masterList == null)
                    return null;

                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList);
                await SendMasterListItemBroadcast(masterList, result, result, OperationType.AddMasterListItem);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error AddOptionToMasterList {name}. Exception: {ex}", typeof(T).Name, ex);
            return null;
        }
    }

    public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList opt, string typeName,
        LocaleEnum locale)
    {
        try
        {
            var repository = GetRepository();
            //TODO: LOCALE
            var oldMasterList = await repository.FindById(id);
            var oldOption = oldMasterList?.Options.FirstOrDefault(o => o.Id == opt.Id);
            var result = await repository.UpdateMasterListOption(id, opt, locale);
            if (result != null)
            {
                //TODO: LOCALE
                var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T();
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem);
                await UpdatePatientItemList(id,
                    new UpdateOptionMasterListDto
                        { OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return null;
        }
    }

    public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList opt, string typeName)
    {
        try
        {
            var repository = GetRepository();
            //TODO: LOCALE
            var oldMasterList = await repository.FindById(id);
            var oldOption = oldMasterList?.Options.FirstOrDefault(o => o.Id == opt.Id);
            var result = await repository.UpdateMasterListOption(id, opt);
            if (result != null)
            {
                //TODO: LOCALE
                var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T();
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem);
                await UpdatePatientItemList(id,
                    new UpdateOptionMasterListDto
                        { OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return null;
        }
    }

    public async Task<OptionList?> UpdateFullMasterListOption(ObjectId id, OptionList opt, string typeName)
    {
        try
        {
            var repository = GetRepository();
            //TODO: LOCALE
            var oldMasterList = await repository.FindById(id);
            var oldOption = oldMasterList?.Options.FirstOrDefault(o => o.Id == opt.Id);
            var result = await repository.UpdateFullMasterListOption(id, opt);
            if (result != null)
            {
                //TODO: LOCALE
                var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T();
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListItemBroadcast(masterList, opt, oldOption, OperationType.UpdateMasterListItem);
                await UpdatePatientItemList(id,
                    new UpdateOptionMasterListDto
                        { OldOption = oldOption, UpdatedOption = opt, OptionId = opt.Id.ToString() }, typeName);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return null;
        }
    }

    public async Task<bool> DeleteMasterListOption(ObjectId id, ObjectId optId, string typeName)
    {
        try
        {
            var repository = GetRepository();
            var oldMasterList = await repository.FindById(id, LocaleEnum.Default);
            var opt = oldMasterList?.Options.First(c => c.Id == optId);
            var result = await repository.DeleteMasterListOption(id, optId);
            if (result && opt != null)
            {
                var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T();

                await DeletePatientItemList(id, opt, typeName);
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList);
                await SendMasterListItemBroadcast(masterList, opt, opt, OperationType.DeleteMasterListItem);

                return true;
            }

            return false;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error DeleteMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return false;
        }
    }

    public async Task<UpdateMasterListDetailsDto?> UpdateOptionDetailsToMasterList(ObjectId id,
        UpdateMasterListDetailsDto opt)
    {
        try
        {
            var repository = GetRepository();
            var oldMasterList = await repository.FindById(id);
            var result = await repository.UpdateOptionDetailsToMasterList(id, opt);
            if (result != null)
            {
                var masterList = await repository.FindById(id) ?? new T();
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return null;
        }
    }

    public async Task<bool> UpdateMasterListName(ObjectId id, string name)
    {
        try
        {
            var repository = GetRepository();
            var oldMasterList = await repository.FindById(id, LocaleEnum.Default);
            var result = await repository.UpdateMasterListName(id, name);
            if (result)
            {
                var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T();
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return false;
        }
    }

    public async Task<bool> UpdateMasterListDescription(ObjectId id, string name)
    {
        try
        {
            var repository = GetRepository();
            var oldMasterList = await repository.FindById(id, LocaleEnum.Default);
            var result = await repository.UpdateMasterListDescription(id, name);
            if (result)
            {
                var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T();
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return false;
        }
    }

    public async Task<bool> RemoveMasterListOption(ObjectId id, OptionList oldOpt)
    {
        try
        {
            var repository = GetRepository();
            var oldMasterList = await repository.FindById(id, LocaleEnum.Default);
            var result = await repository.RemoveMasterListOption(id, oldOpt);
            if (result)
            {
                var masterList = await repository.FindById(id, LocaleEnum.Default) ?? new T();
                await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldMasterList,
                    masterList);
                await SendMasterListBroadcast(masterList, OperationType.UpdateMasterList);
                await SendMasterListItemBroadcast(masterList, oldOpt, oldOpt, OperationType.DeleteMasterListItem);
            }

            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error UpdateMasterListOption {name}. Exception: {ex}", typeof(T).Name, ex);
            return false;
        }
    }

    public async Task<IEnumerable<T>> GetAllMasterList()
    {
        try
        {
            var repository = GetRepository();
            return await repository.GetAll();
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex);
            return [];
        }
    }

    public async Task<IEnumerable<MasterListDto>> GetAllMasterListWithoutOptions()
    {
        try
        {
            var repository = GetRepository();
            return await repository.GetAllWithoutOptions();
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex);
            return [];
        }
    }

    public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId, LocaleEnum locale)
    {
        try
        {
            var repository = GetRepository();
            return await repository.FindOptionItemById(masterId, optionId, locale);
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex);
            return null;
        }
    }

    public async Task<IEnumerable<MasterListWithPaginatedOptionsDto>> GetAllMasterListWithPaginatedOptions(
        PaginationFilter request)
    {
        try
        {
            var repository = GetRepository();
            var masterLists = await repository.GetAll();
            List<MasterListWithPaginatedOptionsDto> results = [];
            foreach (var masterList in masterLists)
            {
                var units = await IsInUse(masterList);
                var list = new MasterListWithPaginatedOptionsDto(masterList, request.PageSize, units);
                results.Add(list);
            }

            return results;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex);
            return [];
        }
    }

    public async Task<int> GetAllMasterListCount()
    {
        try
        {
            var repository = GetRepository();
            return await repository.Count();
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting all {name}. Exception: {ex}", typeof(T).Name, ex);
            return 0;
        }
    }

    public async Task<T?> GetMasterListById(ObjectId id, LocaleEnum? locale)
    {
        try
        {
            var repository = GetRepository();
            return await repository.FindById(id, locale);
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex);
            return null;
        }
    }

    public async Task<MasterListWithPaginatedOptionsDto?> GetMasterListByIdWithPaginatedOptions(ObjectId id,
        PaginationFilter request)
    {
        try
        {
            var repository = GetRepository();
            var list = await repository.FindById(id);
            if (list != null)
            {
                var units = await IsInUse(list);
                return new MasterListWithPaginatedOptionsDto(list, request.PageSize, units);
            }

            return null;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex);
            return null;
        }
    }

    public async Task<List<string>> GetMasterListOptionsNamesById(ObjectId id)
    {
        try
        {
            var repository = GetRepository();
            var list = await repository.FindById(id, LocaleEnum.Default);
            var nameList = new List<string>();

            list?.Options.ForEach(o => nameList.Add(o.Name));
            return nameList;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex);
            return [];
        }
    }

    public async Task<List<OptionList>> GetMasterListByIdAndTextSearch(ObjectId id, string? textSearch)
    {
        try
        {
            var repository = GetRepository();
            return await repository.GetMasterListByIdAndTextSearchContaining(id, textSearch);
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting by id: {id}. Exception: {ex}", id, ex);
            return [];
        }
    }

    public async Task<List<OptionList>> GetMasterListByIdAndSearchOptions(ObjectId id,
        FilterOptionListElement filterOption)
    {
        var repository = GetRepository();
        return await repository.GetMasterListByIdAndSearchOptions(id, filterOption);
    }

    public async Task<T?> GetMasterListByName(string name)
    {
        try
        {
            var repository = GetRepository();
            var result = await repository.FindByName(name);
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error getting by name: {name}. Exception: {ex}", name, ex);
            return null;
        }
    }

    public async Task<PaginationResponse<T>> GetPaginatedMasterList(PaginationFilter filter)
    {
        var repository = GetRepository();

        var result = repository.GetPaginatedMasterList(filter);

        var count = await result.CountDocumentsAsync();

        var data = await result.Skip((filter.PageNumber - 1) * filter.PageSize)
            .Limit(filter.PageSize)
            .ToCursorAsync();


        var dataList = await data.ToListAsync();

        return new PaginationResponse<T>(dataList, filter.PageNumber, filter.PageSize, count);
    }


    public async Task<PaginationResponse<MasterListWithPaginatedOptionsDto>> GetPaginatedMasterListWithPaginatedOptions(
        PaginationFilter listFilter, PaginationFilter optionFilter)
    {
        var repository = GetRepository();

        var result = repository.GetPaginatedMasterList(listFilter);

        var count = await result.CountDocumentsAsync();

        var data = await result.Skip((listFilter.PageNumber - 1) * listFilter.PageSize)
            .Limit(listFilter.PageSize)
            .ToCursorAsync();


        var dataList = await data.ToListAsync();
        List<MasterListWithPaginatedOptionsDto> results = [];

        foreach (var list in dataList)
        {
            var units = await IsInUse(list);
            var listDto = new MasterListWithPaginatedOptionsDto(list, optionFilter.PageSize, units);
            results.Add(listDto);
        }

        return new PaginationResponse<MasterListWithPaginatedOptionsDto>(results, listFilter.PageNumber,
            listFilter.PageSize, count);
    }

    public async Task<PaginationResponse<OptionList>> GetPaginatedOptions(PaginationFilter filter, ObjectId listId)
    {
        var repository = GetRepository();
        var result = await repository.GetPaginatedOptions(filter, listId);
        var count = result.Count;
        var data = result.Skip((filter.PageNumber - 1) * filter.PageSize)
            .Take(filter.PageSize)
            .ToList();
        return new PaginationResponse<OptionList>(data, filter.PageNumber, filter.PageSize, count);
    }

    public async Task<T?> InsertMasterList(T item)
    {
        try
        {
            var repository = GetRepository();
            await repository.InsertOneAsync(item);
            await SendMasterListBroadcast(item, OperationType.NewMasterList);
            var result = await repository.FindById(item.Id);
            await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, null, result);
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error inserting {name}: {item}. Exception: {ex}", typeof(T).Name, item, ex);
            return null;
        }
    }

    public async Task<T?> UpdateMasterList(T item)
    {
        try
        {
            var repository = GetRepository();
            var oldItem = repository.FindById(item.Id, LocaleEnum.Default);
            await repository.Update(item);
            await SendMasterListBroadcast(item, OperationType.UpdateMasterList);
            var result = await repository.FindById(item.Id, LocaleEnum.Default);
            await _auditService.CreateAuditLogAsync(_httpContextAccessor.HttpContext?.User!, oldItem, result);
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError("Error updating {name}: {item}. Exception: {ex}", typeof(T).Name, item, ex);
            return null;
        }
    }


    public async Task<ObjectId?> GetAssociatedList(ObjectId id, MasterListType masterListType1,
        MasterListType masterListType2)
    {
        var repository = GetRepository();
        await repository.FindById(id, LocaleEnum.Default);

        var units = await _unitService.Value.FindUnitsByMasterListId(id, masterListType1);
        var unitArray = units as Unit[] ?? (units ?? []).ToArray();
        if (!unitArray.Any()) return null;
        var unit = unitArray.First();

        switch (masterListType2)
        {
            case MasterListType.AltableOptionList: return unit.AltableOptionListId;
            case MasterListType.AllergyList: return unit.AllergyListId;
            case MasterListType.DestinationList: return unit.DestinationListId;
            case MasterListType.DiagnosisList: return unit.DiagnosisListId;
            case MasterListType.DischargeStatusList: return unit.DischargeStatusListId;
            case MasterListType.DoctorList: return unit.DoctorListId;
            case MasterListType.DoctorTypeList: return unit.DoctorTypeListId;
            case MasterListType.InternalDestinationList: return unit.InternalDestinationListId;
            case MasterListType.InsulationList: return unit.InsulationListId;
            case MasterListType.LanguageBarrierList: return unit.LanguageBarrierListId;
            case MasterListType.PassiveSittingList: return unit.PassiveSittingListId;
            case MasterListType.GenericList: return unit.GenericListId;
            case MasterListType.MobilityOptionList: return unit.MobilityOptionListId;
            case MasterListType.OriginList: return unit.OriginListId;
            case MasterListType.PatientStatusList: return unit.PatientStatusListId;
            case MasterListType.ProcedureList: return unit.ProcedureListId;
            case MasterListType.TestList: return unit.TestListId;
            case MasterListType.ServiceList: return unit.ServiceListId;
            case MasterListType.TherapeuticCeilingList: return unit.TherapeuticCeilingListId;
            case MasterListType.TreatmentList: return unit.TreatmentListId;
            case MasterListType.VisitOptionList: return unit.VisitOptionListId;
            case MasterListType.AccessControlList: return unit.AccessControlListId;
        }

        return null;
    }

    public async Task<List<string>> GetOptionsOfList(ObjectId id)
    {
        var repository = GetRepository();
        var list = await repository.FindById(id, LocaleEnum.Default);
        List<string> options = [];
        if (list == null) return options;
        foreach (var item in list.Options) options.Add(item.Name);
        return options;
    }

    private IMasterListRepository<T> GetRepository()
    {
        return _serviceProvider.GetRequiredService<IMasterListRepository<T>>();
    }

    private async Task UpdatePatientItemList(ObjectId id, UpdateOptionMasterListDto opt, string typeName)
    {
        // Necesito saber que unidades tienen el id de lista que estamos modificando
        var unitList = await _unitService.Value.FindUnitsByMasterListId(id);
        // Que pacientes dentro de esa/s unidades tienen el valor antiguo de la lista que estamos modificando
        var units = unitList as Unit[] ?? unitList.ToArray();
        await _patientService.Value.UpdatePatientMasterListItemChange(opt, units, typeName);
        await _dischargeService.Value.UpdatePatientMasterListItemChange(opt, units, typeName);
        await _admissionService.Value.UpdatePatientMasterListItemChange(opt, units, typeName);
    }

    private async Task DeletePatientItemList(ObjectId id, OptionList opt, string typeName)
    {
        // Necesito saber que unidades tienen el id de lista que estamos modificando
        var unitList = await _unitService.Value.FindUnitsByMasterListId(id);
        // Que pacientes dentro de esa/s unidades tienen el valor antiguo de la lista que estamos modificando
        var units = unitList as Unit[] ?? unitList.ToArray();
        await _patientService.Value.DeletePatientMasterListItem(opt, units, typeName);
        await _dischargeService.Value.DeletePatientMasterListItem(opt, units, typeName);
        await _admissionService.Value.DeletePatientMasterListItem(opt, units, typeName);
    }

    public Task SaveRequestAsync(ApiRequest apiRequest)
    {
        return Task.FromResult(Task.Run(async () => { await SaveRequest(apiRequest); }));
    }

    public Task SaveRequest(ApiRequest apiRequest)
    {
        return Task.CompletedTask;
    }

    private async Task SendMasterListBroadcast(T masterList, OperationType operation)
    {
        try
        {
            var type = masterList.GetType();
            var masterListTypeName = type.Name;
            if (!Enum.TryParse<MasterListType>(masterListTypeName, out var masterListType))
                return;

            var units = await _unitService.Value.FindUnitsByMasterListId(masterList.Id, masterListType);

            if (units == null)
                return;

            List<ObjectId> diplayIds = [];
            foreach (var unit in units)
            {
                var displayList = await _displayService.Value.GetByUnitId(unit.Id);
                displayList.ForEach(d => diplayIds.Add(d.Id));
            }

            var subscribers = _subscribersService.GetSubscribers()
                .Where(s => s.DisplayId.HasValue && diplayIds.Contains(s.DisplayId.Value)).ToList();

            foreach (var subscriber in subscribers)
                _ = _clientMessageService.Value.SendAsync(subscriber.Id, operation,
                    masterList.ReturnMasterListOptionsInLocaleIfExist(subscriber.Locale));
        }
        catch (Exception ex)
        {
            _logger.LogError("Exception sending masterList broadcast. Operation type: {op}. Exception: {ex}",
                operation.ToString(), ex);
        }
    }

    private async Task SendMasterListItemBroadcast(T masterList, OptionList? updatedItem, OptionList? oldItem,
        OperationType operation)
    {
        try
        {
            var type = masterList.GetType();
            var masterListTypeName = type.Name;
            if (!Enum.TryParse<MasterListType>(masterListTypeName, out var masterListType))
                return;

            var units = await _unitService.Value.FindUnitsByMasterListId(masterList.Id, masterListType);

            if (units == null)
                return;

            List<ObjectId> diplayIds = [];
            foreach (var unit in units)
            {
                var displayList = await _displayService.Value.GetByUnitId(unit.Id);
                displayList.ForEach(d => diplayIds.Add(d.Id));
            }

            var subscribers = _subscribersService.GetSubscribers()
                .Where(s => s.DisplayId.HasValue && diplayIds.Contains(s.DisplayId.Value)).ToList();
            object message;
            switch (operation)
            {
                case OperationType.AddMasterListItem:
                    message = new
                    {
                        newItem = updatedItem,
                        listType = masterListTypeName,
                        name = masterList.Name
                    };
                    break;
                case OperationType.DeleteMasterListItem:
                    message = new
                    {
                        deletedItem = oldItem,
                        listType = masterListTypeName,
                        name = masterList.Name
                    };
                    break;
                default:
                    message = new
                    {
                        updatedItem,
                        oldItem,
                        listType = masterListTypeName,
                        name = masterList.Name
                    };
                    break;
            }

            foreach (var subscriber in subscribers)
                _ = _clientMessageService.Value.SendAsync(subscriber.Id, operation, message);
        }
        catch (Exception ex)
        {
            _logger.LogError("Exception sending masterList broadcast. Operation type: {op}. Exception: {ex}",
                operation.ToString(), ex);
        }
    }

    private async Task<List<UnitInfoDto>> IsInUse(T list)
    {
        var units = await _unitService.Value.FindUnitsByMasterListId(list.Id, list.ListType);
        List<UnitInfoDto> unitInfoDtos = [];
        var unitArray = units as Unit[] ?? (units ?? []).ToArray();
        if (units != null && unitArray.Any())
            foreach (var unit in unitArray)
            {
                var unitInfoDto = new UnitInfoDto(unit);
                unitInfoDtos.Add(unitInfoDto);
            }

        return unitInfoDtos;
    }

    private async Task<long> IsInUseCount(T list)
    {
        return await _unitService.Value.CountUnitsByMasterListId(list.Id, list.ListType);
    }
}