using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces.Job;
using PARR.Domain.Entities.Base.History;
using PARR.Domain.Entities.Job;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace PARR.Core.Common.Helpers;

/// <summary>
/// Сервис для синхронизации свойств Job внутри JobGroup.
/// Эталоном считается Job с максимальным MaxValueRelationships.
/// Синхронизируются: WorkName, маски, IsParentRelationships, AutoControl, UnitFilters.
/// НЕ синхронизируются: Id, Name, MinValueRelationships, MaxValueRelationships, TnkId.
/// </summary>
public class JobGroupUnitFilterSynchronizeHelper
{
    private readonly IJobRepository jobRepository;
    private readonly ILogger<JobGroupUnitFilterSynchronizeHelper> logger;

    public JobGroupUnitFilterSynchronizeHelper(IJobRepository jobRepository, ILogger<JobGroupUnitFilterSynchronizeHelper> logger)
    {
        this.jobRepository = jobRepository;
        this.logger = logger;
    }

    public async Task SynchronizeGroupFiltersAsync(Guid jobGroupId, HistoryInitiator initiator, CancellationToken ct = default)
    {
        var jobs = await jobRepository.Get()
            .AsNoTracking()
            .Include(j => j.UnitFilters)
                .ThenInclude(uf => uf.FieldFilters)
            .Include(j => j.UnitFilters)
                .ThenInclude(uf => uf.RelationshipFilters)
            .Include(j => j.AutoControl)
            .Where(j => j.GroupId == jobGroupId)
            .ToListAsync(ct);

        if (jobs == null || jobs.Count < 2)
        {
            logger.LogDebug("JobGroup {JobGroupId} содержит менее двух Job. Синхронизация не требуется.", jobGroupId);
            return;
        }

        var referenceJob = jobs.OrderByDescending(j => j.MaxValueRelationships).FirstOrDefault();
        if (referenceJob == null)
        {
            logger.LogWarning("Не удалось определить эталонный Job для JobGroup {JobGroupId}.", jobGroupId);
            return;
        }

        logger.LogInformation("Начало синхронизации для JobGroup {JobGroupId}. Эталонный Job: {ReferenceJobId}.", jobGroupId, referenceJob.Id);

        foreach (var job in jobs)
        {
            ct.ThrowIfCancellationRequested();

            if (job.Id == referenceJob.Id) continue;

            if (!AreJobsIdentical(job, referenceJob))
            {
                logger.LogDebug("Свойства Job {JobId} отличаются от эталона. Запуск обновления.", job.Id);
                await ApplyJobCorrectionAsync(job.Id, referenceJob, initiator, ct);
            }
            else
            {
                logger.LogDebug("Свойства Job {JobId} идентичны эталону. Пропуск.", job.Id);
            }
        }

        logger.LogInformation("Синхронизация для JobGroup {JobGroupId} завершена.", jobGroupId);
    }

    private bool AreJobsIdentical(Job current, Job reference)
    {
        // Сравнение скалярных полей (кроме Id, Name, Min/MaxValueRelationships, TnkId)
        if (current.WorkName != reference.WorkName) return false;
        if (current.IsParentRelationships != reference.IsParentRelationships) return false;
        if (current.TemplateNameMask != reference.TemplateNameMask) return false;
        if (current.WorkGroupMask != reference.WorkGroupMask) return false;
        if (current.ResponseAreaMask != reference.ResponseAreaMask) return false;

        // Сравнение AutoControl
        if (current.AutoControl == null && reference.AutoControl != null) return false;
        if (current.AutoControl != null && reference.AutoControl == null) return false;
        if (current.AutoControl != null && reference.AutoControl != null)
        {
            if (current.AutoControl.InitUsedTemplateState != reference.AutoControl.InitUsedTemplateState) return false;
            if (current.AutoControl.InitUsedScheduleState != reference.AutoControl.InitUsedScheduleState) return false;
        }

        // Сравнение фильтров
        if (!AreUnitFiltersIdentical(current.UnitFilters, reference.UnitFilters)) return false;

        return true;
    }

