// Initializes the 5 internal MQTT JetStream streams per account.
// Go reference: golang/nats-server/server/mqtt.go mqttCreateAccountSessionManager ~line 600
//   Stream creation for $MQTT_msgs, $MQTT_sess, $MQTT_rmsgs, $MQTT_qos2in, $MQTT_out.

using NATS.Server.JetStream;
using NATS.Server.JetStream.Models;

namespace NATS.Server.Mqtt;

/// <summary>
/// Lazily creates the 5 internal MQTT JetStream streams required for MQTT persistence.
/// Called on first MQTT connection per account.
/// Go reference: server/mqtt.go mqttCreateAccountSessionManager ~line 600.
/// </summary>
public sealed class MqttStreamInitializer
{
    private readonly StreamManager _streamManager;
    private volatile bool _initialized;
    private readonly Lock _initLock = new();

    public MqttStreamInitializer(StreamManager streamManager)
    {
        _streamManager = streamManager;
    }

    /// <summary>
    /// Whether the MQTT streams have been initialized.
    /// </summary>
    public bool IsInitialized => _initialized;

    /// <summary>
    /// Ensures the 5 internal MQTT streams exist. Idempotent — safe to call multiple times.
    /// Go reference: server/mqtt.go mqttCreateAccountSessionManager.
    /// </summary>
    public void EnsureStreams()
    {
        if (_initialized)
            return;

        lock (_initLock)
        {
            if (_initialized)
                return;

            CreateStream(MqttProtocolConstants.SessStreamName, [$"{MqttProtocolConstants.SessStreamSubjectPrefix}>"], maxMsgsPer: 1);
            CreateStream(MqttProtocolConstants.StreamName, [$"{MqttProtocolConstants.StreamSubjectPrefix}>"], retention: RetentionPolicy.Interest);
            CreateStream(MqttProtocolConstants.RetainedMsgsStreamName, [$"{MqttProtocolConstants.RetainedMsgsStreamSubject}>"], maxMsgsPer: 1);
            CreateStream(MqttProtocolConstants.QoS2IncomingMsgsStreamName, [$"{MqttProtocolConstants.QoS2IncomingMsgsStreamSubjectPrefix}>"], maxMsgsPer: 1);
            CreateStream(MqttProtocolConstants.OutStreamName, [$"{MqttProtocolConstants.OutSubjectPrefix}>"], retention: RetentionPolicy.Interest);

            _initialized = true;
        }
    }

    private void CreateStream(string name, List<string> subjects, RetentionPolicy retention = RetentionPolicy.Limits, int maxMsgsPer = 0)
    {
        if (_streamManager.Exists(name))
            return;

        _streamManager.CreateOrUpdate(new StreamConfig
        {
            Name = name,
            Subjects = subjects,
            Storage = StorageType.Memory,
            Retention = retention,
            MaxMsgsPer = maxMsgsPer,
            Replicas = 1,
        });
    }
}
