using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Common.Interfaces.RabbitServices;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.Job;
using PARR.Core.Repositories.Interfaces.Unit;
using PARR.Core.Services.MatchingStatusService;
using PARR.Core.Services.UnitFilterService;
using PARR.Domain.Cache.Models;
using PARR.Domain.Common.Rabbit.Messages;
using PARR.Domain.Entities;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Entities.Job;
using PARR.Domain.Enums;
using PARR.TemplateMatcher.Services.Interfaces;
using PARR.TemplateMatcher.Settings;

namespace PARR.TemplateMatcher.Services.Implementations;

internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
{
    private const bool DefaultUsedTemplateState = false;
    private const bool DefaultUsedScheduleState = false;

    private readonly ILogger<GroupedTemplateSynchronizer> logger;
    private readonly IUnitFilterService unitFilterService;
    private readonly IUnitInValueRepository unitInValueService;
    private readonly IUnitRepository unitService;
    private readonly MqSettings mqSettings;
    private readonly IRabbitService mqService;
    private readonly ITemplateRepository templateService;
    private readonly IJobGroupRepository jobGroupService;
    private readonly ITemplateReuser templateReuser;
    private readonly IUnitRegionalEkPtkGroupRepository regionalEkPtkGroupService;
    private readonly IUnitFieldRepository unitFieldService;
    private readonly ITemplateDeactivator templateDeactivator;
    private readonly ITemplateNameNormalizer templateNameNormalizer;
    private readonly ITemplateUpdaterMqSender templateUpdaterMqSender;
    private readonly IMatchingStatusService matchingStatusService;


    public GroupedTemplateSynchronizer(
        ILogger<GroupedTemplateSynchronizer> logger,
        IUnitFilterService unitFilterService,
        IUnitInValueRepository unitInValueService,
        IUnitRepository unitService,
        MqSettings mqSettings,
        IRabbitService mqService,
        ITemplateRepository templateService,
        IJobGroupRepository jobGroupService,
        ITemplateReuser templateReuser,
        IUnitRegionalEkPtkGroupRepository regionalEkPtkGroupService,
        IUnitFieldRepository unitFieldService,
        ITemplateDeactivator templateDeactivator,
        ITemplateNameNormalizer templateNameNormalizer,
        ITemplateUpdaterMqSender templateUpdaterMqSender,
        IMatchingStatusService matchingStatusService
    )
    {
        this.logger = logger;
        this.unitFilterService = unitFilterService;
        this.unitInValueService = unitInValueService;
        this.unitService = unitService;
        this.mqSettings = mqSettings;
        this.mqService = mqService;
        this.templateService = templateService;
        this.jobGroupService = jobGroupService;
        this.templateReuser = templateReuser;
        this.regionalEkPtkGroupService = regionalEkPtkGroupService;
        this.unitFieldService = unitFieldService;
        this.templateDeactivator = templateDeactivator;
        this.templateNameNormalizer = templateNameNormalizer;
        this.templateUpdaterMqSender = templateUpdaterMqSender;
        this.matchingStatusService = matchingStatusService;
    }

    public async Task SyncTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
    {
        logger.LogWarning("GroupedTemplateSynchronizer: SyncTemplatesForJob вызван для JobId {JobId}. Это не поддерживаемая операция.", jobId);
    }
    public async Task SyncTemplatesForJobGroupAsync(Guid jobGroupId, HistoryInitiator initiator)
    {
        logger.LogDebug("Начало синхронизации шаблонов для JobGroup {JobGroupId}", jobGroupId);

        // === Проверка: уже запущена? ===
        var existingStatus = await matchingStatusService.GetStatusAsync(jobGroupId, SyncTaskEntityTypeEnum.JobGroup);
        if (existingStatus.DetailsJobGroups?.Any() == true)
        {
            logger.LogWarning("Синхронизация для JobGroup {JobGroupId} уже запущена. Пропускаем.", jobGroupId);
            return;
        }

        // === Устанавливаем статус "в процессе" ===
        var initialStatus = new MatchingStatusItemDto
        {
            DateStart = DateTimeOffset.UtcNow,
            Action = TemplateMatcherActionEnum.Sync,
            Comment = "Начало синхронизации"
        };
        await matchingStatusService.SetMatchingStatusAsync(
            jobGroupId,
            SyncTaskEntityTypeEnum.JobGroup,
            new MatchingStatusItem { Data = initialStatus, Timestamp = DateTimeOffset.UtcNow, Source = nameof(GroupedTemplateSynchronizer) },
            TimeSpan.FromMinutes(35)
        );

        try
        {
            // 1. Получить JobGroup и связанные Job'ы
            var jobGroup = await jobGroupService.Get()
                .AsNoTracking()
                .AsSingleQuery()
                .Include(jg => jg.GroupType)
                .Include(jg => jg.Jobs)
                    .ThenInclude(j => j.AutoControl)
                .Include(jg => jg.Jobs)
                    .ThenInclude(j => j.UnitFilters)
                        .ThenInclude(uf => uf.RelationshipFilters)
                            .ThenInclude(rf => rf.UnitField) // Подгрузим поля для фильтрации
                .Include(jg => jg.Jobs)
                    .ThenInclude(jg => jg.Tnk)
                .FirstOrDefaultAsync(jg => jg.Id == jobGroupId);

            if (jobGroup == null || jobGroup.Jobs == null || !jobGroup.Jobs.Any())
            {
                logger.LogWarning("JobGroup {JobGroupId} не найден или не содержит Job'ов.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "JobGroup не найден или пуст");
                return;
            }

            var jobsInGroup = jobGroup.Jobs.ToList();

            // 2. Найти Job с максимальным MaxValueRelationships
            var maxJob = jobsInGroup
                .Where(j => j.MaxValueRelationships.HasValue)
                .OrderByDescending(j => j.MaxValueRelationships)
                .FirstOrDefault();

            if (maxJob == null)
            {
                logger.LogWarning("В JobGroup {JobGroupId} не найдено Job с установленным MaxValueRelationships.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Не найден Job с MaxValueRelationships");
                return;
            }

            logger.LogDebug("Используется Job {JobId} с максимальным MaxValueRelationships ({MaxValue}) для фильтрации.", maxJob.Id, maxJob.MaxValueRelationships);

            // 3. Использовать unitFilterService для получения отфильтрованных юнитов с их связями
            // Это включает в себя все фильтры: UnitFilter, FieldFilter, RelationshipFilter, UmbrellaFilter
            logger.LogDebug("Получение отфильтрованных юнитов с их связями через UnitFilterService для Job {JobId}.", maxJob.Id);
            var unitFilterResults = await unitFilterService.GetUnitsByJobFilterAsync(maxJob.Id);

            if (unitFilterResults == null || !unitFilterResults.Any())
            {
                logger.LogInformation("Для JobGroup {JobGroupId} фильтры не дали Unit'ов с подходящими связями.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Фильтры не дали Unit'ов с подходящими связями");
                return;
            }

            logger.LogDebug("Получено {Count} юнитов с подходящими связями через UnitFilterService.", unitFilterResults.Count());

            // --- ФИЛЬТРАЦИЯ unitFilterResults (dto.Id) ---

            // 4. Фильтрация unitFilterResults по GroupingUnitFieldId (проверяем dto.Id)
            if (!jobGroup.GroupingUnitFieldId.HasValue)
            {
                logger.LogError("JobGroup {JobGroupId} не имеет GroupingUnitFieldId, необходимого для группировки.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Отсутствует GroupingUnitFieldId");
                return;
            }

            var groupingFieldId = jobGroup.GroupingUnitFieldId.Value;
            logger.LogDebug("Фильтрация юнитов (UnitFilterResultDto.Id) по GroupingUnitFieldId (FieldId={FieldId}).", groupingFieldId);

            // Загрузим значения поля GroupingUnitFieldId для всех Id из unitFilterResults
            var allUnitFilterResultIds = unitFilterResults.Select(dto => dto.Id).ToList();
            var groupingUnitValues = await unitInValueService.GetByUnitIdsAndFieldIdsAsync(allUnitFilterResultIds, new HashSet<Guid> { groupingFieldId });

            // Найдем Id юнитов, у которых есть значение в GroupingUnitFieldId
            var validUnitFilterResultIds = groupingUnitValues
                .Where(uv => uv.Value != null && !string.IsNullOrEmpty(uv.Value.Value))
                .Select(uv => uv.UnitId)
                .ToHashSet();

            // Оставляем только те UnitFilterResultDto, чей Id проходит фильтр
            var filteredUnitFilterResultsByGrouping = unitFilterResults
                .Where(dto => validUnitFilterResultIds.Contains(dto.Id))
                .ToList();

            logger.LogDebug("После фильтрации по GroupingUnitFieldId осталось {Count} UnitFilterResultDto.", filteredUnitFilterResultsByGrouping.Count);

            if (!filteredUnitFilterResultsByGrouping.Any())
            {
                logger.LogInformation("После фильтрации по GroupingUnitFieldId в JobGroup {JobGroupId} не осталось юнитов.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Нет юнитов после фильтрации по GroupingUnitFieldId");
                return;
            }

            // 5. Фильтрация unitFilterResults по РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК (проверяем dto.Id)
            var workGroupFieldId = await GetFieldIdByAihitNameAsync("РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК");

            logger.LogDebug("Фильтрация юнитов (UnitFilterResultDto.Id) по полю 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' (FieldId={FieldId}).", workGroupFieldId);

            // Загрузим значения поля РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК для Id из filteredUnitFilterResultsByGrouping
            var allFilteredUnitFilterResultIds = filteredUnitFilterResultsByGrouping.Select(dto => dto.Id).ToList();
            var workGroupValues = await unitInValueService.GetByUnitIdsAndFieldIdsAsync(allFilteredUnitFilterResultIds, new HashSet<Guid> { workGroupFieldId });

            // Получим разрешенные значения из regionalEkPtkGroupService
            var allowedValueIds = regionalEkPtkGroupService.Get()
                .Select(g => g.FieldValueId)
                .ToHashSet();
            logger.LogDebug("Найдено {Count} разрешенных значений для поля 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК'.", allowedValueIds.Count);

            // Найдем Id юнитов, у которых значение в РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК разрешено
            var validUnitFilterResultIdsForWorkGroup = workGroupValues
                .Where(uv => uv.Value != null && allowedValueIds.Contains(uv.Value.Id))
                .Select(uv => uv.UnitId)
                .ToHashSet();

            // Оставляем только те UnitFilterResultDto, чей Id проходит фильтр
            var finalFilteredUnitFilterResults = filteredUnitFilterResultsByGrouping
                .Where(dto => validUnitFilterResultIdsForWorkGroup.Contains(dto.Id))
                .ToList();

            logger.LogDebug("После фильтрации по 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' осталось {Count} UnitFilterResultDto.", finalFilteredUnitFilterResults.Count);

            if (!finalFilteredUnitFilterResults.Any())
            {
                logger.LogInformation("После фильтрации по 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' в JobGroup {JobGroupId} не осталось юнитов.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Нет юнитов после фильтрации по 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК'");
                return;
            }

            // --- ПОСТРОЕНИЕ ОБРАТНОГО ОТОБРАЖЕНИЯ (после фильтрации) ---
            logger.LogDebug("Построение обратного отображения: связанные юниты -> юниты, связанные с ними (после фильтрации).");
            var reverseMapping = new Dictionary<Guid, List<Guid>>();
            foreach (var dto in finalFilteredUnitFilterResults)
            {
                List<Guid> relatedUnitIds;
                if (maxJob.IsParentRelationships == true)
                {
                    // dto.Id - это ParentUnitId, связанные - ChildUnitIds (expectedUnitIds) -> dto.Id идет в UnitsInTemplate
                    // relatedUnitIds - это ChildUnitIds, которые станут UnitId шаблона
                    relatedUnitIds = dto.Children.Select(c => c.UnitId).ToList(); 
                }
                else
                {
                    // dto.Id - это ChildUnitId, связанные - ParentUnitIds (expectedUnitIds) -> dto.Id идет в UnitsInTemplate
                    // relatedUnitIds - это ParentUnitIds, которые станут UnitId шаблона
                    relatedUnitIds = dto.Parents.Select(p => p.UnitId).ToList();
                }

                // dto.Id - это юнит, который прошел фильтры, он будет в UnitsInTemplate
                var unitInTemplateId = dto.Id;

                foreach (var relatedUnitId in relatedUnitIds)
                {
                    // relatedUnitId уже прошел все фильтры, т.к. dto.Id (его связанный юнит) прошел фильтры
                    if (!reverseMapping.ContainsKey(relatedUnitId))
                    {
                        reverseMapping[relatedUnitId] = new List<Guid>();
                    }
                    reverseMapping[relatedUnitId].Add(unitInTemplateId);
                }
            }

            logger.LogDebug("Построено {Count} записей в обратном отображении.", reverseMapping.Count);

            if (!reverseMapping.Any())
            {
                logger.LogInformation("После построения обратного отображения в JobGroup {JobGroupId} не осталось юнитов.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Нет юнитов после построения обратного отображения");
                return;
            }

            // 6. Внутренняя группировка по ОТВЕТСТВЕННЫЙ_ЗА_ЭК / РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК (для юнитов в UnitsInTemplate)
            logger.LogDebug("Внутренняя группировка по полю (IsGroupByResponsible={IsGroupByResponsible}).", jobGroup.IsGroupByResponsible);

            var groupingFieldIdForInnerGrouping = jobGroup.IsGroupByResponsible == true
                ? await GetFieldIdByAihitNameAsync("ОТВЕТСТВЕННЫЙ_ЗА_ЭК")
                : await GetFieldIdByAihitNameAsync("РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК");

            // Загрузим значения поля для *всех* юнитов, которые могут быть в UnitsInTemplate
            // Это все юниты из всех списков в reverseMapping.Values
            var allUnitsInTemplate = reverseMapping.Values.SelectMany(list => list).Distinct().ToList();
            var innerGroupingValues = await unitInValueService.GetByUnitIdsAndFieldIdsAsync(allUnitsInTemplate, new HashSet<Guid> { groupingFieldIdForInnerGrouping });

            // Создадим маппинг UnitId (из UnitsInTemplate) -> значение поля для внутренней группировки
            var unitInTemplateToInnerGroupingValueMap = innerGroupingValues
                .Where(uv => uv.Value != null && !string.IsNullOrEmpty(uv.Value.Value))
                .ToDictionary(uv => uv.UnitId, uv => uv.Value.Value);

            // 7. Основной цикл обработки: итерируемся по potentialUnitIds (UnitId шаблонов)
            var expectedTemplateKeys = new HashSet<(Guid JobId, Guid UnitId, int Index)>();

            foreach (var kvpOuter in reverseMapping)
            {
                var potentialUnitId = kvpOuter.Key;
                var unitsInTemplateForThisPotentialUnitId = kvpOuter.Value;

                logger.LogDebug("Обработка потенциального шаблона для UnitId {PotentialUnitId} с {Count} юнитами в UnitsInTemplate до внутренней группировки.", potentialUnitId, unitsInTemplateForThisPotentialUnitId.Count);

                // --- ВНУТРЕННЯЯ ГРУППИРОВКА ---
                // Сгруппируем *юниты из UnitsInTemplate* для *этого* potentialUnitId по значению поля
                var innerGroupedUnitsInTemplate = unitsInTemplateForThisPotentialUnitId
                    .GroupBy(unitId => unitInTemplateToInnerGroupingValueMap.GetValueOrDefault(unitId, "Нет данных"))
                    .ToList();

                logger.LogDebug("Для UnitId {PotentialUnitId}: сформировано {Count} внутренних групп UnitsInTemplate.", potentialUnitId, innerGroupedUnitsInTemplate.Count);

                // 8. Цикл по внутренним группам UnitsInTemplate
                foreach (var innerGroup in innerGroupedUnitsInTemplate)
                {
                    var groupingValueName = innerGroup.Key;

                    var unitsInTemplateInInnerGroup = innerGroup.ToList(); // Список юнитов (UnitId), связанных с potentialUnitId и имеющих одно и то же значение поля

                    logger.LogDebug("Обработка внутренней группы '{GroupingValue}' для UnitId {PotentialUnitId} с {Count} юнитами.", groupingValueName, potentialUnitId, unitsInTemplateInInnerGroup.Count);

                    // Разбиваем юниты из *этой* внутренней группы на подгруппы по maxJob.MaxValueRelationships
                    int maxValueForSplitting = maxJob.MaxValueRelationships!.Value;
                    var unitsInTemplateSubGroups = unitsInTemplateInInnerGroup
                        .Select((id, index) => new { id, groupIndex = index / maxValueForSplitting })
                        .GroupBy(x => x.groupIndex)
                        .Select(g => g.Select(x => x.id).ToList())
                        .ToList();

                    logger.LogDebug("Внутренняя группа '{GroupingValue}' для UnitId {PotentialUnitId}: разбит на {GroupCount} подгрупп UnitsInTemplate.", groupingValueName, potentialUnitId, unitsInTemplateSubGroups.Count);

                    // 9. Цикл по подгруппам UnitsInTemplate для создания/обновления шаблонов
                    for (int i = 1; i <= unitsInTemplateSubGroups.Count; i++) // Индекс начинается с 1
                    {
                        var unitsInTemplateSubGroup = unitsInTemplateSubGroups[i - 1]; // корректируем индекс для доступа к коллекции
                        var subGroupSize = unitsInTemplateSubGroup.Count;
                        logger.LogDebug("Обработка подгруппы {Index} внутренней группы '{GroupingValue}' для UnitId {PotentialUnitId}, размер UnitsInTemplate {Size}.", i, groupingValueName, potentialUnitId, subGroupSize);

                        Job? targetJob = SelectTargetJob(jobsInGroup, subGroupSize, maxJob);

                        // --- Добавляем ключ в список ожидаемых прямо здесь ---
                        expectedTemplateKeys.Add((targetJob.Id, potentialUnitId, i)); // potentialUnitId - это UnitId шаблона

                        var existingTemplatesForRelationship = await templateService.Get()
                            .AsNoTracking()
                            .Include(t => t.Unit)
                            .Include(t => t.Job)
                                .ThenInclude(t => t!.Tnk)
                            .Include(t => t.Job)
                                .ThenInclude(t => t!.Group)
                                    .ThenInclude(t => t!.GroupType)
                            .Include(t => t.UnitsInTemplate)
                                .ThenInclude(uit => uit.Unit)
                            .Where(t => t.JobId == targetJob.Id && t.UnitId == potentialUnitId && t.Index == i && t.StatusTypeId == TemplateStatusTypeEnum.Used)
                            .ToListAsync();

                        var existingTemplateForSubGroup = existingTemplatesForRelationship.FirstOrDefault();

                        if (existingTemplateForSubGroup != null)
                        {
                            // === 1. Получаем текущие и новые ID юнитов ===
                            var currentUnitIds = existingTemplateForSubGroup.UnitsInTemplate.Select(uit => uit.UnitId).ToList();
                            var proposedUnitIds = unitsInTemplateSubGroup.ToList();

                            // === 2. Сравниваем детерминированно с сортировкой по имени ===
                            var allUnitIdsForSort = currentUnitIds.Concat(proposedUnitIds).Distinct().ToList();
                            var unitNamesForSort = await unitService.Get()
                                .AsNoTracking()
                                .Where(u => allUnitIdsForSort.Contains(u.Id))
                                .ToDictionaryAsync(u => u.Id, u => u.Name ?? u.Id.ToString());

                            var sortedCurrentUnitIds = currentUnitIds
                                .OrderBy(id => unitNamesForSort.GetValueOrDefault(id, id.ToString()))
                                .ToList();

                            var sortedProposedUnitIds = proposedUnitIds
                                .OrderBy(id => unitNamesForSort.GetValueOrDefault(id, id.ToString()))
                                .ToList();

                            bool unitsAreEqual = sortedCurrentUnitIds.SequenceEqual(sortedProposedUnitIds);

                            if (unitsAreEqual)
                            {
                                logger.LogDebug("Шаблон {TemplateId} актуален по юнитам и их порядку (после сортировки).", existingTemplateForSubGroup.Id);
                                existingTemplateForSubGroup.UnitsInTemplate = sortedProposedUnitIds.Select(id => new UnitsInTemplate { UnitId = id }).ToList();
                                var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(existingTemplateForSubGroup);

                                if (!string.Equals(existingTemplateForSubGroup.Name, expectedName, StringComparison.OrdinalIgnoreCase))
                                {
                                    logger.LogDebug("Шаблон {TemplateId} требует обновления имени.", existingTemplateForSubGroup.Id);
                                    var updateRequest = new TemplateUpdaterMq
                                    {
                                        TemplateId = existingTemplateForSubGroup.Id,
                                        JobId = targetJob.Id,
                                        UnitId = potentialUnitId,
                                        Name = expectedName,
                                        IsActiveTemplate = existingTemplateForSubGroup.IsActiveTemplate,
                                        IsActiveSchedule = existingTemplateForSubGroup.IsActiveSchedule,
                                        IsNew = false,
                                        Index = i,
                                        StatusTypeId = TemplateStatusTypeEnum.Used,
                                        Initiator = initiator,
                                        UnitsInTemplate = sortedProposedUnitIds
                                    };
                                    await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
                                }
                                else
                                {
                                    logger.LogDebug("Шаблон {TemplateId} полностью актуален.", existingTemplateForSubGroup.Id);
                                }
                            }
                            else
                            {
                                logger.LogDebug("Шаблон {TemplateId} требует обновления юнитов или их порядка (после сортировки).", existingTemplateForSubGroup.Id);
                                var newTargetJob = SelectTargetJob(jobsInGroup, unitsInTemplateSubGroup.Count, maxJob); // Размер - из подмножества
                                if (newTargetJob.Id != existingTemplateForSubGroup.JobId)
                                {
                                    logger.LogDebug("Job для шаблона {TemplateId} изменился.", existingTemplateForSubGroup.Id);
                                }
                                await UpdateTemplateUnitsAsync(existingTemplateForSubGroup, sortedProposedUnitIds, newTargetJob, initiator, i);
                            }
                        }
                        else
                        {
                            var reusableTemplate = await templateReuser.TryReuseOneUnusedTemplateAsync(targetJob.Id, potentialUnitId, initiator);
                            if (reusableTemplate != null)
                            {
                                logger.LogInformation("Переиспользован шаблон {TemplateId} для Job {JobId}, связанного юнита {RelationshipId}, Index {Index}.", reusableTemplate.Id, targetJob.Id, potentialUnitId, i);

                                var tempTemplateForName = new Template
                                {
                                    Id = reusableTemplate.Id,
                                    Name = reusableTemplate.Name,
                                    JobId = targetJob.Id,
                                    UnitId = potentialUnitId,
                                    Index = i,
                                    Job = targetJob,
                                    Unit = reusableTemplate.Unit,
                                    UnitsInTemplate = unitsInTemplateSubGroup.Select(id => new UnitsInTemplate { UnitId = id }).ToList()
                                };

                                var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);

                                var updateRequest = new TemplateUpdaterMq
                                {
                                    TemplateId = reusableTemplate.Id,
                                    JobId = targetJob.Id,
                                    UnitId = potentialUnitId,
                                    Name = expectedName,
                                    IsActiveTemplate = targetJob.AutoControl?.InitUsedTemplateState ?? DefaultUsedTemplateState,
                                    IsActiveSchedule = targetJob.AutoControl?.InitUsedScheduleState ?? DefaultUsedScheduleState,
                                    StatusTypeId = TemplateStatusTypeEnum.Used,
                                    Initiator = initiator,
                                    IsNew = true,
                                    Index = i,
                                    UnitsInTemplate = unitsInTemplateSubGroup
                                };

                                await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
                            }
                            else
                            {
                                logger.LogDebug("Создание нового шаблона для Job {JobId}, связанного юнита {RelationshipId}, Index {Index}, с {Count} юнитами.", targetJob.Id, potentialUnitId, i, unitsInTemplateSubGroup.Count);
                                await CreateGroupedTemplateAsync(targetJob.Id, potentialUnitId, unitsInTemplateSubGroup, i, initiator);
                            }
                        }
                    }
                }
            }

            // === Деактивация ===
            // Теперь expectedTemplateKeys уже собран

            // Получаем ВСЕ шаблоны для JobGroup (не только для текущих potentialUnitIds)
            var allJobIdsInGroup = jobsInGroup.Select(j => j.Id).ToHashSet();
            var allExistingTemplatesInGroup = await templateService.Get()
                .AsNoTracking()
                .Include(t => t.Unit)
                .Include(t => t.UnitsInTemplate)
                .Where(t => allJobIdsInGroup.Contains(t.JobId) &&
                            t.StatusTypeId == TemplateStatusTypeEnum.Used &&
                            t.Job!.GroupId == jobGroupId)
                .ToListAsync();

            foreach (var existingTemplate in allExistingTemplatesInGroup)
            {
                var key = (existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index ?? -1);
                if (!expectedTemplateKeys.Contains(key))
                {
                    logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, Relationship {UnitId}, Index {Index}).",
                        existingTemplate.Id, existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index);
                    await templateDeactivator.DeactivateTemplateAsync(existingTemplate, initiator);
                }
            }

            // === Успешное завершение ===
            await UpdateMatchingStatusAsync(jobGroupId, "Синхронизация завершена успешно");
            await matchingStatusService.DeleteMatchingStatusAsync(jobGroupId, SyncTaskEntityTypeEnum.JobGroup);
            logger.LogInformation("Синхронизация шаблонов завершена для JobGroup {JobGroupId}.", jobGroupId);
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Ошибка при синхронизации JobGroup {JobGroupId}", jobGroupId);
            await UpdateMatchingStatusAsync(jobGroupId, $"Ошибка: {ex.Message}");
            throw;
        }
    }

    public async Task UpdateTemplatesForJobAsync(Guid jobId, HistoryInitiator initiator)
    {
        logger.LogWarning("GroupedTemplateSynchronizer: UpdateTemplatesForJob вызван для JobId {JobId}. Это не поддерживаемая операция. Используйте SyncTemplatesForJobGroup для обновления.", jobId);
        return;
    }

    private Job SelectTargetJob(List<Job> jobsInGroup, int subGroupSize, Job maxJob)
    {
        Job? targetJob = jobsInGroup
            .Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value == subGroupSize)
            .FirstOrDefault();

        if (targetJob == null)
        {
            targetJob = jobsInGroup
                .Where(j => j.MaxValueRelationships.HasValue && j.MaxValueRelationships.Value >= subGroupSize)
                .OrderBy(j => j.MaxValueRelationships!.Value)
                .FirstOrDefault();
        }

        if (targetJob == null)
        {
            targetJob = maxJob;
            logger.LogDebug("Для подгруппы размером {Size} не найден подходящий Job, используем maxJob {MaxJobId}.", subGroupSize, maxJob.Id);
        }
        else
        {
            logger.LogDebug("Для подгруппы размером {Size} выбран Job {TargetJobId} с MaxValueRelationships {MaxValue}.", subGroupSize, targetJob.Id, targetJob.MaxValueRelationships);
        }

        return targetJob;
    }

    private async Task UpdateTemplateUnitsAsync(Template template, List<Guid> newUnitIds, Job targetJob, HistoryInitiator initiator, int newIndex)
    {
        template.StatusTypeId = TemplateStatusTypeEnum.Updating;
        template.DateModified = DateTimeOffset.UtcNow;
        if (!await templateService.CommitAsync(initiator))
        {
            logger.LogError("Не удалось перевести шаблон {TemplateId} в Updating для обновления юнитов.", template.Id);
            return;
        }

        var tempTemplateForName = new Template
        {
            Id = template.Id,
            Name = template.Name,
            JobId = targetJob.Id,
            UnitId = template.UnitId,
            Index = newIndex,
            Job = targetJob,
            Unit = template.Unit,
            UnitsInTemplate = newUnitIds.Select(id => new UnitsInTemplate { UnitId = id }).ToList()
        };

        var expectedName = await templateNameNormalizer.GetNormalizedTemplateNameAsync(tempTemplateForName);
        //var nextRun = await nextRunService.GetNextRunForTemplateAsync(template.Id, false);
        var updateRequest = new TemplateUpdaterMq
        {
            TemplateId = template.Id,
            JobId = targetJob.Id,
            UnitId = template.UnitId,
            Name = expectedName,
            IsActiveTemplate = template.IsActiveTemplate,
            IsActiveSchedule = template.IsActiveSchedule,
            //LastRun = template.LastRun,
            //NextRun = nextRun,
            IsNew = false,
            Index = newIndex,
            StatusTypeId = TemplateStatusTypeEnum.Used,
            Initiator = initiator,
            UnitsInTemplate = newUnitIds
        };

        await templateUpdaterMqSender.SendTemplateUpdateMessageAsync(updateRequest);
    }

    private async Task CreateGroupedTemplateAsync(Guid jobId, Guid relationshipUnitId, List<Guid> unitIds, int index, HistoryInitiator initiator)
    {
        logger.LogInformation("Создание нового группового шаблона для Job {JobId}, связанного юнита {RelationshipUnitId}, Index {Index}, с {Count} юнитами.", jobId, relationshipUnitId, index, unitIds.Count);

        var mqRequest = new TemplateGeneratorMq
        {
            JobId = jobId,
            UnitId = relationshipUnitId,
            UnitsInTemplate = unitIds,
            Index = index,
            HistoryInitiator = initiator
        };

        var result = await mqService.SendAsync(mqSettings.TemplateGenerator, new List<object> { mqRequest });

        if (!result.IsSuccess)
            logger.LogError("Ошибка отправки команды создания группового шаблона для Job {JobId}, связанного юнита {RelationshipUnitId}, Index {Index}.", jobId, relationshipUnitId, index);
    }


    private async Task UpdateMatchingStatusAsync(Guid jobGroupId, string comment)
    {
        var status = new MatchingStatusItemDto
        {
            DateStart = DateTimeOffset.UtcNow,
            Action = TemplateMatcherActionEnum.Sync,
            Comment = comment
        };

        await matchingStatusService.SetMatchingStatusAsync(
            jobGroupId,
            SyncTaskEntityTypeEnum.JobGroup,
            new MatchingStatusItem { Data = status, Timestamp = DateTimeOffset.UtcNow, Source = nameof(GroupedTemplateSynchronizer) },
            TimeSpan.FromMinutes(30)
        );
    }

    #region Вспомогательные методы
    private async Task<Guid> GetFieldIdByAihitNameAsync(string fieldName)
    {
        var field = await unitFieldService.GetByAihitNameAsync(fieldName);
        if (field == null)
        {
            logger.LogError("Поле '{FieldName}' не найдено в справочнике полей.", fieldName);
            throw new InvalidOperationException($"Поле '{fieldName}' не найдено в справочнике полей.");
        }
        return field.Id;
    }
    #endregion
}