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 PARR.Domain.Enums;
using PARR.TemplateMatcher.Services.Implementations;
using PARR.TemplateMatcher.Services.Interfaces;

namespace PARR.TemplateMatcher
{
    internal class TemplateMatcher : ITemplateMatcher
    {
        private readonly ILogger<TemplateMatcher> logger;
        private readonly IJobRepository jobService;
        private readonly IJobGroupRepository jobGroupService;
        private readonly IEnumerable<ITemplateSynchronizer> synchronizers;

        public TemplateMatcher(
            ILogger<TemplateMatcher> logger,
            IJobRepository jobService,
            IJobGroupRepository jobGroupService,
            IEnumerable<ITemplateSynchronizer> synchronizers
            )
        {
            this.logger = logger;
            this.jobService = jobService;
            this.jobGroupService = jobGroupService;
            this.synchronizers = synchronizers;

        }

        public async Task SyncTemplatesForJob(Guid jobId, HistoryInitiator initiator)
        {
            logger.LogDebug("Начало синхронизации шаблонов для JobId {JobId}", jobId);

            var job = await GetJobWithGroupAndAutoControlAsync(jobId);
            if (job == null)
            {
                logger.LogError("Job с Id {JobId} не найден.", jobId);
                return;
            }

            // Проверяем, является ли Job "групповым"
            bool isGroupJob = job.Group != null && job.Group.GroupType?.Code == JobGroupTypesEnum.Group;

            if (isGroupJob && job.Group!.GroupingUnitFieldId.HasValue)
            {
                logger.LogInformation("Job {JobId} является групповым. Передаём в GroupedSynchronizer.", jobId);
                // Находим нужный синхронизатор
                var synchronizer = synchronizers.FirstOrDefault(s => s is GroupedTemplateSynchronizer);
                if (synchronizer != null)
                {
                    // Так как Job групповой, вызываем синхронизацию для его JobGroup
                    await synchronizer.SyncTemplatesForJobGroupAsync(job.GroupId, initiator);
                }
                else
                {
                    logger.LogError("GroupedTemplateSynchronizer не найден.");
                }
                return;
            }
            else
            {
                logger.LogInformation("Job {JobId} является обычным. Передаём в SimpleSynchronizer.", jobId);
                // Находим нужный синхронизатор
                var synchronizer = synchronizers.FirstOrDefault(s => s is SimpleTemplateSynchronizer);
                if (synchronizer != null)
                {
                    await synchronizer.SyncTemplatesForJobAsync(jobId, initiator);
                }
                else
                {
                    logger.LogError("SimpleTemplateSynchronizer не найден.");
                }
                return;
            }
        }

        public async Task SyncTemplatesForJobGroup(Guid jobGroupId, HistoryInitiator initiator)
        {
            logger.LogDebug("Начало синхронизации шаблонов для JobGroup {JobGroupId}", jobGroupId);

            var jobGroup = await jobGroupService.Get()
                .AsNoTracking()
                .Include(jg => jg.GroupType)
                .FirstOrDefaultAsync(jg => jg.Id == jobGroupId);

            if (jobGroup == null || jobGroup.GroupType == null)
            {
                logger.LogWarning("JobGroup {JobGroupId} не найден или не содержит GroupType.", jobGroupId);
                return;
            }

            // Определяем стратегию по типу группы
            switch (jobGroup.GroupType.Code)
            {
                case JobGroupTypesEnum.Group:
                    // Проверяем, есть ли GroupingUnitFieldId — это признак "настоящей" группировки
                    if (jobGroup.GroupingUnitFieldId.HasValue)
                    {
                        logger.LogInformation("JobGroup {JobGroupId} является Group с GroupingUnitFieldId. Передаём в GroupedTemplateSynchronizer.", jobGroupId);
                        var synchronizer = synchronizers.FirstOrDefault(s => s is GroupedTemplateSynchronizer);
                        if (synchronizer != null)
                        {
                            await synchronizer.SyncTemplatesForJobGroupAsync(jobGroupId, initiator);
                        }
                        else
                        {
                            logger.LogError("GroupedTemplateSynchronizer не найден для JobGroup {JobGroupId}.", jobGroupId);
                        }
                    }
                    else
                    {
                        logger.LogInformation("JobGroup {JobGroupId} является Group, но не имеет GroupingUnitFieldId. Обрабатываем как Collection.", jobGroupId);
                        //await SyncJobGroupAsCollectionAsync(jobGroupId, initiator);
                    }
                    break;

                case JobGroupTypesEnum.Umbrella:
                    logger.LogInformation("JobGroup {JobGroupId} является Umbrella. Обрабатываем как Collection (каждый Job — независимо).", jobGroupId);
                    await SyncJobGroupAsCollectionAsync(jobGroupId, initiator);
                    break;

                case JobGroupTypesEnum.Simple:
                default:
                    logger.LogInformation("JobGroup {JobGroupId} имеет тип Simple. Обрабатываем как Collection.", jobGroupId);
                    await SyncJobGroupAsCollectionAsync(jobGroupId, initiator);
                    break;
            }
        }

