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

namespace TeleWave.Application.Library.Collections;

/// <summary>
/// Подбирает коллекции постер по её частям: берётся первая часть с постером — обычно это первый
/// фильм цикла, и он же узнаётся с обложки. Отдельный тип, потому что подбор нужен и при создании
/// коллекции, и по кнопке для уже заведённых.
///
/// Своего изображения коллекция не заводит: постер — это тот же <see cref="Image"/> из реестра,
/// на который ссылается шоу. Копировать файл ради второй ссылки незачем.
/// </summary>
public sealed class CollectionPosterPicker(IAppDbContext dbContext)
{
    /// <summary>
    /// Проставляет постер коллекциям, у которых его нет. Уже выбранный не трогаем: его мог
    /// поставить человек, и перебивать такой выбор автоматикой нельзя. Возвращает, скольким
    /// коллекциям постер нашёлся.
    /// </summary>
    public async Task<int> FillAsync(
        IReadOnlyList<Collection> collections,
        CancellationToken cancellationToken
    )
    {
        var pending = collections.Where(c => c.PosterImageId is null).ToList();
        if (pending.Count == 0)
            return 0;

        var posters = await PostersAsync(pending, cancellationToken);
        var filled = 0;

        foreach (var collection in pending)
        {
            if (Pick(collection, posters) is not { } imageId)
                continue;

            collection.SetPosterImage(imageId);
            filled++;
        }

        return filled;
    }

    /// <summary>Постер первой по порядку части, у которой он есть.</summary>
    private static Guid? Pick(Collection collection, IReadOnlyDictionary<Guid, Guid> posters) =>
        collection
            .Items.OrderBy(i => i.Position)
            .Select(i => posters.TryGetValue(i.ShowId, out var image) ? image : (Guid?)null)
            .FirstOrDefault(image => image is not null);

    /// <summary>Постеры шоу, входящих в эти коллекции, одним запросом на всю пачку.</summary>
    private async Task<Dictionary<Guid, Guid>> PostersAsync(
        IReadOnlyList<Collection> collections,
        CancellationToken cancellationToken
    )
    {
        var showIds = collections
            .SelectMany(c => c.Items.Select(i => i.ShowId))
            .Distinct()
            .ToList();

        var shows = await dbContext
            .Shows.AsNoTracking()
            .Where(s => showIds.Contains(s.Id) && s.PosterImageId != null)
            .Select(s => new { s.Id, PosterImageId = s.PosterImageId!.Value })
            .ToListAsync(cancellationToken);

        return shows.ToDictionary(s => s.Id, s => s.PosterImageId);
    }
}
