// 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/jetstream_api.go in the NATS server Go source.

using System.Text.Json.Serialization;

namespace ZB.MOM.NatsNet.Server;

// ---------------------------------------------------------------------------
// Forward stubs for types defined in later sessions
// ---------------------------------------------------------------------------

/// <summary>Stored message returned by direct-get and message-get APIs.</summary>
public sealed class StoredMsg
{
    [JsonPropertyName("subject")]
    public string Subject { get; set; } = string.Empty;

    [JsonPropertyName("seq")]
    public ulong Sequence { get; set; }

    [JsonPropertyName("hdrs")]
    public byte[]? Header { get; set; }

    [JsonPropertyName("data")]
    public byte[]? Data { get; set; }

    [JsonPropertyName("time")]
    public DateTime Time { get; set; }
}

/// <summary>
/// Priority group for pull consumers.
/// Mirrors <c>PriorityGroup</c> in server/consumer.go.
/// </summary>
public sealed class PriorityGroup
{
    [JsonPropertyName("group")]
    public string Group { get; set; } = string.Empty;

    [JsonPropertyName("min_pending")]
    public long MinPending { get; set; }

    [JsonPropertyName("min_ack_pending")]
    public long MinAckPending { get; set; }

    [JsonPropertyName("id")]
    public string Id { get; set; } = string.Empty;

    [JsonPropertyName("priority")]
    public int Priority { get; set; }
}

// ---------------------------------------------------------------------------
// API subject constants
// ---------------------------------------------------------------------------

/// <summary>
/// JetStream API subject constants.
/// Mirrors the const block at the top of server/jetstream_api.go.
/// </summary>
public static class JsApiSubjects
{
    public const string JsAllApi                      = "$JS.API.>";
    public const string JsApiPrefix                   = "$JS.API";
    public const string JsApiAccountInfo              = "$JS.API.INFO";

    public const string JsApiStreamCreate             = "$JS.API.STREAM.CREATE.*";
    public const string JsApiStreamCreateT            = "$JS.API.STREAM.CREATE.{0}";
    public const string JsApiStreamUpdate             = "$JS.API.STREAM.UPDATE.*";
    public const string JsApiStreamUpdateT            = "$JS.API.STREAM.UPDATE.{0}";
    public const string JsApiStreams                  = "$JS.API.STREAM.NAMES";
    public const string JsApiStreamList               = "$JS.API.STREAM.LIST";
    public const string JsApiStreamInfo               = "$JS.API.STREAM.INFO.*";
    public const string JsApiStreamInfoT              = "$JS.API.STREAM.INFO.{0}";
    public const string JsApiStreamDelete             = "$JS.API.STREAM.DELETE.*";
    public const string JsApiStreamDeleteT            = "$JS.API.STREAM.DELETE.{0}";
    public const string JsApiStreamPurge              = "$JS.API.STREAM.PURGE.*";
    public const string JsApiStreamPurgeT             = "$JS.API.STREAM.PURGE.{0}";
    public const string JsApiStreamSnapshot           = "$JS.API.STREAM.SNAPSHOT.*";
    public const string JsApiStreamSnapshotT          = "$JS.API.STREAM.SNAPSHOT.{0}";
    public const string JsApiStreamRestore            = "$JS.API.STREAM.RESTORE.*";
    public const string JsApiStreamRestoreT           = "$JS.API.STREAM.RESTORE.{0}";
    public const string JsApiMsgDelete                = "$JS.API.STREAM.MSG.DELETE.*";
    public const string JsApiMsgDeleteT               = "$JS.API.STREAM.MSG.DELETE.{0}";
    public const string JsApiMsgGet                   = "$JS.API.STREAM.MSG.GET.*";
    public const string JsApiMsgGetT                  = "$JS.API.STREAM.MSG.GET.{0}";
    public const string JsDirectMsgGet                = "$JS.API.DIRECT.GET.*";
    public const string JsDirectMsgGetT               = "$JS.API.DIRECT.GET.{0}";
    public const string JsDirectGetLastBySubject      = "$JS.API.DIRECT.GET.*.>";
    public const string JsDirectGetLastBySubjectT     = "$JS.API.DIRECT.GET.{0}.{1}";