    private async Task ApplyJobCorrectionAsync(Guid jobId, Job referenceJob, HistoryInitiator initiator, CancellationToken ct)
    {
        var job = await jobRepository.Get()
            .Include(j => j.UnitFilters)
                .ThenInclude(uf => uf.FieldFilters)
            .Include(j => j.UnitFilters)
                .ThenInclude(uf => uf.RelationshipFilters)
            .Include(j => j.AutoControl)
            .FirstOrDefaultAsync(j => j.Id == jobId, ct);

        if (job == null) return;

        // 1. Обновляем скалярные поля
        job.WorkName = referenceJob.WorkName;
        job.IsParentRelationships = referenceJob.IsParentRelationships;
        job.TemplateNameMask = referenceJob.TemplateNameMask;
        job.WorkGroupMask = referenceJob.WorkGroupMask;
        job.ResponseAreaMask = referenceJob.ResponseAreaMask;
        job.DateModified = DateTimeOffset.UtcNow;

        // 2. Обновляем AutoControl
        if (referenceJob.AutoControl != null)
        {
            if (job.AutoControl == null)
            {
                job.AutoControl = new JobAutoControl
                {
                    JobId = job.Id,
                    InitUsedTemplateState = referenceJob.AutoControl.InitUsedTemplateState,
                    InitUsedScheduleState = referenceJob.AutoControl.InitUsedScheduleState
                };
            }
            else
            {
                job.AutoControl.InitUsedTemplateState = referenceJob.AutoControl.InitUsedTemplateState;
                job.AutoControl.InitUsedScheduleState = referenceJob.AutoControl.InitUsedScheduleState;
            }
        }

        // 3. Обновляем UnitFilters
        if (job.UnitFilters == null)
        {
            job.UnitFilters = new List<JobUnitFilter>();
        }
        else
        {
            job.UnitFilters.Clear();
        }

        var clonedFilters = CloneFilters(referenceJob.UnitFilters ?? Enumerable.Empty<JobUnitFilter>(), job.Id);
        foreach (var filter in clonedFilters)
        {
            job.UnitFilters!.Add(filter);
        }

        await jobRepository.CommitAsync(initiator);
        logger.LogInformation("Job {JobId} успешно синхронизирован с эталоном.", jobId);
    }

    private bool AreUnitFiltersIdentical(IEnumerable<JobUnitFilter>? current, IEnumerable<JobUnitFilter>? reference)
    {
        var currentList = (current ?? Enumerable.Empty<JobUnitFilter>()).ToList();
        var refList = (reference ?? Enumerable.Empty<JobUnitFilter>()).ToList();

        if (currentList.Count != refList.Count) return false;

        for (int i = 0; i < currentList.Count; i++)
        {
            if (!AreFiltersEqual(currentList[i], refList[i]))
                return false;
        }
        return true;
    }

    private bool AreFiltersEqual(JobUnitFilter a, JobUnitFilter b)
    {
        if (!AreFieldFiltersEqual(a.FieldFilters, b.FieldFilters)) return false;
        if (!AreRelationshipFiltersEqual(a.RelationshipFilters, b.RelationshipFilters)) return false;
        return true;
    }

    private bool AreFieldFiltersEqual(IEnumerable<JobFieldFilter>? a, IEnumerable<JobFieldFilter>? b)
    {
        var listA = (a ?? Enumerable.Empty<JobFieldFilter>()).OrderBy(f => f.FieldId).ToList();
        var listB = (b ?? Enumerable.Empty<JobFieldFilter>()).OrderBy(f => f.FieldId).ToList();

        if (listA.Count != listB.Count) return false;

        for (int i = 0; i < listA.Count; i++)
        {
            if (listA[i].FieldId != listB[i].FieldId ||
                !string.Equals(listA[i].ValueMask, listB[i].ValueMask, StringComparison.OrdinalIgnoreCase) ||
                listA[i].IsInverse != listB[i].IsInverse)
            {
                return false;
            }
        }
        return true;
    }

    private bool AreRelationshipFiltersEqual(IEnumerable<JobRelationshipFilter>? a, IEnumerable<JobRelationshipFilter>? b)
    {
        var listA = (a ?? Enumerable.Empty<JobRelationshipFilter>()).OrderBy(f => f.FieldId).ToList();
        var listB = (b ?? Enumerable.Empty<JobRelationshipFilter>()).OrderBy(f => f.FieldId).ToList();

        if (listA.Count != listB.Count) return false;

        for (int i = 0; i < listA.Count; i++)
        {
            if (listA[i].FieldId != listB[i].FieldId ||
                !string.Equals(listA[i].ValueMask, listB[i].ValueMask, StringComparison.OrdinalIgnoreCase) ||
                listA[i].IsInverse != listB[i].IsInverse ||
                listA[i].IsParent != listB[i].IsParent ||
                listA[i].IsFullMatch != listB[i].IsFullMatch)
            {
                return false;
            }
        }
        return true;
    }

    private List<JobUnitFilter> CloneFilters(IEnumerable<JobUnitFilter> source, Guid targetJobId)
    {
        var result = new List<JobUnitFilter>();
        foreach (var src in source ?? Enumerable.Empty<JobUnitFilter>())
        {
            var newFilter = new JobUnitFilter
            {
                JobId = targetJobId,
                UnitFilter = src.UnitFilter!,
                DateCreated = DateTimeOffset.UtcNow,
                FieldFilters = src.FieldFilters?.Select(f => new JobFieldFilter
                {
                    FieldId = f.FieldId,
                    ValueMask = f.ValueMask,
                    IsInverse = f.IsInverse,
                    DateCreated = DateTimeOffset.UtcNow
                }).ToList() ?? new List<JobFieldFilter>(),
                RelationshipFilters = src.RelationshipFilters?.Select(r => new JobRelationshipFilter
                {
                    FieldId = r.FieldId,
                    ValueMask = r.ValueMask,
                    IsInverse = r.IsInverse,
                    IsParent = r.IsParent,
                    IsFullMatch = r.IsFullMatch
                }).ToList() ?? new List<JobRelationshipFilter>()
            };
            result.Add(newFilter);
        }
        return result;
    }
}