using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using TeleWave.Application.Programming.Groups;
using TeleWave.Domain.Programming;

namespace TeleWave.Application.Library.GetShowUsage;

public sealed class GetShowUsageQueryHandler(
    IAppDbContext dbContext,
    DynamicGroupResolver dynamicResolver
) : IQueryHandler<GetShowUsageQuery, Result<ShowUsageDto>>
{
    public async Task<Result<ShowUsageDto>> Handle(
        GetShowUsageQuery query,
        CancellationToken cancellationToken
    )
    {
        if (!await dbContext.Shows.AnyAsync(s => s.Id == query.ShowId, cancellationToken))
            return Result.Failure<ShowUsageDto>(ShowErrors.NotFound);

        var collections = await dbContext
            .Collections.AsNoTracking()
            .Where(c => c.Items.Any(i => i.ShowId == query.ShowId))
            .Select(c => new ShowUsageRefDto(c.Id, c.Name, false))
            .ToListAsync(cancellationToken);

        var channels = await dbContext
            .ScheduleEntries.AsNoTracking()
            .Where(e => e.ShowId == query.ShowId)
            .Select(e => e.ChannelId)
            .Distinct()
            .Join(
                dbContext.Channels.AsNoTracking(),
                id => id,
                channel => channel.Id,
                (_, channel) => new ShowUsageRefDto(channel.Id, channel.Name, false)
            )
            .ToListAsync(cancellationToken);

        return Result.Success(
            new ShowUsageDto(
                await GroupsAsync(query.ShowId, collections, cancellationToken),
                collections,
                channels
            )
        );
    }

    /// <summary>
    /// Группы считаем по вычисленному составу: у динамической шоу может входить правилом, без своей
    /// позиции, и такое участие для «можно ли удалять» ничем не отличается от явного. Коллекции
    /// учитываются заодно — шоу попадает в группу и через франшизу, целиком.
    /// </summary>
    private async Task<IReadOnlyList<ShowUsageRefDto>> GroupsAsync(
        Guid showId,
        IReadOnlyList<ShowUsageRefDto> collections,
        CancellationToken cancellationToken
    )
    {
        var groups = await dbContext
            .Groups.AsNoTracking()
            .Include(g => g.Items)
            .ToListAsync(cancellationToken);

        var collectionIds = collections.Select(c => c.Id).ToHashSet();
        var result = new List<ShowUsageRefDto>();

        foreach (var group in groups)
        {
            var composition = await dynamicResolver.ResolveAsync(group, cancellationToken);
            var element = composition.FirstOrDefault(e =>
                (e.Kind == GroupElementKind.Show && e.Id == showId)
                || (e.Kind == GroupElementKind.Collection && collectionIds.Contains(e.Id))
            );
            if (element is null)
                continue;

            result.Add(
                new ShowUsageRefDto(group.Id, group.Name, !element.Pinned && HasRule(group))
            );
        }

        return result;
    }

    private static bool HasRule(Domain.Programming.Group group) =>
        group.Mode == GroupMode.Dynamic && group.FilterJson is not null;
}