    public const string JsApiConsumerCreate           = "$JS.API.CONSUMER.CREATE.*";
    public const string JsApiConsumerCreateT          = "$JS.API.CONSUMER.CREATE.{0}";
    public const string JsApiConsumerCreateEx         = "$JS.API.CONSUMER.CREATE.*.>";
    public const string JsApiConsumerCreateExT        = "$JS.API.CONSUMER.CREATE.{0}.{1}.{2}";
    public const string JsApiDurableCreate            = "$JS.API.CONSUMER.DURABLE.CREATE.*.*";
    public const string JsApiDurableCreateT           = "$JS.API.CONSUMER.DURABLE.CREATE.{0}.{1}";
    public const string JsApiConsumers                = "$JS.API.CONSUMER.NAMES.*";
    public const string JsApiConsumersT               = "$JS.API.CONSUMER.NAMES.{0}";
    public const string JsApiConsumerList             = "$JS.API.CONSUMER.LIST.*";
    public const string JsApiConsumerListT            = "$JS.API.CONSUMER.LIST.{0}";
    public const string JsApiConsumerInfo             = "$JS.API.CONSUMER.INFO.*.*";
    public const string JsApiConsumerInfoT            = "$JS.API.CONSUMER.INFO.{0}.{1}";
    public const string JsApiConsumerDelete           = "$JS.API.CONSUMER.DELETE.*.*";
    public const string JsApiConsumerDeleteT          = "$JS.API.CONSUMER.DELETE.{0}.{1}";
    public const string JsApiConsumerPause            = "$JS.API.CONSUMER.PAUSE.*.*";
    public const string JsApiConsumerPauseT           = "$JS.API.CONSUMER.PAUSE.{0}.{1}";
    public const string JsApiRequestNextT             = "$JS.API.CONSUMER.MSG.NEXT.{0}.{1}";
    public const string JsApiConsumerResetT           = "$JS.API.CONSUMER.RESET.{0}.{1}";
    public const string JsApiConsumerUnpin            = "$JS.API.CONSUMER.UNPIN.*.*";
    public const string JsApiConsumerUnpinT           = "$JS.API.CONSUMER.UNPIN.{0}.{1}";

    public const string JsApiStreamRemovePeer         = "$JS.API.STREAM.PEER.REMOVE.*";
    public const string JsApiStreamRemovePeerT        = "$JS.API.STREAM.PEER.REMOVE.{0}";
    public const string JsApiStreamLeaderStepDown     = "$JS.API.STREAM.LEADER.STEPDOWN.*";
    public const string JsApiStreamLeaderStepDownT    = "$JS.API.STREAM.LEADER.STEPDOWN.{0}";
    public const string JsApiConsumerLeaderStepDown   = "$JS.API.CONSUMER.LEADER.STEPDOWN.*.*";
    public const string JsApiConsumerLeaderStepDownT  = "$JS.API.CONSUMER.LEADER.STEPDOWN.{0}.{1}";
    public const string JsApiLeaderStepDown           = "$JS.API.META.LEADER.STEPDOWN";
    public const string JsApiRemoveServer             = "$JS.API.SERVER.REMOVE";
    public const string JsApiAccountPurge             = "$JS.API.ACCOUNT.PURGE.*";
    public const string JsApiAccountPurgeT            = "$JS.API.ACCOUNT.PURGE.{0}";
    public const string JsApiServerStreamMove         = "$JS.API.ACCOUNT.STREAM.MOVE.*.*";
    public const string JsApiServerStreamMoveT        = "$JS.API.ACCOUNT.STREAM.MOVE.{0}.{1}";
    public const string JsApiServerStreamCancelMove   = "$JS.API.ACCOUNT.STREAM.CANCEL_MOVE.*.*";
    public const string JsApiServerStreamCancelMoveT  = "$JS.API.ACCOUNT.STREAM.CANCEL_MOVE.{0}.{1}";

