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

namespace TeleWave.Application.Library.Interstitials.ListInterstitialBlocks;

public sealed class ListInterstitialBlocksQueryHandler(IAppDbContext dbContext)
    : IQueryHandler<ListInterstitialBlocksQuery, IReadOnlyList<InterstitialBlockDto>>
{
    public async Task<IReadOnlyList<InterstitialBlockDto>> Handle(
        ListInterstitialBlocksQuery query,
        CancellationToken cancellationToken
    )
    {
        var collections = await dbContext
            .Collections.AsNoTracking()
            .Include(c => c.Items)
            .OrderBy(c => c.Name)
            .ToListAsync(cancellationToken);

        // «Блок» — не отдельная сущность, а признак состава: коллекция целиком из роликов. Смешанные
        // коллекции (франшизы) остаются на своём экране и сюда не попадают.
        var showIds = collections
            .SelectMany(c => c.Items.Select(i => i.ShowId))
            .Distinct()
            .ToList();
        var clips = await dbContext
            .Shows.AsNoTracking()
            .Where(s => showIds.Contains(s.Id) && s.Kind == ShowKind.Interstitial)
            .Select(s => new { s.Id, AssetIds = s.Episodes.Select(e => e.MediaAssetId).ToList() })
            .ToDictionaryAsync(s => s.Id, s => s.AssetIds, cancellationToken);

        var assetIds = clips.Values.SelectMany(ids => ids).Distinct().ToList();
        var durations = await dbContext
            .MediaAssets.AsNoTracking()
            .Where(a => assetIds.Contains(a.Id) && a.Duration != null)
            .Select(a => new { a.Id, a.Duration })
            .ToDictionaryAsync(a => a.Id, a => a.Duration!.Value.TotalSeconds, cancellationToken);

        // Длительность шоу-ролика считаем один раз: одно и то же шоу встречается в нескольких блоках.
        var clipSeconds = clips.ToDictionary(
            pair => pair.Key,
            pair => pair.Value.Sum((Guid assetId) => durations.GetValueOrDefault(assetId, 0d))
        );

        return collections
            .Where(c => c.Items.Count > 0 && c.Items.All(i => clips.ContainsKey(i.ShowId)))
            .Select(c => new InterstitialBlockDto(
                c.Id,
                c.Name,
                c.Items.Count,
                c.Items.Sum(i => clipSeconds[i.ShowId])
            ))
            .ToList();
    }
}
