using System.Runtime.InteropServices;
using VoiceCat.Interop;

namespace VoiceCat.Interop.Tests;

/// <summary>
/// API-surface smoke tests for StreamFeedPcm / SetPcmSink through the P/Invoke layer.
/// These tests need voicecat.dll but NOT a running server — they verify the C# wrapper
/// compiles, is callable, and the error paths work at the C-ABI boundary. Full E2E relay
/// verification is handled by tests/test_external_pcm.cpp (C++ ctest).
/// </summary>
public sealed class ExternalPcmTests
{
    [Fact]
    public void StreamFeedPcm_NoActiveStream_ReturnsInvalidArg()
    {
        using var client = new VoiceCatClient("ext-pcm-test", "0.1", VcLogLevel.Off);
        var pcm = new short[960];
        // Stream 0 doesn't exist — core must return InvalidArg, not crash.
        var result = client.StreamFeedPcm(0, pcm, 960, channels: 1);
        Assert.Equal(VcResult.InvalidArg, result);
    }

    [Fact]
    public void StreamFeedPcm_InvalidChannels_ReturnsInvalidArg()
    {
        using var client = new VoiceCatClient("ext-pcm-test", "0.1", VcLogLevel.Off);
        var pcm = new short[960 * 3];
        // channels=3 is not supported — trampoline rejects before touching audio state.
        var result = client.StreamFeedPcm(0, pcm, 960, channels: 3);
        Assert.Equal(VcResult.InvalidArg, result);
    }

    [Fact]
    public void SetPcmSink_Zero_ReturnsOk()
    {
        using var client = new VoiceCatClient("ext-pcm-test", "0.1", VcLogLevel.Off);
        // IntPtr.Zero = disable — default state, must be a no-op that returns Ok.
        var result = client.SetPcmSink(IntPtr.Zero, IntPtr.Zero);
        Assert.Equal(VcResult.Ok, result);
    }

    [Fact]
    public void SetPcmSink_EnableThenDisable_BothSucceed()
    {
        using var client = new VoiceCatClient("ext-pcm-test", "0.1", VcLogLevel.Off);

        // Keep the delegate alive until after we unregister it (not just until the P/Invoke call).
        NativeMethods.VcPcmSinkCallback sink = static (_, _, _, _, _, _, _) => { };
        nint fp = Marshal.GetFunctionPointerForDelegate(sink);
        Assert.Equal(VcResult.Ok, client.SetPcmSink(fp, IntPtr.Zero));
        Assert.Equal(VcResult.Ok, client.SetPcmSink(IntPtr.Zero, IntPtr.Zero));
        GC.KeepAlive(sink);
    }
}