    // Advisory/metric subjects
    public const string JsAdvisoryPrefix                  = "$JS.EVENT.ADVISORY";
    public const string JsMetricPrefix                    = "$JS.EVENT.METRIC";
    public const string JsMetricConsumerAckPre            = "$JS.EVENT.METRIC.CONSUMER.ACK";
    public const string JsAdvisoryConsumerMaxDelivery     = "$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES";
    public const string JsAdvisoryConsumerMsgNak          = "$JS.EVENT.ADVISORY.CONSUMER.MSG_NAKED";
    public const string JsAdvisoryConsumerMsgTerminated   = "$JS.EVENT.ADVISORY.CONSUMER.MSG_TERMINATED";
    public const string JsAdvisoryStreamCreated           = "$JS.EVENT.ADVISORY.STREAM.CREATED";
    public const string JsAdvisoryStreamDeleted           = "$JS.EVENT.ADVISORY.STREAM.DELETED";
    public const string JsAdvisoryStreamUpdated           = "$JS.EVENT.ADVISORY.STREAM.UPDATED";
    public const string JsAdvisoryConsumerCreated         = "$JS.EVENT.ADVISORY.CONSUMER.CREATED";
    public const string JsAdvisoryConsumerDeleted         = "$JS.EVENT.ADVISORY.CONSUMER.DELETED";
    public const string JsAdvisoryConsumerPause           = "$JS.EVENT.ADVISORY.CONSUMER.PAUSE";
    public const string JsAdvisoryConsumerPinned          = "$JS.EVENT.ADVISORY.CONSUMER.PINNED";
    public const string JsAdvisoryConsumerUnpinned        = "$JS.EVENT.ADVISORY.CONSUMER.UNPINNED";
    public const string JsAdvisoryStreamSnapshotCreate    = "$JS.EVENT.ADVISORY.STREAM.SNAPSHOT_CREATE";
    public const string JsAdvisoryStreamSnapshotComplete  = "$JS.EVENT.ADVISORY.STREAM.SNAPSHOT_COMPLETE";
    public const string JsAdvisoryStreamRestoreCreate     = "$JS.EVENT.ADVISORY.STREAM.RESTORE_CREATE";
    public const string JsAdvisoryStreamRestoreComplete   = "$JS.EVENT.ADVISORY.STREAM.RESTORE_COMPLETE";
    public const string JsAdvisoryDomainLeaderElected     = "$JS.EVENT.ADVISORY.DOMAIN.LEADER_ELECTED";
    public const string JsAdvisoryStreamLeaderElected     = "$JS.EVENT.ADVISORY.STREAM.LEADER_ELECTED";
    public const string JsAdvisoryStreamQuorumLost        = "$JS.EVENT.ADVISORY.STREAM.QUORUM_LOST";
    public const string JsAdvisoryStreamBatchAbandoned    = "$JS.EVENT.ADVISORY.STREAM.BATCH_ABANDONED";
    public const string JsAdvisoryConsumerLeaderElected   = "$JS.EVENT.ADVISORY.CONSUMER.LEADER_ELECTED";
    public const string JsAdvisoryConsumerQuorumLost      = "$JS.EVENT.ADVISORY.CONSUMER.QUORUM_LOST";
    public const string JsAdvisoryServerOutOfStorage      = "$JS.EVENT.ADVISORY.SERVER.OUT_OF_STORAGE";
    public const string JsAdvisoryServerRemoved           = "$JS.EVENT.ADVISORY.SERVER.REMOVED";
    public const string JsAdvisoryApiLimitReached         = "$JS.EVENT.ADVISORY.API.LIMIT_REACHED";
    public const string JsAuditAdvisory                   = "$JS.EVENT.ADVISORY.API";

