// Copyright 2020-2026 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/mqtt.go in the NATS server Go source.
namespace ZB.MOM.NatsNet.Server.Mqtt;
// ============================================================================
// Per-client MQTT state
// ============================================================================
///
/// Per-client MQTT state attached to every connection established via the MQTT
/// listener or WebSocket upgrade.
/// Mirrors Go mqtt struct in server/mqtt.go.
///
internal sealed class MqttHandler
{
private readonly Lock _mu = new();
// ------------------------------------------------------------------
// Identity
// ------------------------------------------------------------------
/// MQTT client identifier presented in the CONNECT packet.
public string ClientId { get; set; } = string.Empty;
/// Whether this is a clean session.
public bool CleanSession { get; set; }
// ------------------------------------------------------------------
// Session / Will
// ------------------------------------------------------------------
/// Session associated with this connection after a successful CONNECT.
public MqttSession? Session { get; set; }
///
/// Quick reference to the account session manager.
/// Immutable after processConnect() completes.
///
public MqttAccountSessionManager? AccountSessionManager { get; set; }
/// Will message to publish when this connection closes unexpectedly.
public MqttWill? Will { get; set; }
// ------------------------------------------------------------------
// Keep-alive
// ------------------------------------------------------------------
/// Keep-alive interval in seconds (0 = disabled).
public ushort KeepAlive { get; set; }
// ------------------------------------------------------------------
// QoS pending / packet identifiers
// ------------------------------------------------------------------
/// Next packet identifier to use for QoS >0 outbound messages.
public ushort NextPi { get; set; }
///
/// Pending ack map: packet identifier → pending state.
/// Used for tracking in-flight QoS 1/2 PUBLISH packets.
///
public Dictionary Pending { get; } = new();
// ------------------------------------------------------------------
// Protocol flags
// ------------------------------------------------------------------
///
/// When true, the server rejects QoS-2 PUBLISH from this client
/// and terminates the connection on receipt of such a packet.
/// Mirrors Go mqtt.rejectQoS2Pub.
///
public bool RejectQoS2Pub { get; set; }
///
/// When true, QoS-2 SUBSCRIBE requests are silently downgraded to QoS-1.
/// Mirrors Go mqtt.downgradeQoS2Sub.
///
public bool DowngradeQoS2Sub { get; set; }
// ------------------------------------------------------------------
// Parse state (used by the read-loop MQTT byte-stream parser)
// ------------------------------------------------------------------
/// Current state of the fixed-header / remaining-length state machine.
public byte ParseState { get; set; }
/// Control packet type byte extracted from the current fixed header.
public byte PktType { get; set; }
/// Remaining length of the current control packet (bytes still to read).
public int RemLen { get; set; }
/// Buffer accumulating the current packet's variable-header and payload.
public byte[]? Buf { get; set; }
/// Multiplier accumulator used during multi-byte remaining-length decoding.
public int RemLenMult { get; set; }
// ------------------------------------------------------------------
// Thread safety
// ------------------------------------------------------------------
/// Lock protecting mutable fields on this instance.
public Lock Mu => _mu;
}
// ============================================================================
// Server-side MQTT extension methods (stubs)
// ============================================================================
///
/// Stub extension methods on for MQTT server operations.
/// Mirrors the server-receiver MQTT functions in server/mqtt.go.
/// All methods throw until session 22 is complete.
///
internal static class MqttServerExtensions
{
///
/// Start listening for MQTT client connections.
/// Mirrors Go (*Server).startMQTT().
///
public static void StartMqtt(this NatsServer server) =>
throw new NotImplementedException("TODO: session 22");
///
/// Configure MQTT authentication overrides from the MQTT options block.
/// Mirrors Go (*Server).mqttConfigAuth().
///
public static void MqttConfigAuth(this NatsServer server, object mqttOpts) =>
throw new NotImplementedException("TODO: session 22");
///
/// Handle cleanup when an MQTT client connection closes.
/// Mirrors Go (*Server).mqttHandleClosedClient().
///
public static void MqttHandleClosedClient(this NatsServer server, object client) =>
throw new NotImplementedException("TODO: session 22");
///
/// Propagate a change to the maximum ack-pending limit to all MQTT sessions.
/// Mirrors Go (*Server).mqttUpdateMaxAckPending().
///
public static void MqttUpdateMaxAckPending(this NatsServer server, ushort maxp) =>
throw new NotImplementedException("TODO: session 22");
///
/// Retrieve or lazily-create the JSA for the named account.
/// Mirrors Go (*Server).mqttGetJSAForAccount().
///
public static MqttJsa MqttGetJsaForAccount(this NatsServer server, string account) =>
throw new NotImplementedException("TODO: session 22");
///
/// Store a QoS message for an account on a (possibly new) NATS subject.
/// Mirrors Go (*Server).mqttStoreQoSMsgForAccountOnNewSubject().
///
public static void MqttStoreQosMsgForAccountOnNewSubject(
this NatsServer server,
int hdr, byte[] msg, string account, string subject) =>
throw new NotImplementedException("TODO: session 22");
///
/// Get or create the for the client's account.
/// Mirrors Go (*Server).getOrCreateMQTTAccountSessionManager().
///
public static MqttAccountSessionManager GetOrCreateMqttAccountSessionManager(
this NatsServer server, object client) =>
throw new NotImplementedException("TODO: session 22");
///
/// Create a new for the given account.
/// Mirrors Go (*Server).mqttCreateAccountSessionManager().
///
public static MqttAccountSessionManager MqttCreateAccountSessionManager(
this NatsServer server, object account, System.Threading.CancellationToken cancel) =>
throw new NotImplementedException("TODO: session 22");
///
/// Determine how many JetStream replicas to use for MQTT streams.
/// Mirrors Go (*Server).mqttDetermineReplicas().
///
public static int MqttDetermineReplicas(this NatsServer server) =>
throw new NotImplementedException("TODO: session 22");
///
/// Process an MQTT CONNECT packet after parsing.
/// Mirrors Go (*Server).mqttProcessConnect().
///
public static void MqttProcessConnect(
this NatsServer server, object client, MqttConnectProto cp, bool trace) =>
throw new NotImplementedException("TODO: session 22");
///
/// Send the Will message for a client that disconnected unexpectedly.
/// Mirrors Go (*Server).mqttHandleWill().
///
public static void MqttHandleWill(this NatsServer server, object client) =>
throw new NotImplementedException("TODO: session 22");
///
/// Process an inbound MQTT PUBLISH packet.
/// Mirrors Go (*Server).mqttProcessPub().
///
public static void MqttProcessPub(
this NatsServer server, object client, MqttPublishInfo pp, bool trace) =>
throw new NotImplementedException("TODO: session 22");
///
/// Initiate delivery of a PUBLISH message via JetStream.
/// Mirrors Go (*Server).mqttInitiateMsgDelivery().
///
public static void MqttInitiateMsgDelivery(
this NatsServer server, object client, MqttPublishInfo pp) =>
throw new NotImplementedException("TODO: session 22");
///
/// Store a QoS-2 PUBLISH exactly once (idempotent).
/// Mirrors Go (*Server).mqttStoreQoS2MsgOnce().
///
public static void MqttStoreQoS2MsgOnce(
this NatsServer server, object client, MqttPublishInfo pp) =>
throw new NotImplementedException("TODO: session 22");
///
/// Process an inbound MQTT PUBREL packet.
/// Mirrors Go (*Server).mqttProcessPubRel().
///
public static void MqttProcessPubRel(
this NatsServer server, object client, ushort pi, bool trace) =>
throw new NotImplementedException("TODO: session 22");
///
/// Audit retained-message permissions after a configuration reload.
/// Mirrors Go (*Server).mqttCheckPubRetainedPerms().
///
public static void MqttCheckPubRetainedPerms(this NatsServer server) =>
throw new NotImplementedException("TODO: session 22");
}