using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Library.BulkTagShows;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Library;
using Xunit;

namespace TeleWave.Application.Tests.Library;

/// <summary>
/// Массовая разметка: жанры и рейтинг — то, на что опираются группы контента, и проставлять их
/// по одному шоу нереально. Проверяем, что пакет не затирает лишнего.
/// </summary>
public class BulkTagShowsTests
{
    [Fact]
    public async Task AddsGenres_WithoutErasingExisting()
    {
        var fixture = new TestDb();
        var existing = Genre.Create("Комедия", "comedy");
        var added = Genre.Create("Мультфильм", "animation");
        var show = Show.Create("A", ShowKind.Series);
        show.SetGenres([existing.Id]);

        await using (var seed = fixture.New())
        {
            seed.Genres.AddRange(existing, added);
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await RunAsync(fixture, new BulkTagShowsCommand([show.Id], GenreIds: [added.Id]));

        await using var check = fixture.New();
        var stored = check.Shows.Include(s => s.Genres).Single();
        Assert.Equal(2, stored.Genres.Count);
        // Основной жанр не должен незаметно переехать: его показывает список шоу.
        Assert.Equal(existing.Id, stored.PrimaryGenreId);
    }

    [Fact]
    public async Task ReplacesGenres_WhenAsked()
    {
        var fixture = new TestDb();
        var old = Genre.Create("Комедия", "comedy");
        var fresh = Genre.Create("Мультфильм", "animation");
        var show = Show.Create("A", ShowKind.Series);
        show.SetGenres([old.Id]);

        await using (var seed = fixture.New())
        {
            seed.Genres.AddRange(old, fresh);
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await RunAsync(
            fixture,
            new BulkTagShowsCommand([show.Id], GenreIds: [fresh.Id], ReplaceGenres: true)
        );

        await using var check = fixture.New();
        var stored = check.Shows.Include(s => s.Genres).Single();
        Assert.Equal(fresh.Id, stored.Genres.Single().GenreId);
    }

    [Fact]
    public async Task SetsAudience_AndLeavesGenresAlone()
    {
        var fixture = new TestDb();
        var genre = Genre.Create("Комедия", "comedy");
        var show = Show.Create("A", ShowKind.Series);
        show.SetGenres([genre.Id]);

        await using (var seed = fixture.New())
        {
            seed.Genres.Add(genre);
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await RunAsync(
            fixture,
            new BulkTagShowsCommand([show.Id], Audience: ShowAudience.Pg13, SetAudience: true)
        );

        await using var check = fixture.New();
        var stored = check.Shows.Include(s => s.Genres).Single();
        Assert.Equal(ShowAudience.Pg13, stored.Audience);
        Assert.Single(stored.Genres);
    }

    [Fact]
    public async Task FailsForUnknownGenre()
    {
        // Ссылка на несуществующий жанр упала бы нарушением внешнего ключа посреди пакета.
        var fixture = new TestDb();
        var show = Show.Create("A", ShowKind.Series);
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var result = await new BulkTagShowsCommandHandler(db).Handle(
            new BulkTagShowsCommand([show.Id], GenreIds: [Guid.NewGuid()]),
            CancellationToken.None
        );

        Assert.False(result.IsSuccess);
    }

    [Fact]
    public async Task SkipsMissingShows_WithoutFailing()
    {
        var fixture = new TestDb();
        var show = Show.Create("A", ShowKind.Series);
        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        var updated = await RunAsync(
            fixture,
            new BulkTagShowsCommand(
                [show.Id, Guid.NewGuid()],
                Audience: ShowAudience.R,
                SetAudience: true
            )
        );

        // Пока собирали галочки, шоу могли удалить — это не повод валить всю операцию.
        Assert.Equal(1, updated);
    }

    private static async Task<int> RunAsync(TestDb fixture, BulkTagShowsCommand command)
    {
        await using var db = fixture.New();
        var result = await new BulkTagShowsCommandHandler(db).Handle(
            command,
            CancellationToken.None
        );
        Assert.True(result.IsSuccess);
        await db.SaveChangesAsync(CancellationToken.None);
        return result.Value;
    }
}