    // Response type strings
    public const string JsApiSystemResponseType           = "io.nats.jetstream.api.v1.system_response";
    public const string JsApiOverloadedType               = "io.nats.jetstream.api.v1.system_overloaded";
    public const string JsApiAccountInfoResponseType      = "io.nats.jetstream.api.v1.account_info_response";
    public const string JsApiStreamCreateResponseType     = "io.nats.jetstream.api.v1.stream_create_response";
    public const string JsApiStreamDeleteResponseType     = "io.nats.jetstream.api.v1.stream_delete_response";
    public const string JsApiStreamInfoResponseType       = "io.nats.jetstream.api.v1.stream_info_response";
    public const string JsApiStreamNamesResponseType      = "io.nats.jetstream.api.v1.stream_names_response";
    public const string JsApiStreamListResponseType       = "io.nats.jetstream.api.v1.stream_list_response";
    public const string JsApiStreamPurgeResponseType      = "io.nats.jetstream.api.v1.stream_purge_response";
    public const string JsApiStreamUpdateResponseType     = "io.nats.jetstream.api.v1.stream_update_response";
    public const string JsApiMsgDeleteResponseType        = "io.nats.jetstream.api.v1.stream_msg_delete_response";
    public const string JsApiStreamSnapshotResponseType   = "io.nats.jetstream.api.v1.stream_snapshot_response";
    public const string JsApiStreamRestoreResponseType    = "io.nats.jetstream.api.v1.stream_restore_response";
    public const string JsApiStreamRemovePeerResponseType = "io.nats.jetstream.api.v1.stream_remove_peer_response";
    public const string JsApiStreamLeaderStepDownResponseType   = "io.nats.jetstream.api.v1.stream_leader_stepdown_response";
    public const string JsApiConsumerLeaderStepDownResponseType = "io.nats.jetstream.api.v1.consumer_leader_stepdown_response";
    public const string JsApiLeaderStepDownResponseType   = "io.nats.jetstream.api.v1.meta_leader_stepdown_response";
    public const string JsApiMetaServerRemoveResponseType = "io.nats.jetstream.api.v1.meta_server_remove_response";
    public const string JsApiAccountPurgeResponseType     = "io.nats.jetstream.api.v1.account_purge_response";
    public const string JsApiMsgGetResponseType           = "io.nats.jetstream.api.v1.stream_msg_get_response";
    public const string JsApiConsumerCreateResponseType   = "io.nats.jetstream.api.v1.consumer_create_response";
    public const string JsApiConsumerDeleteResponseType   = "io.nats.jetstream.api.v1.consumer_delete_response";
    public const string JsApiConsumerPauseResponseType    = "io.nats.jetstream.api.v1.consumer_pause_response";
    public const string JsApiConsumerInfoResponseType     = "io.nats.jetstream.api.v1.consumer_info_response";
    public const string JsApiConsumerNamesResponseType    = "io.nats.jetstream.api.v1.consumer_names_response";
    public const string JsApiConsumerListResponseType     = "io.nats.jetstream.api.v1.consumer_list_response";
    public const string JsApiConsumerUnpinResponseType    = "io.nats.jetstream.api.v1.consumer_unpin_response";
    public const string JsApiConsumerResetResponseType    = "io.nats.jetstream.api.v1.consumer_reset_response";

    // Limits
    public const int JsApiNamesLimit      = 1024;
    public const int JsApiListLimit       = 256;
    public const int JsMaxSubjectDetails  = 100_000;
    public const int JsWaitQueueDefaultMax = 512;
    public const int JsMaxDescriptionLen  = 4 * 1024;
    public const int JsMaxMetadataLen     = 128 * 1024;
    public const int JsMaxNameLen         = 255;
    public const int JsDefaultRequestQueueLimit = 10_000;

    // Request headers
    public const string JsRequiredApiLevel = "Nats-Required-Api-Level";
}

// ---------------------------------------------------------------------------
// Base API types
// ---------------------------------------------------------------------------

/// <summary>
/// Standard base response from the JetStream JSON API.
/// Mirrors <c>ApiResponse</c> in server/jetstream_api.go.
/// </summary>
public sealed class ApiResponse
{
    [JsonPropertyName("type")]  public string?   Type  { get; set; }
    [JsonPropertyName("error")] public JsApiError? Error { get; set; }

    /// <summary>Returns the <see cref="Error"/> as an exception, or null if none.</summary>
    public Exception? ToError() =>
        Error is null ? null : new InvalidOperationException($"{Error.Description} ({Error.ErrCode})");
}

