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.Entities.Schedule;

namespace PARR.Core.Common.Helpers;

/// <summary>
/// Сервис для полного клонирования JobGroup со всеми вложенными Job, фильтрами, AutoControl и EsppSchValues.
/// Шаблоны (Template) не клонируются. Идентификаторы генерируются стратегией EF Core.
/// </summary>
public class JobGroupCloneHelper
{
    private readonly IJobGroupRepository groupRepository;
    private readonly IJobRepository jobRepository;
    private readonly ILogger<JobGroupCloneHelper> logger;

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

    public async Task<Guid> CloneAsync(
        Guid sourceJobGroupId,
        string? newGroupName,
        HistoryInitiator initiator,
        CancellationToken ct = default)
    {
        var sourceGroup = await groupRepository.Get()
            .AsNoTracking()
            .Include(g => g.Jobs)
                .ThenInclude(j => j.UnitFilters)
                    .ThenInclude(uf => uf.FieldFilters)
            .Include(g => g.Jobs)
                .ThenInclude(j => j.UnitFilters)
                    .ThenInclude(uf => uf.RelationshipFilters)
            .Include(g => g.Jobs)
                .ThenInclude(j => j.AutoControl)
            .Include(g => g.EsppSchValues)
            .FirstOrDefaultAsync(g => g.Id == sourceJobGroupId, ct);

        if (sourceGroup == null)
        {
            throw new InvalidOperationException($"JobGroup {sourceJobGroupId} not found.");
        }

        logger.LogInformation("Начало клонирования JobGroup {JobGroupId}.", sourceJobGroupId);

        var newGroup = MapJobGroup(sourceGroup, newGroupName);

        foreach (var sourceJob in sourceGroup.Jobs ?? Enumerable.Empty<Job>())
        {
            var newJob = MapJob(sourceJob);
            newGroup.Jobs.Add(newJob);

            CloneJobFilters(sourceJob.UnitFilters, newJob);

            if (sourceJob.AutoControl != null)
            {
                newJob.AutoControl = MapAutoControl(sourceJob.AutoControl);
            }
        }

        // Клонирование значений расписания
        foreach (var sourceSchValue in sourceGroup.EsppSchValues ?? Enumerable.Empty<EsppSchValue>())
        {
            newGroup.EsppSchValues.Add(new EsppSchValue
            {
                TypeValueId = sourceSchValue.TypeValueId,
                TypeConfigId = sourceSchValue.TypeConfigId
            });
        }

        var isSuccess = await groupRepository.CreateAsync(newGroup);
        if (!isSuccess)
        {
            throw new InvalidOperationException("Не удалось сохранить клон JobGroup в базу данных.");
        }

        await groupRepository.CommitAsync(initiator);

        logger.LogInformation("JobGroup успешно клонирована. Новый ID: {NewGroupId}.", newGroup.Id);

        return newGroup.Id;
    }

    private JobGroup MapJobGroup(JobGroup source, string? newName)
    {
        string finalName = string.IsNullOrWhiteSpace(newName)
            ? $"{source.GroupName?.TrimEnd()} КЛОН"
            : newName.Trim();

        return new JobGroup
        {
            GroupName = finalName,
            ShortDescription = source.ShortDescription,
            FullDescription = source.FullDescription,
            Solution = source.Solution,
            TemplateDuration = source.TemplateDuration,
            ReferenceDate = source.ReferenceDate,
            UserTimeZoneOffsetMinutes = source.UserTimeZoneOffsetMinutes,
            IsResponseAreaTimezone = source.IsResponseAreaTimezone,
            IsAutoDistributionEnabled = source.IsAutoDistributionEnabled,
            IsAgent = source.IsAgent,
            AgentName = source.AgentName,
            AgentTimeOutSec = source.AgentTimeOutSec,
            AgentScript = source.AgentScript,
            GroupTypeId = source.GroupTypeId,
            GroupingUnitFieldId = source.GroupingUnitFieldId,
            IsGroupByResponsible = source.IsGroupByResponsible,
            ScheduleExcludeTypeId = source.ScheduleExcludeTypeId,
            ScheduleExcludeTypeCalendarId = source.ScheduleExcludeTypeCalendarId,
            DateCreated = DateTimeOffset.UtcNow,
            Jobs = new HashSet<Job>(),
            EsppSchValues = new HashSet<EsppSchValue>()
        };
    }

    private Job MapJob(Job source)
    {
        return new Job
        {
            Name = source.Name,
            WorkName = source.WorkName,
            MinValueRelationships = source.MinValueRelationships,
            MaxValueRelationships = source.MaxValueRelationships,
            IsParentRelationships = source.IsParentRelationships,
            TemplateNameMask = source.TemplateNameMask,
            WorkGroupMask = source.WorkGroupMask,
            ResponseAreaMask = source.ResponseAreaMask,
            TnkId = source.TnkId,
            DateCreated = DateTimeOffset.UtcNow,
            UnitFilters = new HashSet<JobUnitFilter>()
        };
    }

    private JobAutoControl MapAutoControl(JobAutoControl source)
    {
        return new JobAutoControl
        {
            InitUsedTemplateState = source.InitUsedTemplateState,
            InitUsedScheduleState = source.InitUsedScheduleState
        };
    }

    private void CloneJobFilters(IEnumerable<JobUnitFilter>? sourceFilters, Job targetJob)
    {
        if (sourceFilters == null) return;

        foreach (var sourceFilter in sourceFilters)
        {
            var newFilter = new JobUnitFilter
            {
                UnitFilter = sourceFilter.UnitFilter,
                DateCreated = DateTimeOffset.UtcNow,
                FieldFilters = sourceFilter.FieldFilters?.Select(f => new JobFieldFilter
                {
                    FieldId = f.FieldId,
                    ValueMask = f.ValueMask,
                    IsInverse = f.IsInverse,
                    DateCreated = DateTimeOffset.UtcNow
                }).ToList() ?? new List<JobFieldFilter>(),
                RelationshipFilters = sourceFilter.RelationshipFilters?.Select(r => new JobRelationshipFilter
                {
                    FieldId = r.FieldId,
                    ValueMask = r.ValueMask,
                    IsInverse = r.IsInverse,
                    IsParent = r.IsParent,
                    IsFullMatch = r.IsFullMatch
                }).ToList() ?? new List<JobRelationshipFilter>()
            };
            targetJob.UnitFilters.Add(newFilter);
        }
    }
}