using System.Runtime.Versioning;
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using TeamsISO.Engine.Interop;
using TeamsISO.Engine.NdiInterop;

namespace TeamsISO.Engine.IntegrationTests;

/// <summary>
/// Integration tests that talk to a real NDI runtime — gated behind
/// <c>requires=ndi</c> so the default CI run skips them. Run locally with:
///   dotnet test --filter requires=ndi
///
/// Pre-conditions:
///   - Windows host
///   - NDI 6 Runtime installed (NDI_RUNTIME_DIR_V6 set)
///   - Network discovery permitted on the loopback / local subnet
/// </summary>
[SupportedOSPlatform("windows")]
public class NdiInteropIntegrationTests
{
    private static NdiInteropPInvoke NewInterop() =>
        new(NullLogger<NdiInteropPInvoke>.Instance);

    [Fact]
    [Trait("requires", "ndi")]
    public void NdiRuntime_LoadsAndReportsVersion()
    {
        using var interop = NewInterop();

        var version = interop.GetRuntimeVersion();

        version.Should().NotBeNullOrEmpty();
        version.Should().StartWith(NdiVersion.ExpectedRuntimeVersionPrefix,
            because: "the engine probe asserts this prefix; if it ever drifts CI must catch it");
    }

    [Fact]
    [Trait("requires", "ndi")]
    public void Finder_DefaultGroups_CreatesAndDisposesCleanly()
    {
        using var interop = NewInterop();

        using (var finder = interop.CreateFinder())
        {
            finder.Should().NotBeNull(because: "default-group finder must construct successfully");
            // Snapshot any visible sources — exercises the path; we don't assert on count
            // because the test environment's NDI sources are unknowable.
            _ = interop.GetCurrentSources(finder);
        }
    }

    [Theory]
    [Trait("requires", "ndi")]
    [InlineData("teamsiso-test-input")]
    [InlineData("teamsiso-test-input,production")]
    [InlineData(" teamsiso-test-input ")]
    public void Finder_CustomGroups_DoesNotThrow(string groups)
    {
        using var interop = NewInterop();

        var act = () =>
        {
            using var finder = interop.CreateFinder(groups);
            _ = interop.GetCurrentSources(finder);
        };

        act.Should().NotThrow(because: $"groups='{groups}' must round-trip into NDIlib_find_create_v2 cleanly");
    }

    [Fact]
    [Trait("requires", "ndi")]
    public void Sender_DefaultGroups_CreatesAndDisposesCleanly()
    {
        using var interop = NewInterop();

        using (var sender = interop.CreateSender("TEAMSISO_TEST_DEFAULT"))
        {
            sender.Should().NotBeNull();
        }
    }

    [Fact]
    [Trait("requires", "ndi")]
    public void Sender_CustomGroups_CreatesAndDisposesCleanly()
    {
        using var interop = NewInterop();

        using (var sender = interop.CreateSender("TEAMSISO_TEST_GROUPED", "teamsiso-test-output"))
        {
            sender.Should().NotBeNull();
        }
    }

    [Fact]
    [Trait("requires", "ndi")]
    public async Task LoopbackDiscovery_FindsOurOwnSenderWithinFiveSeconds()
    {
        // End-to-end check: a sender we create on the local machine must be visible
        // to a finder running in the same process, within a reasonable window. Catches
        // network-layer regressions (firewall, mDNS, multicast disable).
        using var interop = NewInterop();

        var uniqueName = $"TEAMSISO_LOOP_{Guid.NewGuid().ToString("N")[..8].ToUpperInvariant()}";
        using var sender = interop.CreateSender(uniqueName);
        using var finder = interop.CreateFinder();

        var deadline = DateTime.UtcNow.AddSeconds(5);
        bool found = false;
        while (DateTime.UtcNow < deadline)
        {
            var sources = interop.GetCurrentSources(finder);
            if (sources.Any(s => s.Contains(uniqueName, StringComparison.Ordinal)))
            {
                found = true;
                break;
            }
            await Task.Delay(250);
        }

        found.Should().BeTrue(
            because: $"local sender '{uniqueName}' must be discovered by a same-process finder within 5s");
    }
}