/// <summary>
/// Paged response metadata included in list responses.
/// Mirrors <c>ApiPaged</c> in server/jetstream_api.go.
/// </summary>
public sealed class ApiPaged
{
    [JsonPropertyName("total")]  public int Total  { get; set; }
    [JsonPropertyName("offset")] public int Offset { get; set; }
    [JsonPropertyName("limit")]  public int Limit  { get; set; }
}

/// <summary>
/// Request parameters for paged API responses.
/// Mirrors <c>ApiPagedRequest</c> in server/jetstream_api.go.
/// </summary>
public sealed class ApiPagedRequest
{
    [JsonPropertyName("offset")] public int Offset { get; set; }
}

// ---------------------------------------------------------------------------
// Account
// ---------------------------------------------------------------------------

/// <summary>Account info response.</summary>
public sealed class JsApiAccountInfoResponse
{
    [JsonPropertyName("type")]   public string?  Type  { get; set; }
    [JsonPropertyName("error")]  public JsApiError? Error { get; set; }

    // JetStreamAccountStats fields (embedded in Go)
    [JsonPropertyName("memory")]          public ulong Memory         { get; set; }
    [JsonPropertyName("storage")]         public ulong Store          { get; set; }
    [JsonPropertyName("reserved_memory")] public ulong ReservedMemory { get; set; }
    [JsonPropertyName("reserved_storage")]public ulong ReservedStore  { get; set; }
    [JsonPropertyName("streams")]         public int   Streams        { get; set; }
    [JsonPropertyName("consumers")]       public int   Consumers      { get; set; }
    [JsonPropertyName("limits")]          public JetStreamAccountLimits Limits { get; set; } = new();
    [JsonPropertyName("domain")]          public string? Domain       { get; set; }
    [JsonPropertyName("api")]             public JetStreamApiStats    Api    { get; set; } = new();
    [JsonPropertyName("tiers")]           public Dictionary<string, JetStreamTier>? Tiers { get; set; }
}

// ---------------------------------------------------------------------------
// Stream API types
// ---------------------------------------------------------------------------

/// <summary>Stream creation response.</summary>
public sealed class JsApiStreamCreateResponse
{
    [JsonPropertyName("type")]       public string?     Type      { get; set; }
    [JsonPropertyName("error")]      public JsApiError? Error     { get; set; }
    [JsonPropertyName("config")]     public StreamConfig? Config  { get; set; }
    [JsonPropertyName("state")]      public StreamState?  State   { get; set; }
    [JsonPropertyName("did_create")] public bool          DidCreate { get; set; }
}

/// <summary>Stream deletion response.</summary>
public sealed class JsApiStreamDeleteResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Stream info request with optional filtering.</summary>
public sealed class JsApiStreamInfoRequest
{
    [JsonPropertyName("offset")]          public int    Offset          { get; set; }
    [JsonPropertyName("deleted_details")] public bool   DeletedDetails  { get; set; }
    [JsonPropertyName("subjects_filter")] public string? SubjectsFilter { get; set; }
}

/// <summary>Stream info response.</summary>
public sealed class JsApiStreamInfoResponse
{
    [JsonPropertyName("type")]   public string?     Type   { get; set; }
    [JsonPropertyName("error")]  public JsApiError? Error  { get; set; }
    [JsonPropertyName("total")]  public int         Total  { get; set; }
    [JsonPropertyName("offset")] public int         Offset { get; set; }
    [JsonPropertyName("limit")]  public int         Limit  { get; set; }
    // StreamInfo fields embedded — delegated to StreamInfo stub for now
    public StreamInfo? Info { get; set; }
}

/// <summary>Stream names list request.</summary>
public sealed class JsApiStreamNamesRequest
{
    [JsonPropertyName("offset")]  public int     Offset  { get; set; }
    [JsonPropertyName("subject")] public string? Subject { get; set; }
}

/// <summary>Stream names list response.</summary>
public sealed class JsApiStreamNamesResponse
{
    [JsonPropertyName("type")]    public string?   Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error { get; set; }
    [JsonPropertyName("total")]   public int         Total  { get; set; }
    [JsonPropertyName("offset")]  public int         Offset { get; set; }
    [JsonPropertyName("limit")]   public int         Limit  { get; set; }
    [JsonPropertyName("streams")] public List<string>? Streams { get; set; }
}

