using NATS.Server.TestUtilities;

namespace NATS.Server.JetStream.Tests;

internal static class JetStreamIntegrationMatrix
{
    public static async Task<(bool Success, string Details)> RunScenarioAsync(string scenario)
    {
        try
        {
            return scenario switch
            {
                "stream-msg-delete-roundtrip" => await StreamMsgDeleteRoundtripAsync(),
                "consumer-msg-next-no-wait" => await ConsumerNextNoWaitAsync(),
                "direct-get-by-sequence" => await DirectGetBySequenceAsync(),
                _ => (false, $"unknown scenario: {scenario}"),
            };
        }
        catch (Exception ex)
        {
            return (false, ex.Message);
        }
    }

    private static async Task<(bool Success, string Details)> StreamMsgDeleteRoundtripAsync()
    {
        await using var fx = await JetStreamApiFixture.StartWithStreamAsync("ORDERS", "orders.*");
        var ack = await fx.PublishAndGetAckAsync("orders.created", "1");

        var del = await fx.RequestLocalAsync("$JS.API.STREAM.MSG.DELETE.ORDERS", $"{{\"seq\":{ack.Seq}}}");
        if (!del.Success)
            return (false, "stream msg delete did not return success");

        var get = await fx.RequestLocalAsync("$JS.API.STREAM.MSG.GET.ORDERS", $"{{\"seq\":{ack.Seq}}}");
        return (get.Error != null, get.Error == null ? "deleted message was still retrievable" : string.Empty);
    }

    private static async Task<(bool Success, string Details)> ConsumerNextNoWaitAsync()
    {
        await using var fx = await JetStreamApiFixture.StartWithPullConsumerAsync();
        var batch = await fx.FetchWithNoWaitAsync("ORDERS", "PULL", 1);
        return (batch.Messages.Count == 0 && !batch.TimedOut, batch.Messages.Count == 0 ? "batch timed out unexpectedly" : "expected empty pull batch");
    }

    private static async Task<(bool Success, string Details)> DirectGetBySequenceAsync()
    {
        await using var fx = await JetStreamApiFixture.StartWithStreamAsync("ORDERS", "orders.*");
        var ack = await fx.PublishAndGetAckAsync("orders.created", "1");
        var direct = await fx.RequestLocalAsync("$JS.API.DIRECT.GET.ORDERS", $"{{\"seq\":{ack.Seq}}}");
        return (direct.DirectMessage?.Payload == "1", direct.DirectMessage == null ? "direct message payload missing" : "unexpected direct message payload");
    }
}
