using TeleWave.Application.Library.ListShows;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;

namespace TeleWave.Application.Tests.Library;

/// <summary>
/// Список библиотеки: он же считает сезоны (номера в модели не хранятся — только в именах файлов)
/// и подставляет названия жанров, поэтому проверяем именно сводку, а не факт выборки.
/// </summary>
public class ListShowsTests
{
    private sealed record Seeded(TestDb Db, Guid ActionGenreId, Guid DramaGenreId, Guid SeriesId);

    private static async Task<Seeded> SeedAsync()
    {
        var fixture = new TestDb();
        var action = Genre.Create("Боевик", "action");
        var drama = Genre.Create("Драма", "drama");

        var series = Show.Create("Сериал", ShowKind.Series);
        series.SetGenres([action.Id, drama.Id], action.Id);
        series.SetPosterImage(Guid.NewGuid());
        var assets = new[]
        {
            MediaAsset.Register("Serial.S01E01.mkv", ".mkv", MediaSource.Upload),
            MediaAsset.Register("Serial.S01E02.mkv", ".mkv", MediaSource.Upload),
            MediaAsset.Register("Serial.S02E01.mkv", ".mkv", MediaSource.Upload),
            // Имя без метки сезона: серия считается, сезон — нет.
            MediaAsset.Register("Serial-bonus.mkv", ".mkv", MediaSource.Upload),
        };
        foreach (var asset in assets)
            series.AddEpisode(asset.Id);

        var film = Show.Create("Автофильм", ShowKind.Single);
        var bumper = Show.Create("Ролик", ShowKind.Interstitial);

        await using var seed = fixture.New();
        seed.Genres.AddRange(action, drama);
        seed.MediaAssets.AddRange(assets);
        seed.Shows.AddRange(series, film, bumper);
        await seed.SaveChangesAsync(CancellationToken.None);

        return new Seeded(fixture, action.Id, drama.Id, series.Id);
    }

    [Fact]
    public async Task List_ReturnsContentSortedByName_WithoutInterstitials()
    {
        var seeded = await SeedAsync();
        await using var db = seeded.Db.New();

        var shows = await new ListShowsQueryHandler(db).Handle(
            new ListShowsQuery(),
            CancellationToken.None
        );

        // Ролики-врезки живут на своей странице и в общей библиотеке только мешали бы.
        Assert.Equal(["Автофильм", "Сериал"], shows.Select(s => s.Name).ToArray());
    }

    [Fact]
    public async Task List_CountsEpisodesAndDistinctSeasons()
    {
        var seeded = await SeedAsync();
        await using var db = seeded.Db.New();

        var shows = await new ListShowsQueryHandler(db).Handle(
            new ListShowsQuery(),
            CancellationToken.None
        );

        var series = shows.Single(s => s.Id == seeded.SeriesId);
        Assert.Equal(4, series.EpisodeCount);
        Assert.Equal(2, series.SeasonCount);
        Assert.True(series.HasPoster);
        Assert.Equal("Боевик", series.PrimaryGenre);
        Assert.Equal(["Драма"], series.OtherGenres.ToArray());
    }

    [Fact]
    public async Task List_ByGenre_KeepsOnlyShowsWithIt()
    {
        var seeded = await SeedAsync();
        await using var db = seeded.Db.New();

        var byDrama = await new ListShowsQueryHandler(db).Handle(
            new ListShowsQuery(seeded.DramaGenreId),
            CancellationToken.None
        );
        var byUnknown = await new ListShowsQueryHandler(db).Handle(
            new ListShowsQuery(Guid.NewGuid()),
            CancellationToken.None
        );

        // Жанр ищется среди всех, а не только основного.
        Assert.Equal(seeded.SeriesId, Assert.Single(byDrama).Id);
        Assert.Empty(byUnknown);
    }

    [Fact]
    public async Task List_Interstitials_ReturnsOnlyThem()
    {
        var seeded = await SeedAsync();
        await using var db = seeded.Db.New();

        var shows = await new ListShowsQueryHandler(db).Handle(
            new ListShowsQuery(Interstitials: true),
            CancellationToken.None
        );

        var only = Assert.Single(shows);
        Assert.Equal("Ролик", only.Name);
        Assert.Equal(ShowKind.Interstitial, only.Kind);
        Assert.Equal(0, only.SeasonCount);
        Assert.Null(only.PrimaryGenre);
        Assert.False(only.HasPoster);
    }
}