/// <summary>Detailed stream list request.</summary>
public sealed class JsApiStreamListRequest
{
    [JsonPropertyName("offset")]  public int     Offset  { get; set; }
    [JsonPropertyName("subject")] public string? Subject { get; set; }
}

/// <summary>Detailed stream list response.</summary>
public sealed class JsApiStreamListResponse
{
    [JsonPropertyName("type")]    public string?               Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError?           Error   { get; set; }
    [JsonPropertyName("total")]   public int                   Total   { get; set; }
    [JsonPropertyName("offset")]  public int                   Offset  { get; set; }
    [JsonPropertyName("limit")]   public int                   Limit   { get; set; }
    [JsonPropertyName("streams")] public List<StreamInfo>?     Streams { get; set; }
    [JsonPropertyName("missing")] public List<string>?         Missing { get; set; }
    [JsonPropertyName("offline")] public Dictionary<string, string>? Offline { get; set; }
}

/// <summary>Stream purge request.</summary>
public sealed class JsApiStreamPurgeRequest
{
    [JsonPropertyName("seq")]    public ulong   Sequence { get; set; }
    [JsonPropertyName("filter")] public string? Subject  { get; set; }
    [JsonPropertyName("keep")]   public ulong   Keep     { get; set; }
}

/// <summary>Stream purge response.</summary>
public sealed class JsApiStreamPurgeResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
    [JsonPropertyName("purged")]  public ulong       Purged  { get; set; }
}

/// <summary>Stream update response.</summary>
public sealed class JsApiStreamUpdateResponse
{
    [JsonPropertyName("type")]  public string?     Type  { get; set; }
    [JsonPropertyName("error")] public JsApiError? Error { get; set; }
    public StreamInfo? Info { get; set; }
}

/// <summary>Message deletion request.</summary>
public sealed class JsApiMsgDeleteRequest
{
    [JsonPropertyName("seq")]      public ulong Seq     { get; set; }
    [JsonPropertyName("no_erase")] public bool  NoErase { get; set; }
}

/// <summary>Message deletion response.</summary>
public sealed class JsApiMsgDeleteResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Stream snapshot request.</summary>
public sealed class JsApiStreamSnapshotRequest
{
    [JsonPropertyName("deliver_subject")] public string DeliverSubject { get; set; } = string.Empty;
    [JsonPropertyName("no_consumers")]    public bool   NoConsumers    { get; set; }
    [JsonPropertyName("chunk_size")]      public int    ChunkSize      { get; set; }
    [JsonPropertyName("window_size")]     public int    WindowSize     { get; set; }
    [JsonPropertyName("jsck")]            public bool   CheckMsgs      { get; set; }
}

/// <summary>Direct stream snapshot response.</summary>
public sealed class JsApiStreamSnapshotResponse
{
    [JsonPropertyName("type")]   public string?      Type   { get; set; }
    [JsonPropertyName("error")]  public JsApiError?  Error  { get; set; }
    [JsonPropertyName("config")] public StreamConfig? Config { get; set; }
    [JsonPropertyName("state")]  public StreamState?  State  { get; set; }
}

/// <summary>Stream restore request.</summary>
public sealed class JsApiStreamRestoreRequest
{
    [JsonPropertyName("config")] public StreamConfig Config { get; set; } = new();
    [JsonPropertyName("state")]  public StreamState  State  { get; set; } = new();
}

/// <summary>Stream restore response.</summary>
public sealed class JsApiStreamRestoreResponse
{
    [JsonPropertyName("type")]            public string?    Type           { get; set; }
    [JsonPropertyName("error")]           public JsApiError? Error         { get; set; }
    [JsonPropertyName("deliver_subject")] public string     DeliverSubject { get; set; } = string.Empty;
}

/// <summary>Remove a peer from a stream.</summary>
public sealed class JsApiStreamRemovePeerRequest
{
    [JsonPropertyName("peer")] public string Peer { get; set; } = string.Empty;
}

