using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using PARR.Core.Repositories.Interfaces.Unit;
using PARR.Core.Services.Shortcodes;
using PARR.Domain.Constants;
using PARR.Domain.Entities;
using PARR.Domain.Entities.Job;
using PARR.TemplateMatcher.Models;
using PARR.TemplateMatcher.Services.Interfaces;

namespace PARR.TemplateMatcher.Services.Implementations;

internal class GroupedTemplateBuilder : IGroupedTemplateBuilder
{
    private readonly ILogger<GroupedTemplateBuilder> logger;
    private readonly IUnitInValueRepository unitInValueRepository;
    private readonly IUnitFieldRepository unitFieldRepository;
    private readonly IShortcodesService shortcodesService;

    public GroupedTemplateBuilder(
        ILogger<GroupedTemplateBuilder> logger,
        IUnitInValueRepository unitInValueRepository,
        IUnitFieldRepository unitFieldRepository,
        IShortcodesService shortcodesService
        )
    {
        this.logger = logger;
        this.unitInValueRepository = unitInValueRepository;
        this.unitFieldRepository = unitFieldRepository;
        this.shortcodesService = shortcodesService;
    }

    public async Task<List<GroupedTemplateGroup>> BuildAsync(
        Dictionary<Guid, List<Guid>> initialReverseMapping,
        JobGroup jobGroup,
        Job maxJob,
        CancellationToken ct = default)
    {
        logger.LogDebug("Начало построения структуры групп для JobGroup {JobGroupId}.", jobGroup.Id);

        if (!initialReverseMapping.Any())
            return new List<GroupedTemplateGroup>();

        // 1. Определяем стратегию внутренней группировки
        // Если IsGroupByResponsible != true, используем WorkGroupMask через ShortcodesService
        bool useWorkGroupMask = jobGroup.IsGroupByResponsible != true;

        logger.LogDebug("Стратегия внутренней группировки: {Strategy}",
            useWorkGroupMask ? "WorkGroupMask" : "Поле 'Ответственный за ЭК'");

        // 2. Собираем все исходные UnitId
        var allSourceUnitIds = initialReverseMapping.Values.SelectMany(ids => ids).Distinct().ToList();

        // 3. Загружаем UnitInValue для трансформации по полю группировки из настроек JobGroup
        var groupingFieldId = jobGroup.GroupingUnitFieldId!.Value;
        var relevantUnitInValues = await unitInValueRepository.Get()
            .AsNoTracking()
            .Where(uiv => allSourceUnitIds.Contains(uiv.UnitId) && uiv.FieldId == groupingFieldId)
            .OrderBy(uiv => uiv.UnitId)
            .ThenBy(uiv => uiv.ValueId)
            .Select(uiv => new { uiv.UnitId, uiv.ValueId })
            .ToListAsync(ct);

        var uivLookup = relevantUnitInValues
            .GroupBy(x => x.UnitId)
            .ToDictionary(g => g.Key, g => g.Select(x => x.ValueId).ToList());

        // 4. Трансформируем в reverseMapping с парами
        var reverseMapping = new Dictionary<Guid, List<(Guid UnitId, Guid UnitFieldValueId)>>();

        foreach (var kvp in initialReverseMapping.OrderBy(k => k.Key))
        {
            var potentialUnitId = kvp.Key;
            var sourceDtoIds = kvp.Value;
            var entries = new List<(Guid UnitId, Guid UnitFieldValueId)>();

            foreach (var dtoId in sourceDtoIds)
            {
                if (uivLookup.TryGetValue(dtoId, out var valueIds))
                {
                    foreach (Guid valId in valueIds)
                        entries.Add((UnitId: dtoId, UnitFieldValueId: valId));
                }
            }

            var uniqueEntries = entries
                .GroupBy(e => (e.UnitId, e.UnitFieldValueId))
                .Select(g => g.First())
                .OrderBy(e => e.UnitId)
                .ThenBy(e => e.UnitFieldValueId)
                .ToList();

            if (uniqueEntries.Any())
                reverseMapping[potentialUnitId] = uniqueEntries;
        }

        if (!reverseMapping.Any())
            return new List<GroupedTemplateGroup>();

        // 5. Определяем значения для внутренней группировки
        Dictionary<Guid, string> unitIdToGroupingValueMap;

        if (useWorkGroupMask)
        {
            // Используем ShortcodesService для получения финальных значений WorkGroupMask
            unitIdToGroupingValueMap = new Dictionary<Guid, string>();

            foreach (var potentialUnitId in reverseMapping.Keys)
            {
                var relatedUnitIds = reverseMapping[potentialUnitId].Select(e => e.UnitId).Distinct().ToList();

                foreach (var relatedUnitId in relatedUnitIds)
                {
                    // Создаем временный Template для применения шорткодов
                    var tempTemplate = new Template
                    {
                        Id = Guid.NewGuid(),
                        Name = "temp",
                        JobId = maxJob.Id,
                        UnitId = potentialUnitId, // Родительский юнит шаблона
                        Job = maxJob,
                        UnitsInTemplate = new List<UnitsInTemplate>
                        {
                            new UnitsInTemplate { UnitId = relatedUnitId, UnitFieldValueId = Guid.Empty }
                        }
                    };

                    var workGroupValue = await shortcodesService.ApplyShortcodesAsync(
                        maxJob.WorkGroupMask,
                        tempTemplate,
                        nameof(GroupedTemplateBuilder));

                    unitIdToGroupingValueMap[relatedUnitId] = workGroupValue;
                }
            }
        }
        else
        {
            // Логика разделения по ответственному за ЭК через служебный код поля
            var innerGroupingField = await unitFieldRepository.GetByCodeAsync(UnitFieldCodes.Responsible, ct)
                ?? throw new InvalidOperationException($"Поле с кодом '{UnitFieldCodes.Responsible}' не найдено в справочнике UnitField.");

            var innerGroupingFieldId = innerGroupingField.Id;
            var allUnitsInTemplatePairs = reverseMapping.Values.SelectMany(list => list).ToList();
            var allUnitIdsForInnerGrouping = allUnitsInTemplatePairs.Select(e => e.UnitId).Distinct().ToList();

            var innerGroupingValues = await unitInValueRepository.GetByUnitIdsAndFieldIdsAsync(
                allUnitIdsForInnerGrouping,
                new HashSet<Guid> { innerGroupingFieldId },
                ct);

            unitIdToGroupingValueMap = innerGroupingValues
                .Where(uv => uv.Value != null && uv.Value.Value != null && !string.IsNullOrEmpty(uv.Value.Value))
                .ToDictionary(uv => uv.UnitId, uv => uv.Value!.Value!);
        }

        // 6. Формируем итоговую структуру
        var templateGroups = new List<GroupedTemplateGroup>();
        int maxValueForSplitting = maxJob.MaxValueRelationships!.Value;

        foreach (var kvp in reverseMapping.OrderBy(k => k.Key))
        {
            var potentialUnitId = kvp.Key;
            var unitsInTemplateForThisPotentialUnitId = kvp.Value;

            var innerGroupedUnits = unitsInTemplateForThisPotentialUnitId
                .GroupBy(entry => unitIdToGroupingValueMap.GetValueOrDefault(entry.UnitId, "Нет данных"))
                .OrderBy(g => g.Key, StringComparer.Ordinal)
                .ToList();

            var subGroups = new List<GroupedTemplateSubGroup>();
            int globalIndex = 1;

            foreach (var innerGroup in innerGroupedUnits)
            {
                var innerGroupName = innerGroup.Key;
                if (innerGroupName == null)
                    continue;

                var unitsInInnerGroup = innerGroup.ToList();

                var splitSubGroups = unitsInInnerGroup
                    .Select((entry, index) => new { entry, groupIndex = index / maxValueForSplitting })
                    .GroupBy(x => x.groupIndex)
                    .Select(g => g.Select(x => x.entry).ToList())
                    .ToList();

                foreach (var subGroupEntries in splitSubGroups)
                {
                    var sortedEntries = subGroupEntries
                        .OrderBy(e => e.UnitId)
                        .ThenBy(e => e.UnitFieldValueId)
                        .ToList();

                    subGroups.Add(new GroupedTemplateSubGroup(
                        Entries: sortedEntries,
                        InnerGroupName: innerGroupName,
                        GlobalIndex: globalIndex
                    ));
                    globalIndex++;
                }
            }

            if (subGroups.Any())
            {
                templateGroups.Add(new GroupedTemplateGroup(
                    PotentialUnitId: potentialUnitId,
                    SubGroups: subGroups
                ));
            }
        }

        logger.LogDebug("Построено {Count} групп шаблонов.", templateGroups.Count);
        return templateGroups;
    }
}