using TeleWave.Application.Streaming.ListPublicChannels;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using Xunit;

namespace TeleWave.Application.Tests.Streaming;

/// <summary>
/// Витрина зрителя: только включённые каналы, порядок по номеру и обложка того, что идёт прямо
/// сейчас. Выключенный канал не должен просачиваться в публичную выдачу.
/// </summary>
public class ListPublicChannelsTests
{
    private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);

    [Fact]
    public async Task List_ReturnsEnabledChannelsOrderedByNumber()
    {
        var fixture = new TestDb();
        var numbered = Channel.Create("Второй", "two", T0);
        numbered.UpdateTimeSettings(
            2,
            Channel.DefaultUtcOffsetMinutes,
            Channel.DefaultDayStartTime
        );
        var first = Channel.Create("Первый", "one", T0);
        first.UpdateTimeSettings(1, Channel.DefaultUtcOffsetMinutes, Channel.DefaultDayStartTime);
        var unnumbered = Channel.Create("Аврора", "aurora", T0);
        var disabled = Channel.Create("Выключенный", "off", T0);

        foreach (var channel in new[] { numbered, first, unnumbered })
            channel.UpdateSettings(channel.Name, isEnabled: true, null);
        disabled.UpdateSettings("Выключенный", isEnabled: false, null);

        await using (var seed = fixture.New())
        {
            seed.Channels.AddRange(numbered, first, unnumbered, disabled);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var channels = await new ListPublicChannelsQueryHandler(db).Handle(
            new ListPublicChannelsQuery(),
            CancellationToken.None
        );

        // Ненумерованный уходит в конец: на телевизоре порядок задаёт номер.
        Assert.Equal(["Первый", "Второй", "Аврора"], channels.Select(c => c.Name).ToArray());
        Assert.Null(channels[^1].Number);
        Assert.All(channels, c => Assert.Null(c.CurrentShowId));
    }

    [Fact]
    public async Task List_AddsCurrentProgramCover()
    {
        var fixture = new TestDb();
        var channel = Channel.Create("Первый", "one", T0);
        channel.UpdateSettings("Первый", isEnabled: true, null);
        var logoId = Guid.NewGuid();
        channel.UpdateViewerSettings(logoId, LogoCorner.TopRight, 0.5, showClock: true, 0.3);

        var show = Show.Create("Сейчас в эфире", ShowKind.Series);
        show.SetPosterImage(Guid.NewGuid());
        var asset = MediaAsset.Register("now.mkv", ".mkv", MediaSource.Upload);
        var now = DateTimeOffset.UtcNow;
        var current = ScheduleEntry.Program(
            channel.Id,
            asset.Id,
            now.AddMinutes(-5),
            now.AddMinutes(25),
            show.Id,
            0
        );
        // Прошедшая запись того же канала не должна перебить текущую.
        var past = ScheduleEntry.Program(
            channel.Id,
            asset.Id,
            now.AddHours(-2),
            now.AddHours(-1),
            Guid.NewGuid(),
            0
        );

        await using (var seed = fixture.New())
        {
            seed.Channels.Add(channel);
            seed.Shows.Add(show);
            seed.MediaAssets.Add(asset);
            seed.ScheduleEntries.AddRange(current, past);
            await seed.SaveChangesAsync(CancellationToken.None);
        }

        await using var db = fixture.New();
        var channels = await new ListPublicChannelsQueryHandler(db).Handle(
            new ListPublicChannelsQuery(),
            CancellationToken.None
        );

        var dto = Assert.Single(channels);
        Assert.Equal(show.Id, dto.CurrentShowId);
        Assert.Equal("Сейчас в эфире", dto.CurrentShowName);
        Assert.Equal(show.PosterImageId, dto.CurrentShowPosterImageId);
        Assert.Equal(logoId, dto.LogoImageId);
        Assert.Equal(LogoCorner.TopRight, dto.LogoCorner);
        Assert.Equal(0.5, dto.LogoOpacity);
        Assert.True(dto.ShowClock);
        Assert.Equal(0.3, dto.AnalogFilterStrength);
    }

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

        var channels = await new ListPublicChannelsQueryHandler(db).Handle(
            new ListPublicChannelsQuery(),
            CancellationToken.None
        );

        Assert.Empty(channels);
    }
}