/// <summary>Response to remove peer request.</summary>
public sealed class JsApiStreamRemovePeerResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Response to stream leader step-down.</summary>
public sealed class JsApiStreamLeaderStepDownResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Response to consumer leader step-down.</summary>
public sealed class JsApiConsumerLeaderStepDownResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Meta-leader step-down request with optional placement.</summary>
public sealed class JsApiLeaderStepdownRequest
{
    [JsonPropertyName("placement")] public Placement? Placement { get; set; }
}

/// <summary>Response to meta-leader step-down.</summary>
public sealed class JsApiLeaderStepDownResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Remove a peer server from the meta group.</summary>
public sealed class JsApiMetaServerRemoveRequest
{
    [JsonPropertyName("peer")]    public string  Server  { get; set; } = string.Empty;
    [JsonPropertyName("peer_id")] public string? PeerId  { get; set; }
}

/// <summary>Response to peer removal.</summary>
public sealed class JsApiMetaServerRemoveResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Request to move a stream off a server.</summary>
public sealed class JsApiMetaServerStreamMoveRequest
{
    [JsonPropertyName("server")]  public string?   Server  { get; set; }
    [JsonPropertyName("cluster")] public string?   Cluster { get; set; }
    [JsonPropertyName("domain")]  public string?   Domain  { get; set; }
    [JsonPropertyName("tags")]    public string[]? Tags    { get; set; }
}

/// <summary>Account purge response.</summary>
public sealed class JsApiAccountPurgeResponse
{
    [JsonPropertyName("type")]      public string?    Type      { get; set; }
    [JsonPropertyName("error")]     public JsApiError? Error    { get; set; }
    [JsonPropertyName("initiated")] public bool        Initiated { get; set; }
}

// ---------------------------------------------------------------------------
// Message get
// ---------------------------------------------------------------------------

/// <summary>
/// Direct message get request (by sequence, last-for-subject, next-for-subject, or batch).
/// Mirrors <c>JSApiMsgGetRequest</c> in server/jetstream_api.go.
/// </summary>
public sealed class JsApiMsgGetRequest
{
    [JsonPropertyName("seq")]           public ulong     Seq          { get; set; }
    [JsonPropertyName("last_by_subj")]  public string?   LastFor      { get; set; }
    [JsonPropertyName("next_by_subj")]  public string?   NextFor      { get; set; }
    [JsonPropertyName("batch")]         public int       Batch        { get; set; }
    [JsonPropertyName("max_bytes")]     public int       MaxBytes     { get; set; }
    [JsonPropertyName("start_time")]    public DateTime? StartTime    { get; set; }
    [JsonPropertyName("multi_last")]    public string[]? MultiLastFor { get; set; }
    [JsonPropertyName("up_to_seq")]     public ulong     UpToSeq      { get; set; }
    [JsonPropertyName("up_to_time")]    public DateTime? UpToTime     { get; set; }
    [JsonPropertyName("no_hdr")]        public bool      NoHeaders    { get; set; }
}

/// <summary>Message get response.</summary>
public sealed class JsApiMsgGetResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("message")] public StoredMsg?  Message { get; set; }
}

// ---------------------------------------------------------------------------
// Consumer API types
// ---------------------------------------------------------------------------

/// <summary>Consumer create/update response.</summary>
public sealed class JsApiConsumerCreateResponse
{
    [JsonPropertyName("type")]  public string?      Type  { get; set; }
    [JsonPropertyName("error")] public JsApiError?  Error { get; set; }
    public ConsumerInfo? Info { get; set; }
}

/// <summary>Consumer delete response.</summary>
public sealed class JsApiConsumerDeleteResponse
{
    [JsonPropertyName("type")]    public string?    Type    { get; set; }
    [JsonPropertyName("error")]   public JsApiError? Error  { get; set; }
    [JsonPropertyName("success")] public bool        Success { get; set; }
}

/// <summary>Consumer pause request.</summary>
public sealed class JsApiConsumerPauseRequest
{
    [JsonPropertyName("pause_until")] public DateTime PauseUntil { get; set; }
}

