// Copyright 2012-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Adapted from server/parser.go in the NATS server Go source.

using ZB.MOM.NatsNet.Server.Internal;

namespace ZB.MOM.NatsNet.Server.Protocol;

/// <summary>
/// Parser state machine states.
/// Mirrors the Go <c>parserState</c> const block in parser.go (79 states).
/// </summary>
public enum ParserState
{
    OpStart = 0,
    OpPlus,
    OpPlusO,
    OpPlusOk,
    OpMinus,
    OpMinusE,
    OpMinusEr,
    OpMinusErr,
    OpMinusErrSpc,
    MinusErrArg,
    OpC,
    OpCo,
    OpCon,
    OpConn,
    OpConne,
    OpConnec,
    OpConnect,
    ConnectArg,
    OpH,
    OpHp,
    OpHpu,
    OpHpub,
    OpHpubSpc,
    HpubArg,
    OpHm,
    OpHms,
    OpHmsg,
    OpHmsgSpc,
    HmsgArg,
    OpP,
    OpPu,
    OpPub,
    OpPubSpc,
    PubArg,
    OpPi,
    OpPin,
    OpPing,
    OpPo,
    OpPon,
    OpPong,
    MsgPayload,
    MsgEndR,
    MsgEndN,
    OpS,
    OpSu,
    OpSub,
    OpSubSpc,
    SubArg,
    OpA,
    OpAsub,
    OpAsubSpc,
    AsubArg,
    OpAusub,
    OpAusubSpc,
    AusubArg,
    OpL,
    OpLs,
    OpR,
    OpRs,
    OpU,
    OpUn,
    OpUns,
    OpUnsu,
    OpUnsub,
    OpUnsubSpc,
    UnsubArg,
    OpM,
    OpMs,
    OpMsg,
    OpMsgSpc,
    MsgArg,
    OpI,
    OpIn,
    OpInf,
    OpInfo,
    InfoArg,
}

/// <summary>
/// Parsed publish/message arguments.
/// Mirrors Go <c>pubArg</c> struct in parser.go.
/// </summary>
public sealed class PublishArgument
{
    public byte[]? Arg { get; set; }
    public byte[]? PaCache { get; set; }
    public byte[]? Origin { get; set; }
    public byte[]? Account { get; set; }
    public byte[]? Subject { get; set; }
    public byte[]? Deliver { get; set; }
    public byte[]? Mapped { get; set; }
    public byte[]? Reply { get; set; }
    public byte[]? SizeBytes { get; set; }
    public byte[]? HeaderBytes { get; set; }
    public List<byte[]>? Queues { get; set; }
    public int Size { get; set; }
    public int HeaderSize { get; set; } = -1;
    public bool Delivered { get; set; }

    /// <summary>
    /// Active message-trace state for the current message, if tracing is enabled.
    /// Mirrors Go <c>pubArg.trace *msgTrace</c> in server/msgtrace.go.
    /// </summary>
    public object? Trace { get; set; }

    /// <summary>Resets all fields to their defaults.</summary>
    public void Reset()
    {
        Arg = null;
        PaCache = null;
        Origin = null;
        Account = null;
        Subject = null;
        Deliver = null;
        Mapped = null;
        Reply = null;
        SizeBytes = null;
        HeaderBytes = null;
        Queues = null;
        Size = 0;
        HeaderSize = -1;
        Delivered = false;
        Trace = null;
    }
}

/// <summary>
/// Holds the parser state for a single connection.
/// Mirrors Go <c>parseState</c> struct embedded in <c>client</c>.
/// </summary>
public sealed class ParseContext
{
    // ---- Parser state ----

    public ParserState State { get; set; }
    public byte Op { get; set; }
    public int ArgStart { get; set; }
    public int Drop { get; set; }
    public PublishArgument Pa { get; } = new();
    public byte[]? ArgBuf { get; set; }
    public byte[]? MsgBuf { get; set; }

    // ---- Connection-level properties (set once at creation) ----

    public ClientKind Kind { get; set; }
    public int MaxControlLine { get; set; } = ServerConstants.MaxControlLineSize;
    public int MaxPayload { get; set; } = -1;
    public bool HasHeaders { get; set; }

    // ---- Internal scratch buffer ----

    internal byte[] Scratch { get; } = new byte[ServerConstants.MaxControlLineSize];

    /// <summary>
    /// Convenience accessor for the message-trace state on the current publish arg.
    /// Mirrors Go <c>c.pa.trace</c>.
    /// </summary>
    internal ZB.MOM.NatsNet.Server.MsgTraceState? Trace
    {
        get => Pa.Trace as ZB.MOM.NatsNet.Server.MsgTraceState;
        set => Pa.Trace = value;
    }
}
