using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.Job;
using PARR.Core.Services.MatchingStatusService;
using PARR.Core.Services.UnitFilterService;
using PARR.Domain.Cache.Models;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Entities.Job;
using PARR.Domain.Enums;
using PARR.TemplateMatcher.Services.Interfaces;

namespace PARR.TemplateMatcher.Services.Implementations;

internal class GroupedTemplateSynchronizer : ITemplateSynchronizer
{
    private readonly ILogger<GroupedTemplateSynchronizer> logger;
    private readonly IJobGroupRepository jobGroupService;
    private readonly IUnitFilterService unitFilterService;
    private readonly IGroupedTemplateUnitFilter groupedTemplateUnitFilter;
    private readonly IUnitInTemplateConflictMapper unitInTemplateConflictMapper;
    private readonly IGroupedTemplateBuilder groupedTemplateBuilder;
    private readonly IGroupedTemplateProcessor groupedTemplateProcessor;
    private readonly ITemplateRepository templateService;
    private readonly ITemplateDeactivator templateDeactivator;
    private readonly IMatchingStatusService matchingStatusService;

    public GroupedTemplateSynchronizer(
        ILogger<GroupedTemplateSynchronizer> logger,
        IJobGroupRepository jobGroupService,
        IUnitFilterService unitFilterService,
        IGroupedTemplateUnitFilter groupedTemplateUnitFilter,
        IUnitInTemplateConflictMapper unitInTemplateConflictMapper,
        IGroupedTemplateBuilder groupedTemplateBuilder,
        IGroupedTemplateProcessor groupedTemplateProcessor,
        ITemplateRepository templateService,
        ITemplateDeactivator templateDeactivator,
        IMatchingStatusService matchingStatusService)
    {
        this.logger = logger;
        this.jobGroupService = jobGroupService;
        this.unitFilterService = unitFilterService;
        this.groupedTemplateUnitFilter = groupedTemplateUnitFilter;
        this.unitInTemplateConflictMapper = unitInTemplateConflictMapper;
        this.groupedTemplateBuilder = groupedTemplateBuilder;
        this.groupedTemplateProcessor = groupedTemplateProcessor;
        this.templateService = templateService;
        this.templateDeactivator = templateDeactivator;
        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.LogInformation("Начало синхронизации шаблонов для 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
            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'ов с подходящими связями");
                await matchingStatusService.DeleteMatchingStatusAsync(jobGroupId, SyncTaskEntityTypeEnum.JobGroup);
                return;
            }

            // 4. Применение специфичных правил фильтрации для групповых шаблонов
            logger.LogDebug("Применение специфичных правил фильтрации для групповых шаблонов.");
            var finalFilteredUnits = await groupedTemplateUnitFilter.FilterAsync(unitFilterResults, jobGroup);

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

            // 5. Разрешение конфликтов связей и построение первичного маппинга
            logger.LogDebug("Разрешение конфликтов связей и построение первичного маппинга.");
            var initialReverseMapping = await unitInTemplateConflictMapper.BuildMappingAsync(finalFilteredUnits, maxJob);

            if (!initialReverseMapping.Any())
            {
                logger.LogInformation("После разрешения конфликтов в JobGroup {JobGroupId} не осталось связей.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Нет связей после разрешения конфликтов");
                return;
            }

            // 6. Построение структуры групп (трансформация, внутренняя группировка, разбиение)
            logger.LogDebug("Построение структуры групп для шаблонов.");
            var templateGroups = await groupedTemplateBuilder.BuildAsync(initialReverseMapping, jobGroup, maxJob);

            if (!templateGroups.Any())
            {
                logger.LogInformation("После построения структуры групп в JobGroup {JobGroupId} не осталось данных.", jobGroupId);
                await UpdateMatchingStatusAsync(jobGroupId, "Нет данных после построения групп");
                return;
            }

            // 7. Обработка групп: сравнение, обновление, создание, отправка MQ
            logger.LogDebug("Обработка групп шаблонов: сравнение, обновление и создание.");
            var expectedTemplateKeys = await groupedTemplateProcessor.ProcessAsync(
                templateGroups,
                jobsInGroup,
                maxJob,
                initiator);

            // 8. Деактивация лишних шаблонов
            await DeactivateUnusedTemplatesAsync(expectedTemplateKeys, jobGroupId, jobsInGroup, initiator);

            // 9. Успешное завершение
            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);
    }

    private async Task DeactivateUnusedTemplatesAsync(
        HashSet<(Guid JobId, Guid UnitId, int Index)> expectedKeys,
        Guid jobGroupId,
        List<Job> jobsInGroup,
        HistoryInitiator initiator)
    {
        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 (!expectedKeys.Contains(key))
            {
                logger.LogInformation("Деактивация лишнего шаблона {TemplateId} (Job {JobId}, Unit {UnitId}, Index {Index}).",
                    existingTemplate.Id, existingTemplate.JobId, existingTemplate.UnitId, existingTemplate.Index);
                await templateDeactivator.DeactivateTemplateAsync(existingTemplate, initiator);
            }
        }
    }

    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)
        );
    }
}