/// <summary>Consumer pause response.</summary>
public sealed class JsApiConsumerPauseResponse
{
    [JsonPropertyName("type")]            public string?    Type           { get; set; }
    [JsonPropertyName("error")]           public JsApiError? Error         { get; set; }
    [JsonPropertyName("paused")]          public bool        Paused         { get; set; }
    [JsonPropertyName("pause_until")]     public DateTime    PauseUntil     { get; set; }
    [JsonPropertyName("pause_remaining")] public TimeSpan    PauseRemaining { get; set; }
}

/// <summary>Consumer info response.</summary>
public sealed class JsApiConsumerInfoResponse
{
    [JsonPropertyName("type")]  public string?     Type  { get; set; }
    [JsonPropertyName("error")] public JsApiError? Error { get; set; }
    public ConsumerInfo? Info { get; set; }
}

/// <summary>Consumer names request (paged).</summary>
public sealed class JsApiConsumersRequest
{
    [JsonPropertyName("offset")] public int Offset { get; set; }
}

/// <summary>Consumer names list response.</summary>
public sealed class JsApiConsumerNamesResponse
{
    [JsonPropertyName("type")]      public string?     Type      { get; set; }
    [JsonPropertyName("error")]     public JsApiError? Error     { get; set; }
    [JsonPropertyName("total")]     public int         Total     { get; set; }
    [JsonPropertyName("offset")]    public int         Offset    { get; set; }
    [JsonPropertyName("limit")]     public int         Limit     { get; set; }
    [JsonPropertyName("consumers")] public List<string>? Consumers { get; set; }
}

/// <summary>Consumer list response.</summary>
public sealed class JsApiConsumerListResponse
{
    [JsonPropertyName("type")]      public string?                       Type      { get; set; }
    [JsonPropertyName("error")]     public JsApiError?                   Error     { get; set; }
    [JsonPropertyName("total")]     public int                           Total     { get; set; }
    [JsonPropertyName("offset")]    public int                           Offset    { get; set; }
    [JsonPropertyName("limit")]     public int                           Limit     { get; set; }
    [JsonPropertyName("consumers")] public List<ConsumerInfo>?           Consumers { get; set; }
    [JsonPropertyName("missing")]   public List<string>?                 Missing   { get; set; }
    [JsonPropertyName("offline")]   public Dictionary<string, string>?   Offline   { get; set; }
}

/// <summary>
/// Pull consumer next-message request.
/// Mirrors <c>JSApiConsumerGetNextRequest</c> in server/jetstream_api.go.
/// </summary>
public sealed class JsApiConsumerGetNextRequest
{
    [JsonPropertyName("expires")]        public TimeSpan Expires   { get; set; }
    [JsonPropertyName("batch")]          public int      Batch     { get; set; }
    [JsonPropertyName("max_bytes")]      public int      MaxBytes  { get; set; }
    [JsonPropertyName("no_wait")]        public bool     NoWait    { get; set; }
    [JsonPropertyName("idle_heartbeat")] public TimeSpan Heartbeat { get; set; }
    public PriorityGroup? Priority { get; set; }
}

/// <summary>Consumer reset (seek to sequence) request.</summary>
public sealed class JsApiConsumerResetRequest
{
    [JsonPropertyName("seq")] public ulong Seq { get; set; }
}

/// <summary>Consumer reset response.</summary>
public sealed class JsApiConsumerResetResponse
{
    [JsonPropertyName("type")]      public string?      Type     { get; set; }
    [JsonPropertyName("error")]     public JsApiError?  Error    { get; set; }
    [JsonPropertyName("reset_seq")] public ulong        ResetSeq { get; set; }
    public ConsumerInfo? Info { get; set; }
}

/// <summary>Consumer unpin (priority group) request.</summary>
public sealed class JsApiConsumerUnpinRequest
{
    [JsonPropertyName("group")] public string Group { get; set; } = string.Empty;
}

/// <summary>Consumer unpin response.</summary>
public sealed class JsApiConsumerUnpinResponse
{
    [JsonPropertyName("type")]  public string?    Type  { get; set; }
    [JsonPropertyName("error")] public JsApiError? Error { get; set; }
}
