using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;

namespace TeleWave.Application.Programming.Groups.FindGroupCandidates;

public sealed class FindGroupCandidatesQueryHandler(
    IAppDbContext dbContext,
    GroupFilterMatcher matcher,
    GroupElementResolver resolver
) : IQueryHandler<FindGroupCandidatesQuery, Result<IReadOnlyList<GroupCandidateDto>>>
{
    public async Task<Result<IReadOnlyList<GroupCandidateDto>>> Handle(
        FindGroupCandidatesQuery query,
        CancellationToken cancellationToken
    )
    {
        var group = await dbContext
            .Groups.AsNoTracking()
            .Include(g => g.Items)
            .FirstOrDefaultAsync(g => g.Id == query.GroupId, cancellationToken);
        if (group is null)
            return Result.Failure<IReadOnlyList<GroupCandidateDto>>(GroupErrors.NotFound);

        var filter = query.Filter ?? GroupFilter.FromJson(group.FilterJson);
        if (filter is null)
            return Result.Failure<IReadOnlyList<GroupCandidateDto>>(GroupErrors.FilterNotSet);

        var elements = await matcher.MatchAsync(filter, cancellationToken);
        var info = await resolver.ResolveAsync(elements, cancellationToken);
        var inGroup = group.Items.Select(i => (i.ElementKind, i.ElementId)).ToHashSet();

        var result = new List<GroupCandidateDto>();
        foreach (var (kind, id) in elements)
        {
            if (!info.TryGetValue((kind, id), out var element))
                continue;
            if (!GroupFilterMatcher.MatchesUnitDuration(filter, element))
                continue;

            result.Add(
                new GroupCandidateDto(
                    kind,
                    id,
                    element.Name,
                    element.UnitCount,
                    element.ShowKind,
                    element.Audience,
                    element.Year,
                    element.PosterImageId,
                    inGroup.Contains((kind, id))
                )
            );
        }

        return Result.Success<IReadOnlyList<GroupCandidateDto>>(
            result.OrderBy(c => c.ElementName, StringComparer.CurrentCultureIgnoreCase).ToList()
        );
    }
}
