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

namespace TeleWave.Application.Streaming.GetPublicEpg;

public sealed class GetPublicEpgQueryHandler(IAppDbContext dbContext)
    : IQueryHandler<GetPublicEpgQuery, Result<IReadOnlyList<PublicEpgEntryDto>>>
{
    public async Task<Result<IReadOnlyList<PublicEpgEntryDto>>> Handle(
        GetPublicEpgQuery query,
        CancellationToken cancellationToken
    )
    {
        var channelId = await dbContext
            .Channels.AsNoTracking()
            .Where(c => c.Slug == query.Slug && c.IsEnabled)
            .Select(c => (Guid?)c.Id)
            .FirstOrDefaultAsync(cancellationToken);
        if (channelId is null)
            return Result.Failure<IReadOnlyList<PublicEpgEntryDto>>(ChannelErrors.NotFound);

        var entries = await dbContext
            .ScheduleEntries.AsNoTracking()
            .Where(e =>
                e.ChannelId == channelId
                && e.StartsAtUtc < query.ToUtc
                && e.EndsAtUtc > query.FromUtc
            )
            .OrderBy(e => e.StartsAtUtc)
            .Select(e => new
            {
                e.Kind,
                e.StartsAtUtc,
                e.EndsAtUtc,
                e.ShowId,
                e.MediaAssetId,
            })
            .ToListAsync(cancellationToken);

        var showIds = entries
            .Where(e => e.ShowId != null)
            .Select(e => e.ShowId!.Value)
            .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);

        // Метаданные серий: ключ — (шоу, ассет).
        var assetIds = entries.Select(e => e.MediaAssetId).Distinct().ToList();
        var episodes = await dbContext
            .Shows.AsNoTracking()
            .SelectMany(s => s.Episodes)
            .Where(e => showIds.Contains(e.ShowId) && assetIds.Contains(e.MediaAssetId))
            .Select(e => new
            {
                e.ShowId,
                e.MediaAssetId,
                e.Id,
                e.Title,
                e.Overview,
                e.StillImageId,
            })
            .ToListAsync(cancellationToken);
        var episodeByKey = episodes
            .GroupBy(e => (e.ShowId, e.MediaAssetId))
            .ToDictionary(g => g.Key, g => g.First());

        var dtos = entries
            .Select(e =>
            {
                var show = e.ShowId is { } sid ? shows.GetValueOrDefault(sid) : null;
                var episode =
                    e.ShowId is { } showId
                    && episodeByKey.TryGetValue((showId, e.MediaAssetId), out var ep)
                        ? ep
                        : null;
                return new PublicEpgEntryDto(
                    e.Kind,
                    e.StartsAtUtc,
                    e.EndsAtUtc,
                    e.ShowId,
                    show?.Name,
                    show?.PosterImageId,
                    episode?.Id,
                    episode?.Title,
                    episode?.Overview,
                    episode?.StillImageId
                );
            })
            .ToList();

        return Result.Success<IReadOnlyList<PublicEpgEntryDto>>(dtos);
    }
}
