using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Domain.Broadcast;

namespace TeleWave.Application.Streaming.ListPublicChannels;

public sealed class ListPublicChannelsQueryHandler(IAppDbContext dbContext)
    : IQueryHandler<ListPublicChannelsQuery, IReadOnlyList<PublicChannelDto>>
{
    public async Task<IReadOnlyList<PublicChannelDto>> Handle(
        ListPublicChannelsQuery query,
        CancellationToken cancellationToken
    )
    {
        var channels = await dbContext
            .Channels.AsNoTracking()
            .Where(c => c.IsEnabled)
            // Каналы без номера — в конец: на телевизоре порядок задаёт номер, а имя лишь
            // разрешает ничью между ненумерованными.
            .OrderBy(c => c.Number == null)
            .ThenBy(c => c.Number)
            .ThenBy(c => c.Name)
            .Select(c => new
            {
                c.Id,
                c.Slug,
                c.Name,
                c.Number,
                c.LogoImageId,
                c.LogoCorner,
                c.LogoOpacity,
                c.ShowClock,
                c.AnalogFilterStrength,
            })
            .ToListAsync(cancellationToken);
        if (channels.Count == 0)
            return [];

        var now = DateTimeOffset.UtcNow;
        var channelIds = channels.Select(c => c.Id).ToList();

        // Что идёт прямо сейчас на каждом канале — для постера-обложки плитки. Тип записи не важен:
        // фон — это те же программы из группы, и подписаны они так же. Важно лишь, что у записи
        // есть шоу: у заставок, рекламы и аварийного филлера его нет, и обложке взяться неоткуда.
        var currentByChannel = await dbContext
            .ScheduleEntries.AsNoTracking()
            .Where(e =>
                channelIds.Contains(e.ChannelId)
                && e.StartsAtUtc <= now
                && e.EndsAtUtc > now
                && e.ShowId != null
            )
            .Select(e => new { e.ChannelId, ShowId = e.ShowId!.Value })
            .ToListAsync(cancellationToken);
        var currentShowByChannel = currentByChannel
            .GroupBy(x => x.ChannelId)
            .ToDictionary(g => g.Key, g => g.First().ShowId);

        var showIds = currentShowByChannel.Values.Distinct().ToList();
        var shows = await dbContext
            .Shows.AsNoTracking()
            .Where(s => showIds.Contains(s.Id))
            .Select(s => new
            {
                s.Id,
                s.Name,
                s.PosterImageId,
            })
            .ToDictionaryAsync(s => s.Id, cancellationToken);

        return channels
            .Select(c =>
            {
                var showId = currentShowByChannel.GetValueOrDefault(c.Id);
                var show = showId != Guid.Empty ? shows.GetValueOrDefault(showId) : null;
                return new PublicChannelDto(
                    c.Id,
                    c.Slug,
                    c.Name,
                    c.Number,
                    show is null ? null : showId,
                    show?.Name,
                    show?.PosterImageId,
                    c.LogoImageId,
                    c.LogoCorner,
                    c.LogoOpacity,
                    c.ShowClock,
                    c.AnalogFilterStrength
                );
            })
            .ToList();
    }
}
