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

namespace TeleWave.Application.Library.ListShows;

public sealed class ListShowsQueryHandler(IAppDbContext dbContext)
    : IQueryHandler<ListShowsQuery, IReadOnlyList<ShowSummaryDto>>
{
    public async Task<IReadOnlyList<ShowSummaryDto>> Handle(
        ListShowsQuery query,
        CancellationToken cancellationToken
    )
    {
        var source = dbContext
            .Shows.AsNoTracking()
            .Include(s => s.Episodes)
            .Include(s => s.Genres)
            // Серии и жанры — сиблинги: одним запросом это честное перемножение (сериал на 200 серий
            // с тремя жанрами даёт 600 строк вместо 203). Разделяем.
            .AsSplitQuery()
            .Where(s =>
                query.Interstitials
                    ? s.Kind == ShowKind.Interstitial
                    : s.Kind != ShowKind.Interstitial
            );

        var filtered = query.GenreId is { } genreId
            ? source.Where(s => s.Genres.Any(g => g.GenreId == genreId))
            : source;

        var shows = await filtered.OrderBy(s => s.Name).ToListAsync(cancellationToken);

        // Названия всех проставленных жанров: основной показывается в колонке, прочие — под «…».
        var genreIds = shows.SelectMany(s => s.Genres.Select(g => g.GenreId)).Distinct().ToList();
        var genreNames = await dbContext
            .Genres.AsNoTracking()
            .Where(g => genreIds.Contains(g.Id))
            .ToDictionaryAsync(g => g.Id, g => g.Name, cancellationToken);

        // Имена ассетов нужны, чтобы распознать сезоны (номера в модели не хранятся).
        var assetIds = shows
            .SelectMany(s => s.Episodes.Select(e => e.MediaAssetId))
            .Distinct()
            .ToList();
        var names = await dbContext
            .MediaAssets.AsNoTracking()
            .Where(a => assetIds.Contains(a.Id))
            .Select(a => new { a.Id, a.OriginalFileName })
            .ToDictionaryAsync(a => a.Id, a => a.OriginalFileName, cancellationToken);

        return shows
            .Select(s =>
            {
                var seasons = s
                    .Episodes.Select(e =>
                        names.TryGetValue(e.MediaAssetId, out var n)
                            ? EpisodeName.ParseSeason(n)
                            : null
                    )
                    .Where(season => season is not null)
                    .Distinct()
                    .Count();
                return new ShowSummaryDto(
                    s.Id,
                    s.Name,
                    s.OriginalName,
                    s.Kind,
                    s.Audience,
                    s.Episodes.Count,
                    seasons,
                    s.Year,
                    s.PosterImageId is not null,
                    s.CreatedAt,
                    s.PrimaryGenreId is { } primaryId
                    && genreNames.TryGetValue(primaryId, out var g)
                        ? g
                        : null,
                    [
                        .. s
                            .Genres.Where(x => !x.IsPrimary)
                            .Select(x => genreNames.GetValueOrDefault(x.GenreId))
                            .OfType<string>()
                            .Order(StringComparer.OrdinalIgnoreCase),
                    ]
                );
            })
            .ToList();
    }
}
