using System.Threading.Channels;
using Microsoft.Extensions.Logging.Abstractions;
using DragonISO.Engine.Pipeline;
using DragonISO.Engine.Tests.Fakes;

namespace DragonISO.Engine.Tests.Pipeline;

public class NdiSenderTests
{
    private const string Output = "Dragon-ISO_01";

    private static ProcessedFrame MakeFrame(long ts) =>
        new(1920, 1080, ts, new byte[1920 * 1080 * 4], PixelFormat.Bgra);

    [Fact]
    public async Task SendNextAsync_FrameAvailable_ForwardsToInterop()
    {
        var interop = new FakeNdiInterop();
        var input = Channel.CreateUnbounded<ProcessedFrame>();
        input.Writer.TryWrite(MakeFrame(100));
        var sender = new NdiSender(interop, Output, input.Reader, NullLogger<NdiSender>.Instance);

        var sent = await sender.SendNextAsync(CancellationToken.None);

        sent.Should().BeTrue();
        sender.FramesSent.Should().Be(1);
        interop.SentFrames[Output].Should().HaveCount(1);
        interop.SentFrames[Output][0].TimestampTicks.Should().Be(100);
    }

    [Fact]
    public async Task SendNextAsync_ChannelCompleted_ReturnsFalse()
    {
        var interop = new FakeNdiInterop();
        var input = Channel.CreateUnbounded<ProcessedFrame>();
        input.Writer.TryComplete();
        var sender = new NdiSender(interop, Output, input.Reader, NullLogger<NdiSender>.Instance);

        var sent = await sender.SendNextAsync(CancellationToken.None);

        sent.Should().BeFalse();
    }

    [Fact]
    public async Task RunAsync_CompletesOnCancellation()
    {
        var interop = new FakeNdiInterop();
        var input = Channel.CreateUnbounded<ProcessedFrame>();
        var sender = new NdiSender(interop, Output, input.Reader, NullLogger<NdiSender>.Instance);

        using var cts = new CancellationTokenSource();
        var runTask = sender.RunAsync(cts.Token);

        // Send one frame, then cancel
        input.Writer.TryWrite(MakeFrame(1));
        await Task.Delay(30);
        cts.Cancel();
        await runTask;

        interop.SenderCreatedCount[Output].Should().Be(1);
    }
}
