using LiteCqrs;
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using TeleWave.Application.Library.Genres;

namespace TeleWave.Application.Library.BulkTagShows;

public sealed class BulkTagShowsCommandHandler(IAppDbContext dbContext)
    : ICommandHandler<BulkTagShowsCommand, Result<int>>
{
    public async Task<Result<int>> Handle(
        BulkTagShowsCommand command,
        CancellationToken cancellationToken
    )
    {
        var showIds = command.ShowIds.Distinct().ToList();
        if (showIds.Count == 0)
            return Result.Success(0);

        var genreIds = command.GenreIds?.Distinct().ToList();
        if (genreIds is { Count: > 0 })
        {
            // Ссылка на несуществующий жанр упала бы нарушением внешнего ключа — проверяем заранее.
            var known = await dbContext
                .Genres.Where(g => genreIds.Contains(g.Id))
                .CountAsync(cancellationToken);
            if (known != genreIds.Count)
                return Result.Failure<int>(GenreErrors.NotFound);
        }

        var shows = await dbContext
            .Shows.Include(s => s.Genres)
            .Where(s => showIds.Contains(s.Id))
            .ToListAsync(cancellationToken);

        // Пропавшее шоу не роняет всю операцию: список выбирали галочками, и пока его собирали,
        // что-то могли удалить. Возвращаем, скольких реально коснулись.
        foreach (var show in shows)
        {
            if (genreIds is not null)
                ApplyGenres(show, genreIds, command.ReplaceGenres);

            if (command.SetAudience)
                show.SetAudience(command.Audience);
        }

        return Result.Success(shows.Count);
    }

    /// <summary>
    /// Основным остаётся прежний основной жанр — массовая разметка не должна незаметно менять то,
    /// что показано в списке шоу. Если его не было (жанров не было вовсе), основным станет первый
    /// из добавленных, как и при обычной правке.
    /// </summary>
    private static void ApplyGenres(
        Domain.Library.Show show,
        IReadOnlyList<Guid> genreIds,
        bool replace
    )
    {
        var primary = show.PrimaryGenreId;
        var ids = replace ? genreIds : show.Genres.Select(g => g.GenreId).Union(genreIds).ToList();
        show.SetGenres(ids, ids.Contains(primary ?? Guid.Empty) ? primary : null);
    }
}
