using System.Collections.Concurrent;
using ZB.MOM.NatsNet.Server.Internal;

namespace ZB.MOM.NatsNet.Server;

public sealed partial class InMsg
{
    private static readonly ConcurrentBag<InMsg> Pool = new();

    internal static InMsg Rent() => Pool.TryTake(out var msg) ? msg : new InMsg();

    internal void ReturnToPool()
    {
        Subject = string.Empty;
        Reply = null;
        Hdr = null;
        Msg = null;
        Client = null;
        Seq = 0;
        Pool.Add(this);
    }
}

public sealed partial class CMsg
{
    private static readonly ConcurrentBag<CMsg> Pool = new();

    internal static CMsg Rent() => Pool.TryTake(out var msg) ? msg : new CMsg();

    internal void ReturnToPool()
    {
        Subject = string.Empty;
        Msg = null;
        Seq = 0;
        Pool.Add(this);
    }
}

public sealed partial class JsPubMsg
{
    private static readonly ConcurrentBag<JsPubMsg> Pool = new();

    internal static JsPubMsg Rent() => Pool.TryTake(out var msg) ? msg : new JsPubMsg();

    internal void ReturnToPool()
    {
        Subject = string.Empty;
        Reply = null;
        Hdr = null;
        Msg = null;
        Pa = null;
        Sync = null;
        Pool.Add(this);
    }

    internal int Size() =>
        (Subject?.Length ?? 0) +
        (Reply?.Length ?? 0) +
        (Hdr?.Length ?? 0) +
        (Msg?.Length ?? 0);
}

public sealed class JsOutQ
{
    private readonly IpQueue<JsPubMsg> _queue = new("js-outq");
    private bool _registered = true;

    public (int Len, Exception? Error) SendMsg(string reply, byte[] payload)
    {
        if (string.IsNullOrWhiteSpace(reply))
            return (0, new ArgumentException("reply is required", nameof(reply)));

        var msg = JsPubMsg.Rent();
        msg.Subject = reply;
        msg.Msg = payload;
        return Send(msg);
    }

    public (int Len, Exception? Error) Send(JsPubMsg msg)
    {
        if (!_registered)
            return (0, new InvalidOperationException("queue is unregistered"));

        return _queue.Push(msg);
    }

    public void Unregister()
    {
        _registered = false;
        _queue.Unregister();
    }

    internal JsPubMsg[]? Pop() => _queue.Pop();
}
