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

internal class UnitInTemplateConflictMapper : IUnitInTemplateConflictMapper
{
    private readonly ILogger<UnitInTemplateConflictMapper> logger;
    private readonly IUnitRepository unitRepository;
    private readonly IUnitKiiUnitRepository unitKiiUnitRepository;


    public UnitInTemplateConflictMapper(
        ILogger<UnitInTemplateConflictMapper> logger,
        IUnitRepository unitRepository,
        IUnitKiiUnitRepository unitKiiUnitRepository)
    {
        this.logger = logger;
        this.unitRepository = unitRepository;
        this.unitKiiUnitRepository = unitKiiUnitRepository;
    }


    public async Task<Dictionary<Guid, List<Guid>>> BuildMappingAsync(
        IEnumerable<UnitFilterResultDto> unitFilterResults,
        Job maxJob,
        CancellationToken cancellationToken = default)
    {
        logger.LogDebug("Начало разрешения конфликтов и построения маппинга для юнитов в шаблонах.");

        var unitFilterResultsList = unitFilterResults.ToList();
        if (!unitFilterResultsList.Any())
            return new Dictionary<Guid, List<Guid>>();

        var potentialAssignments = new Dictionary<Guid, List<Guid>>();
        var allPotentialRelatedUnitIds = new HashSet<Guid>();
        var allUnitInTemplateIds = new HashSet<Guid>();

        foreach (var dto in unitFilterResultsList)
        {
            var relatedUnitIds = maxJob.IsParentRelationships == true
                ? dto.Children.Select(c => c.UnitId).ToList()
                : dto.Parents.Select(p => p.UnitId).ToList();

            var unitInTemplateId = dto.Id;

            allUnitInTemplateIds.Add(unitInTemplateId);
            allPotentialRelatedUnitIds.UnionWith(relatedUnitIds);

            foreach (var relatedId in relatedUnitIds)
            {
                if (!potentialAssignments.TryGetValue(relatedId, out var list))
                {
                    list = new List<Guid>();
                    potentialAssignments[relatedId] = list;
                }
                list.Add(unitInTemplateId);
            }
        }

        if (!allPotentialRelatedUnitIds.Any())
            return new Dictionary<Guid, List<Guid>>();

        var relatedUnitNames = await unitRepository.Get()
            .AsNoTracking()
            .Where(u => allPotentialRelatedUnitIds.Contains(u.Id))
            .ToDictionaryAsync(u => u.Id, u => u.Name ?? u.Id.ToString(), cancellationToken);

        // Загружаем идентификаторы КИИ через ваш реальный репозиторий
        var kiiUnitIds = new HashSet<Guid>(await unitKiiUnitRepository.Get()
            .Select(k => k.UnitId)
            .ToListAsync(cancellationToken));

        logger.LogDebug("Загружено {KiiCount} идентификаторов КИИ юнитов для приоритезации.", kiiUnitIds.Count);

        var unitInTemplateToBestRelatedUnit = new Dictionary<Guid, Guid>();

        foreach (var unitInTemplateId in allUnitInTemplateIds)
        {
            var candidates = potentialAssignments
                .Where(kvp => kvp.Value.Contains(unitInTemplateId))
                .Select(kvp => kvp.Key)
                .ToList();

            if (candidates.Count == 0) continue;
            if (candidates.Count == 1)
            {
                unitInTemplateToBestRelatedUnit[unitInTemplateId] = candidates[0];
                continue;
            }

            var kiiCandidates = candidates.Where(id => kiiUnitIds.Contains(id)).ToList();
            var candidatesToConsider = kiiCandidates.Any() ? kiiCandidates : candidates;

            var relationshipCounts = new Dictionary<Guid, int>();
            foreach (var candidateId in candidatesToConsider)
            {
                int totalRelationships = 0;
                foreach (var dto in unitFilterResultsList)
                {
                    if (maxJob.IsParentRelationships == true && dto.Children.Any(c => c.UnitId == candidateId))
                        totalRelationships += dto.Children.Count;
                    else if (maxJob.IsParentRelationships == false && dto.Parents.Any(p => p.UnitId == candidateId))
                        totalRelationships += dto.Parents.Count;
                }
                relationshipCounts[candidateId] = totalRelationships;
            }

            var bestCandidate = candidatesToConsider[0];
            int bestCount = relationshipCounts[bestCandidate];

            foreach (var candidateId in candidatesToConsider.Skip(1))
            {
                int currentCount = relationshipCounts[candidateId];
                if (currentCount > bestCount ||
                   (currentCount == bestCount && string.Compare(
                       relatedUnitNames.GetValueOrDefault(candidateId, candidateId.ToString()),
                       relatedUnitNames.GetValueOrDefault(bestCandidate, bestCandidate.ToString()),
                       StringComparison.OrdinalIgnoreCase) < 0))
                {
                    bestCandidate = candidateId;
                    bestCount = currentCount;
                }
            }

            unitInTemplateToBestRelatedUnit[unitInTemplateId] = bestCandidate;
        }

        logger.LogDebug("Разрешение конфликтов завершено. Найдено {Count} однозначных назначений.", unitInTemplateToBestRelatedUnit.Count);

        var reverseMapping = new Dictionary<Guid, List<Guid>>();
        foreach (var kvp in unitInTemplateToBestRelatedUnit)
        {
            if (!reverseMapping.TryGetValue(kvp.Value, out var list))
            {
                list = new List<Guid>();
                reverseMapping[kvp.Value] = list;
            }
            list.Add(kvp.Key);
        }

        return reverseMapping;
    }
}