        public async Task UpdateTemplatesForJobGroup(Guid jobGroupId, HistoryInitiator initiator)
        {
            logger.LogDebug("Начало обновления шаблонов для JobGroup {JobGroupId}", jobGroupId);

            var jobGroup = await jobGroupService.Get()
                .AsNoTracking()
                .Include(jg => jg.GroupType)
                .FirstOrDefaultAsync(jg => jg.Id == jobGroupId);

            if (jobGroup == null || jobGroup.GroupType == null)
            {
                logger.LogWarning("JobGroup {JobGroupId} не найден или не содержит GroupType.", jobGroupId);
                return;
            }

            switch (jobGroup.GroupType.Code)
            {
                case JobGroupTypesEnum.Group:
                    if (jobGroup.GroupingUnitFieldId.HasValue)
                    {
                        logger.LogWarning("UpdateTemplatesForJobGroup не поддерживается для Group с GroupingUnitFieldId. Id: {JobGroupId}", jobGroupId);
                    }
                    else
                    {
                        logger.LogInformation("JobGroup {JobGroupId} — Group без GroupingUnitFieldId. Обновляем как Collection.", jobGroupId);
                        await UpdateJobGroupAsCollectionAsync(jobGroupId, initiator);
                    }
                    break;

                case JobGroupTypesEnum.Umbrella:
                    logger.LogInformation("JobGroup {JobGroupId} — Umbrella. Обновляем как Collection.", jobGroupId);
                    await UpdateJobGroupAsCollectionAsync(jobGroupId, initiator);
                    break;

                case JobGroupTypesEnum.Simple:
                default:
                    logger.LogInformation("JobGroup {JobGroupId} — Simple. Обновляем как Collection.", jobGroupId);
                    await UpdateJobGroupAsCollectionAsync(jobGroupId, initiator);
                    break;
            }
        }


        public async Task UpdateTemplatesForJob(Guid jobId, HistoryInitiator initiator)
        {
            logger.LogDebug("Начало обновления шаблонов для JobId {JobId}", jobId);

            var job = await GetJobWithGroupAndAutoControlAsync(jobId);
            if (job == null)
            {
                logger.LogError("Job с Id {JobId} не найден.", jobId);
                return;
            }

            // Проверяем, является ли Job "групповым"
            bool isGroupJob = job.Group != null && job.Group.GroupType?.Code == JobGroupTypesEnum.Group;

            if (isGroupJob && job.Group!.GroupingUnitFieldId.HasValue)
            {
                logger.LogInformation("Job {JobId} является групповым. Передаём в GroupedSynchronizer для Update.", jobId);
                // Находим нужный синхронизатор
                var synchronizer = synchronizers.FirstOrDefault(s => s is GroupedTemplateSynchronizer);
                if (synchronizer != null)
                {
                    // Вызов UpdateTemplatesForJobAsync для GroupedTemplateSynchronizer (который делает предупреждение)
                    await synchronizer.UpdateTemplatesForJobAsync(jobId, initiator);
                }
                else
                {
                    logger.LogError("GroupedTemplateSynchronizer не найден.");
                }
                return;
            }
            else
            {
                logger.LogInformation("Job {JobId} является обычным. Передаём в SimpleSynchronizer для Update.", jobId);
                // Находим нужный синхронизатор
                var synchronizer = synchronizers.FirstOrDefault(s => s is SimpleTemplateSynchronizer);
                if (synchronizer != null)
                {
                    await synchronizer.UpdateTemplatesForJobAsync(jobId, initiator);
                }
                else
                {
                    logger.LogError("SimpleTemplateSynchronizer не найден.");
                }
                return;
            }
        }

        // --- Вспомогательные методы ---
        private async Task SyncJobGroupAsCollectionAsync(Guid jobGroupId, HistoryInitiator initiator)
        {
            logger.LogDebug("Синхронизация JobGroup {JobGroupId} как Collection (по каждому Job'у отдельно)", jobGroupId);

            var jobIds = await jobService.Get()
                .AsNoTracking()
                .Where(j => j.GroupId == jobGroupId)
                .Select(j => j.Id)
                .ToListAsync();

            if (!jobIds.Any())
            {
                logger.LogWarning("JobGroup {JobGroupId} не содержит Job'ов.", jobGroupId);
                return;
            }

            logger.LogDebug("Найдено {Count} Job'ов в JobGroup {JobGroupId}", jobIds.Count, jobGroupId);

            var simpleSynchronizer = synchronizers.FirstOrDefault(s => s is SimpleTemplateSynchronizer);

            if (simpleSynchronizer == null)
            {
                logger.LogError("SimpleTemplateSynchronizer не найден для синхронизации Job'ов в JobGroup {JobGroupId}.", jobGroupId);
                return;
            }

            foreach (var jobId in jobIds)
            {
                logger.LogDebug("Синхронизация Job {JobId} в рамках JobGroup {JobGroupId}", jobId, jobGroupId);
                await simpleSynchronizer.SyncTemplatesForJobAsync(jobId, initiator);
            }

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


        private async Task UpdateJobGroupAsCollectionAsync(Guid jobGroupId, HistoryInitiator initiator)
        {
            logger.LogDebug("Обновление JobGroup {JobGroupId} как Collection (по каждому Job'у)", jobGroupId);

            var jobIds = await jobService.Get()
                .AsNoTracking()
                .Where(j => j.GroupId == jobGroupId)
                .Select(j => j.Id)
                .ToListAsync();

            if (!jobIds.Any())
            {
                logger.LogWarning("JobGroup {JobGroupId} не содержит Job'ов.", jobGroupId);
                return;
            }

            foreach (var jobId in jobIds)
            {
                logger.LogDebug("Обновление шаблонов для Job {JobId} в рамках JobGroup {JobGroupId}", jobId, jobGroupId);
                await UpdateTemplatesForJob(jobId, initiator);
            }

            logger.LogInformation("Обновление JobGroup {JobGroupId} как Collection завершено.", jobGroupId);
        }


        private async Task<Job?> GetJobWithGroupAndAutoControlAsync(Guid jobId)
        {
            return await jobService.Get()
                .AsNoTracking()
                .Include(j => j.Group)
                    .ThenInclude(j => j!.GroupType)
                .Include(j => j.AutoControl)
                .FirstOrDefaultAsync(j => j.Id == jobId);
        }
    }
}