using TeleWave.Application.Library.GetShowUsage;
using TeleWave.Application.Programming.Groups;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Programming;
using Xunit;

namespace TeleWave.Application.Tests.Library;

/// <summary>
/// «Где используется» для шоу: удаление отбивается ошибкой «используется», и без этого списка
/// непонятно, кем именно.
/// </summary>
public class ShowUsageTests
{
    [Fact]
    public async Task ReportsExplicitGroupMembership()
    {
        var fixture = new TestDb();
        var show = Show.Create("Шоу", ShowKind.Series);
        var group = Group.Create("Явная");
        group.AddElement(GroupElementKind.Show, show.Id);

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

        var usage = await GetAsync(fixture, show.Id);

        var reference = Assert.Single(usage.Groups);
        Assert.Equal("Явная", reference.Name);
        Assert.False(reference.ViaRule);
    }

    [Fact]
    public async Task ReportsMembershipByRule()
    {
        // У динамической группы шоу входит без своей позиции — для «можно ли удалять» это то же самое.
        var fixture = new TestDb();
        var genre = Genre.Create("Мультфильм", "animation");
        var show = Show.Create("Шоу", ShowKind.Series);
        show.SetGenres([genre.Id]);

        var group = Group.Create("По правилу");
        group.SetFilter(new GroupFilter(GenreIds: [genre.Id]).ToJson());
        group.SetMode(GroupMode.Dynamic);

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

        var usage = await GetAsync(fixture, show.Id);

        Assert.True(Assert.Single(usage.Groups).ViaRule);
    }

    [Fact]
    public async Task ReportsCollectionsAndGroupsReachedThroughThem()
    {
        // В группу шоу попадает и через франшизу, целиком — это тоже использование.
        var fixture = new TestDb();
        var show = Show.Create("Часть", ShowKind.Single);
        var collection = Collection.Create("Франшиза");
        collection.AddShow(show.Id);
        var group = Group.Create("С франшизой");
        group.AddElement(GroupElementKind.Collection, collection.Id);

        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            seed.Collections.Add(collection);
            seed.Groups.Add(group);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        var usage = await GetAsync(fixture, show.Id);

        Assert.Equal("Франшиза", Assert.Single(usage.Collections).Name);
        Assert.Equal("С франшизой", Assert.Single(usage.Groups).Name);
    }

    [Fact]
    public async Task ReportsChannelsWithScheduledEntries()
    {
        var fixture = new TestDb();
        var show = Show.Create("Шоу", ShowKind.Series);
        var channel = Channel.Create("Первый", "one", DateTimeOffset.UtcNow);
        var start = new DateTimeOffset(2026, 3, 1, 12, 0, 0, TimeSpan.Zero);

        await using (var seed = fixture.New())
        {
            seed.Shows.Add(show);
            seed.Channels.Add(channel);
            seed.ScheduleEntries.Add(
                ScheduleEntry.Program(
                    channel.Id,
                    Guid.NewGuid(),
                    start,
                    start.AddMinutes(30),
                    show.Id,
                    0
                )
            );
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        var usage = await GetAsync(fixture, show.Id);

        Assert.Equal("Первый", Assert.Single(usage.Channels).Name);
    }

    [Fact]
    public async Task UnknownShow_ReturnsNotFound()
    {
        var fixture = new TestDb();
        await using var db = fixture.New();

        var result = await new GetShowUsageQueryHandler(db, GroupServices.Dynamic(db)).Handle(
            new GetShowUsageQuery(Guid.NewGuid()),
            CancellationToken.None
        );

        Assert.False(result.IsSuccess);
    }

    private static async Task<ShowUsageDto> GetAsync(TestDb fixture, Guid showId)
    {
        await using var db = fixture.New();
        var result = await new GetShowUsageQueryHandler(db, GroupServices.Dynamic(db)).Handle(
            new GetShowUsageQuery(showId),
            CancellationToken.None
        );
        Assert.True(result.IsSuccess);
        return result.Value;
    }
}
