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;

namespace PARR.TemplateMatcher.Services.Implementations;

internal class GroupedTemplateUnitFilter : IGroupedTemplateUnitFilter
{
    private readonly ILogger<GroupedTemplateUnitFilter> logger;
    private readonly IUnitInValueRepository unitInValueRepository;
    private readonly IUnitRegionalEkPtkGroupRepository regionalEkPtkGroupRepository;
    private readonly IUnitFieldRepository unitFieldRepository;

    public GroupedTemplateUnitFilter(
        ILogger<GroupedTemplateUnitFilter> logger,
        IUnitInValueRepository unitInValueRepository,
        IUnitRegionalEkPtkGroupRepository regionalEkPtkGroupRepository,
        IUnitFieldRepository unitFieldRepository)
    {
        this.logger = logger;
        this.unitInValueRepository = unitInValueRepository;
        this.regionalEkPtkGroupRepository = regionalEkPtkGroupRepository;
        this.unitFieldRepository = unitFieldRepository;
    }

    public async Task<List<UnitFilterResultDto>> FilterAsync(
        IEnumerable<UnitFilterResultDto> initialUnits,
        JobGroup jobGroup,
        CancellationToken ct = default)
    {
        var unitsList = initialUnits.ToList();
        if (!unitsList.Any())
        {
            logger.LogDebug("Входной список юнитов пуст. Фильтрация пропущена.");
            return unitsList;
        }

        logger.LogDebug("Начало фильтрации юнитов для JobGroup {JobGroupId}.", jobGroup.Id);

        // 1. Проверка наличия GroupingUnitFieldId
        if (!jobGroup.GroupingUnitFieldId.HasValue)
        {
            throw new InvalidOperationException($"JobGroup {jobGroup.Id} не имеет GroupingUnitFieldId, необходимого для группировки.");
        }

        var groupingFieldId = jobGroup.GroupingUnitFieldId.Value;
        logger.LogDebug("Фильтрация по GroupingUnitFieldId (FieldId={FieldId}).", groupingFieldId);

        // 2. Фильтрация по GroupingUnitFieldId
        var allUnitIds = unitsList.Select(u => u.Id).ToList();
        var groupingValues = await unitInValueRepository.GetByUnitIdsAndFieldIdsAsync(allUnitIds, new HashSet<Guid> { groupingFieldId });

        var validUnitIdsAfterGrouping = groupingValues
            .Where(uv => uv.Value != null && !string.IsNullOrEmpty(uv.Value.Value))
            .Select(uv => uv.UnitId)
            .ToHashSet();

        var filteredByGrouping = unitsList
            .Where(u => validUnitIdsAfterGrouping.Contains(u.Id))
            .ToList();

        logger.LogDebug("После фильтрации по GroupingUnitFieldId осталось {Count} юнитов.", filteredByGrouping.Count);

        if (!filteredByGrouping.Any())
        {
            return filteredByGrouping;
        }

        // 3. Фильтрация по РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК
        var workGroupField = await unitFieldRepository.GetByAihitNameAsync("РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК")
            ?? throw new InvalidOperationException("Поле 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' не найдено в справочнике полей.");

        var workGroupFieldId = workGroupField.Id;
        logger.LogDebug("Фильтрация по полю 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' (FieldId={FieldId}).", workGroupFieldId);

        var filteredUnitIds = filteredByGrouping.Select(u => u.Id).ToList();
        var workGroupValues = await unitInValueRepository.GetByUnitIdsAndFieldIdsAsync(filteredUnitIds, new HashSet<Guid> { workGroupFieldId });

        var allowedValueIds = regionalEkPtkGroupRepository.Get()
            .Select(g => g.FieldValueId)
            .ToHashSet();

        logger.LogDebug("Найдено {Count} разрешенных значений для поля 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК'.", allowedValueIds.Count);

        var validUnitIdsAfterWorkGroup = workGroupValues
            .Where(uv => uv.Value != null && allowedValueIds.Contains(uv.Value.Id))
            .Select(uv => uv.UnitId)
            .ToHashSet();

        var finalFiltered = filteredByGrouping
            .Where(u => validUnitIdsAfterWorkGroup.Contains(u.Id))
            .ToList();

        logger.LogDebug("После фильтрации по 'РАБОЧАЯ_ГР_ОТВ_ЗА_ЭК' осталось {Count} юнитов.", finalFiltered.Count);

        return finalFiltered;
    }
}