// Copyright 2012-2026 The NATS Authors
// Licensed under the Apache License, Version 2.0

using Shouldly;
using ZB.MOM.NatsNet.Server;

namespace ZB.MOM.NatsNet.Server.Tests.JetStream;

public sealed class WaitQueueTests
{
    [Fact]
    public void Add_Peek_Pop_IsFull_ShouldBehaveAsFifo()
    {
        var q = new WaitQueue();

        q.Peek().ShouldBeNull();
        q.Pop().ShouldBeNull();

        q.Add(new WaitingRequest { Subject = "A", N = 1 });
        q.Add(new WaitingRequest { Subject = "B", N = 2 });

        q.Len.ShouldBe(2);
        q.IsFull(2).ShouldBeTrue();
        q.Peek()!.Subject.ShouldBe("A");

        q.Pop()!.Subject.ShouldBe("A");
        q.Pop()!.Subject.ShouldBe("B");
        q.Len.ShouldBe(1);

        q.Pop()!.Subject.ShouldBe("B");
        q.Len.ShouldBe(0);
        q.IsFull(1).ShouldBeFalse();
    }

    [Fact]
    public void AddPrioritized_AndCycle_ShouldPreserveStableOrder()
    {
        var q = new WaitQueue(max: 10);

        q.AddPrioritized(new WaitingRequest { Reply = "2a", N = 1, PriorityGroup = new PriorityGroup { Priority = 2 } })
            .ShouldBeTrue();
        q.AddPrioritized(new WaitingRequest { Reply = "1a", N = 1, PriorityGroup = new PriorityGroup { Priority = 1 } })
            .ShouldBeTrue();
        q.AddPrioritized(new WaitingRequest { Reply = "1b", N = 1, PriorityGroup = new PriorityGroup { Priority = 1 } })
            .ShouldBeTrue();

        q.Peek()!.Reply.ShouldBe("1a");
        q.Cycle();
        q.Peek()!.Reply.ShouldBe("1b");
    }

    [Fact]
    public void WaitingRequestRecycle_AndWaitQueueFactory_ShouldBehave()
    {
        var request = new WaitingRequest
        {
            Subject = "s",
            Reply = "r",
            N = 0,
            D = 1,
            MaxBytes = 10,
            B = 10,
            PriorityGroup = new PriorityGroup { Group = "g", Priority = 1 },
        };

        request.RecycleIfDone().ShouldBeTrue();
        request.Subject.ShouldBe(string.Empty);
        request.Reply.ShouldBeNull();

        var q = WaitQueue.NewWaitQueue(max: 3);
        q.ShouldNotBeNull();
        q.IsFull(3).ShouldBeFalse();
    }

    [Fact]
    public void WaitingDeliveryRecycle_ShouldClearState()
    {
        var wd = new WaitingDelivery { Reply = "r", Sequence = 42, Created = DateTime.UtcNow };

        wd.Recycle();

        wd.Reply.ShouldBe(string.Empty);
        wd.Sequence.ShouldBe(0UL);
        wd.Created.ShouldBe(DateTime.UnixEpoch);
    }
}
