// Copyright 2019-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.

package server

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"math/rand"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"slices"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/nats-io/nats-server/v2/server/avl"
	"github.com/nats-io/nats-server/v2/server/gsl"
	"github.com/nats-io/nuid"
	"golang.org/x/time/rate"
)

// Headers sent with Request Timeout
const (
	JSPullRequestPendingMsgs  = "Nats-Pending-Messages"
	JSPullRequestPendingBytes = "Nats-Pending-Bytes"
	JSPullRequestNatsPinId    = "Nats-Pin-Id"
)

var (
	validGroupName = regexp.MustCompile(`^[a-zA-Z0-9/_=-]{1,16}$`)
)

// Headers sent when batch size was completed, but there were remaining bytes.
const JsPullRequestRemainingBytesT = "NATS/1.0 409 Batch Completed\r\n%s: %d\r\n%s: %d\r\n\r\n"

type ConsumerInfo struct {
	Stream         string          `json:"stream_name"`
	Name           string          `json:"name"`
	Created        time.Time       `json:"created"`
	Config         *ConsumerConfig `json:"config,omitempty"`
	Delivered      SequenceInfo    `json:"delivered"`
	AckFloor       SequenceInfo    `json:"ack_floor"`
	NumAckPending  int             `json:"num_ack_pending"`
	NumRedelivered int             `json:"num_redelivered"`
	NumWaiting     int             `json:"num_waiting"`
	NumPending     uint64          `json:"num_pending"`
	Cluster        *ClusterInfo    `json:"cluster,omitempty"`
	PushBound      bool            `json:"push_bound,omitempty"`
	Paused         bool            `json:"paused,omitempty"`
	PauseRemaining time.Duration   `json:"pause_remaining,omitempty"`
	// TimeStamp indicates when the info was gathered
	TimeStamp      time.Time            `json:"ts"`
	PriorityGroups []PriorityGroupState `json:"priority_groups,omitempty"`
}

// consumerInfoClusterResponse is a response used in a cluster to communicate the consumer info
// back to the meta leader as part of a consumer list request.
type consumerInfoClusterResponse struct {
	ConsumerInfo
	OfflineReason string `json:"offline_reason,omitempty"` // Reporting when a consumer is offline.
}

type PriorityGroupState struct {
	Group          string    `json:"group"`
	PinnedClientID string    `json:"pinned_client_id,omitempty"`
	PinnedTS       time.Time `json:"pinned_ts,omitempty"`
}

type ConsumerConfig struct {
	Durable         string          `json:"durable_name,omitempty"`
	Name            string          `json:"name,omitempty"`
	Description     string          `json:"description,omitempty"`
	DeliverPolicy   DeliverPolicy   `json:"deliver_policy"`
	OptStartSeq     uint64          `json:"opt_start_seq,omitempty"`
	OptStartTime    *time.Time      `json:"opt_start_time,omitempty"`
	AckPolicy       AckPolicy       `json:"ack_policy"`
	AckWait         time.Duration   `json:"ack_wait,omitempty"`
	MaxDeliver      int             `json:"max_deliver,omitempty"`
	BackOff         []time.Duration `json:"backoff,omitempty"`
	FilterSubject   string          `json:"filter_subject,omitempty"`
	FilterSubjects  []string        `json:"filter_subjects,omitempty"`
	ReplayPolicy    ReplayPolicy    `json:"replay_policy"`
	RateLimit       uint64          `json:"rate_limit_bps,omitempty"` // Bits per sec
	SampleFrequency string          `json:"sample_freq,omitempty"`
	MaxWaiting      int             `json:"max_waiting,omitempty"`
	MaxAckPending   int             `json:"max_ack_pending,omitempty"`
	FlowControl     bool            `json:"flow_control,omitempty"`
	HeadersOnly     bool            `json:"headers_only,omitempty"`

	// Pull based options.
	MaxRequestBatch    int           `json:"max_batch,omitempty"`
	MaxRequestExpires  time.Duration `json:"max_expires,omitempty"`
	MaxRequestMaxBytes int           `json:"max_bytes,omitempty"`

	// Push based consumers.
	DeliverSubject string        `json:"deliver_subject,omitempty"`
	DeliverGroup   string        `json:"deliver_group,omitempty"`
	Heartbeat      time.Duration `json:"idle_heartbeat,omitempty"`

	// Ephemeral inactivity threshold.
	InactiveThreshold time.Duration `json:"inactive_threshold,omitempty"`

	// Generally inherited by parent stream and other markers, now can be configured directly.
	Replicas int `json:"num_replicas"`
	// Force memory storage.
	MemoryStorage bool `json:"mem_storage,omitempty"`

	// Don't add to general clients.
	Direct   bool `json:"direct,omitempty"`
	Sourcing bool `json:"sourcing,omitempty"`

	// Metadata is additional metadata for the Consumer.
	Metadata map[string]string `json:"metadata,omitempty"`

	// PauseUntil is for suspending the consumer until the deadline.
	PauseUntil *time.Time `json:"pause_until,omitempty"`

	// Priority groups
	PriorityGroups []string       `json:"priority_groups,omitempty"`
	PriorityPolicy PriorityPolicy `json:"priority_policy,omitempty"`
	PinnedTTL      time.Duration  `json:"priority_timeout,omitempty"`
}

// SequenceInfo has both the consumer and the stream sequence and last activity.
type SequenceInfo struct {
	Consumer uint64     `json:"consumer_seq"`
	Stream   uint64     `json:"stream_seq"`
	Last     *time.Time `json:"last_active,omitempty"`
}

type CreateConsumerRequest struct {
	Stream   string         `json:"stream_name"`
	Config   ConsumerConfig `json:"config"`
	Action   ConsumerAction `json:"action"`
	Pedantic bool           `json:"pedantic,omitempty"`
}

type ConsumerAction int

const (
	ActionCreateOrUpdate ConsumerAction = iota
	ActionUpdate
	ActionCreate
)

const (
	actionUpdateJSONString         = `"update"`
	actionCreateJSONString         = `"create"`
	actionCreateOrUpdateJSONString = `""`
)

var (
	actionUpdateJSONBytes         = []byte(actionUpdateJSONString)
	actionCreateJSONBytes         = []byte(actionCreateJSONString)
	actionCreateOrUpdateJSONBytes = []byte(actionCreateOrUpdateJSONString)
)

func (a ConsumerAction) String() string {
	switch a {
	case ActionCreateOrUpdate:
		return actionCreateOrUpdateJSONString
	case ActionCreate:
		return actionCreateJSONString
	case ActionUpdate:
		return actionUpdateJSONString
	}
	return actionCreateOrUpdateJSONString
}

func (a ConsumerAction) MarshalJSON() ([]byte, error) {
	switch a {
	case ActionCreate:
		return actionCreateJSONBytes, nil
	case ActionUpdate:
		return actionUpdateJSONBytes, nil
	case ActionCreateOrUpdate:
		return actionCreateOrUpdateJSONBytes, nil
	default:
		return nil, fmt.Errorf("can not marshal %v", a)
	}
}

func (a *ConsumerAction) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case actionCreateJSONString:
		*a = ActionCreate
	case actionUpdateJSONString:
		*a = ActionUpdate
	case actionCreateOrUpdateJSONString:
		*a = ActionCreateOrUpdate
	default:
		return fmt.Errorf("unknown consumer action: %v", string(data))
	}
	return nil
}

// ConsumerNakOptions is for optional NAK values, e.g. delay.
type ConsumerNakOptions struct {
	Delay time.Duration `json:"delay"`
}

// PriorityPolicy determines policy for selecting messages based on priority.
type PriorityPolicy int

const (
	// No priority policy.
	PriorityNone PriorityPolicy = iota
	// Clients will get the messages only if certain criteria are specified.
	PriorityOverflow
	// Single client takes over handling of the messages, while others are on standby.
	PriorityPinnedClient
	// Clients with lowest priority will be selected first.
	PriorityPrioritized
)

const (
	PriorityNoneJSONString         = `"none"`
	PriorityOverflowJSONString     = `"overflow"`
	PriorityPinnedClientJSONString = `"pinned_client"`
	PriorityPrioritizedJSONString  = `"prioritized"`
)

var (
	PriorityNoneJSONBytes         = []byte(PriorityNoneJSONString)
	PriorityOverflowJSONBytes     = []byte(PriorityOverflowJSONString)
	PriorityPinnedClientJSONBytes = []byte(PriorityPinnedClientJSONString)
	PriorityPrioritizedJSONBytes  = []byte(PriorityPrioritizedJSONString)
)

func (pp PriorityPolicy) String() string {
	switch pp {
	case PriorityOverflow:
		return PriorityOverflowJSONString
	case PriorityPinnedClient:
		return PriorityPinnedClientJSONString
	case PriorityPrioritized:
		return PriorityPrioritizedJSONString
	default:
		return PriorityNoneJSONString
	}
}

func (pp PriorityPolicy) MarshalJSON() ([]byte, error) {
	switch pp {
	case PriorityOverflow:
		return PriorityOverflowJSONBytes, nil
	case PriorityPinnedClient:
		return PriorityPinnedClientJSONBytes, nil
	case PriorityPrioritized:
		return PriorityPrioritizedJSONBytes, nil
	case PriorityNone:
		return PriorityNoneJSONBytes, nil
	default:
		return nil, fmt.Errorf("unknown priority policy: %v", pp)
	}
}

func (pp *PriorityPolicy) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case PriorityOverflowJSONString:
		*pp = PriorityOverflow
	case PriorityPinnedClientJSONString:
		*pp = PriorityPinnedClient
	case PriorityPrioritizedJSONString:
		*pp = PriorityPrioritized
	case PriorityNoneJSONString:
		*pp = PriorityNone
	default:
		return fmt.Errorf("unknown priority policy: %v", string(data))
	}
	return nil
}

// DeliverPolicy determines how the consumer should select the first message to deliver.
type DeliverPolicy int

const (
	// DeliverAll will be the default so can be omitted from the request.
	DeliverAll DeliverPolicy = iota
	// DeliverLast will start the consumer with the last sequence received.
	DeliverLast
	// DeliverNew will only deliver new messages that are sent after the consumer is created.
	DeliverNew
	// DeliverByStartSequence will look for a defined starting sequence to start.
	DeliverByStartSequence
	// DeliverByStartTime will select the first messsage with a timestamp >= to StartTime.
	DeliverByStartTime
	// DeliverLastPerSubject will start the consumer with the last message for all subjects received.
	DeliverLastPerSubject
)

func (dp DeliverPolicy) String() string {
	switch dp {
	case DeliverAll:
		return "all"
	case DeliverLast:
		return "last"
	case DeliverNew:
		return "new"
	case DeliverByStartSequence:
		return "by_start_sequence"
	case DeliverByStartTime:
		return "by_start_time"
	case DeliverLastPerSubject:
		return "last_per_subject"
	default:
		return "undefined"
	}
}

// AckPolicy determines how the consumer should acknowledge delivered messages.
type AckPolicy int

const (
	// AckNone requires no acks for delivered messages.
	AckNone AckPolicy = iota
	// AckAll when acking a sequence number, this implicitly acks all sequences below this one as well.
	AckAll
	// AckExplicit requires ack or nack for all messages.
	AckExplicit
	// AckFlowControl functions like AckAll, but acks based on responses to flow control.
	AckFlowControl
)

func (a AckPolicy) String() string {
	switch a {
	case AckNone:
		return "none"
	case AckAll:
		return "all"
	case AckFlowControl:
		return "flow_control"
	default:
		return "explicit"
	}
}

// ReplayPolicy determines how the consumer should replay messages it already has queued in the stream.
type ReplayPolicy int

const (
	// ReplayInstant will replay messages as fast as possible.
	ReplayInstant ReplayPolicy = iota
	// ReplayOriginal will maintain the same timing as the messages were received.
	ReplayOriginal
)

func (r ReplayPolicy) String() string {
	switch r {
	case ReplayInstant:
		return replayInstantPolicyJSONString
	default:
		return replayOriginalPolicyJSONString
	}
}

// OK
const OK = "+OK"

// Ack responses. Note that a nil or no payload is same as AckAck
var (
	// Ack
	AckAck = []byte("+ACK") // nil or no payload to ack subject also means ACK
	AckOK  = []byte(OK)     // deprecated but +OK meant ack as well.

	// Nack
	AckNak = []byte("-NAK")
	// Progress indicator
	AckProgress = []byte("+WPI")
	// Ack + Deliver the next message(s).
	AckNext = []byte("+NXT")
	// Terminate delivery of the message.
	AckTerm = []byte("+TERM")
)

const (
	// reasons to supply when terminating messages using limits
	ackTermLimitsReason        = "Message deleted by stream limits"
	ackTermUnackedLimitsReason = "Unacknowledged message was deleted"
)

// Calculate accurate replicas for the consumer config with the parent stream config.
func (consCfg ConsumerConfig) replicas(strCfg *StreamConfig) int {
	if consCfg.Replicas == 0 || consCfg.Replicas > strCfg.Replicas {
		if !isDurableConsumer(&consCfg) && strCfg.Retention == LimitsPolicy && consCfg.Replicas == 0 {
			// Matches old-school ephemerals only, where the replica count is 0.
			return 1
		}
		return strCfg.Replicas
	}
	return consCfg.Replicas
}

// Consumer is a jetstream consumer.
type consumer struct {
	// Atomic used to notify that we want to process an ack.
	// This will be checked in checkPending to abort processing
	// and let ack be processed in priority.
	awl               int64
	leader            atomic.Bool
	mu                sync.RWMutex
	js                *jetStream
	mset              *stream
	acc               *Account
	srv               *Server
	client            *client
	sysc              *client
	sid               int
	name              string
	stream            string
	sseq              uint64             // next stream sequence
	subjf             subjectFilters     // subject filters and their sequences
	filters           *gsl.SimpleSublist // When we have multiple filters we will use LoadNextMsgMulti and pass this in.
	dseq              uint64             // delivered consumer sequence
	adflr             uint64             // ack delivery floor
	asflr             uint64             // ack store floor
	chkflr            uint64             // our check floor, interest streams only.
	npc               int64              // Num Pending Count
	npf               uint64             // Num Pending Floor Sequence
	dsubj             string
	qgroup            string
	lss               *lastSeqSkipList
	rlimit            *rate.Limiter
	reqSub            *subscription
	resetSub          *subscription
	ackSubOld         *subscription
	ackReplyOldT      string
	ackSubjOld        string
	ackSub            *subscription
	ackReplyT         string
	ackSubj           string
	fcPreOld          string
	fcSubjOld         string
	fcPre             string
	fcSubj            string
	nextMsgSubj       string
	nextMsgReqs       *ipQueue[*nextMsgReq]
	resetSubj         string
	maxp              int
	pblimit           int
	maxpb             int
	pbytes            int
	fcsz              int
	fcid              string
	fcSubOld          *subscription
	fcSub             *subscription
	outq              *jsOutQ
	pending           map[uint64]*Pending
	ptmr              *time.Timer
	ptmrEnd           time.Time
	rdq               []uint64
	rdqi              avl.SequenceSet
	rdc               map[uint64]uint64
	replies           map[uint64]string
	pendingDeliveries map[uint64]*jsPubMsg        // Messages that can be delivered after achieving quorum.
	waitingDeliveries map[string]*waitingDelivery // (Optional) request timeout messages that need to wait for replicated deliveries first.
	maxdc             uint64
	waiting           *waitQueue
	cfg               ConsumerConfig
	ici               *ConsumerInfo
	store             ConsumerStore
	active            bool
	replay            bool
	useV2Ack          bool
	dtmr              *time.Timer
	uptmr             *time.Timer // Unpause timer
	gwdtmr            *time.Timer
	dthresh           time.Duration
	mch               chan struct{} // Message channel
	qch               chan struct{} // Quit channel
	mqch              chan struct{} // The monitor's quit channel.
	inch              chan bool     // Interest change channel
	sfreq             int32
	ackEventT         string
	nakEventT         string
	deliveryExcEventT string
	created           time.Time
	ldt               time.Time
	lat               time.Time
	lwqic             time.Time
	closed            bool

	// Clustered.
	ca        *consumerAssignment
	node      RaftNode
	infoSub   *subscription
	lqsent    time.Time
	prm       map[string]struct{}
	rsm       map[string]bool // Reset requests that need to be responded to on the internal sys account (if true).
	prOk      bool
	uch       chan struct{}
	retention RetentionPolicy

	monitorWg sync.WaitGroup
	inMonitor bool

	// R>1 proposals
	pch   chan struct{}
	phead *proposal
	ptail *proposal

	// Ack queue
	ackMsgs *ipQueue[*jsAckMsg]

	// for stream signaling when multiple filters are set.
	sigSubs []string

	// Priority groups
	// Details described in ADR-42.

	// currentPinId is the current nuid for the pinned consumer.
	// If the Consumer is running in `PriorityPinnedClient` mode, server will
	// pick up a new nuid and assign it to first pending pull request.
	currentPinId string
	/// pinnedTtl is the remaining time before the current PinId expires.
	pinnedTtl *time.Timer
	pinnedTS  time.Time

	// If standalone/single-server, the offline reason needs to be stored directly in the consumer.
	// Otherwise, if clustered it will be part of the consumer assignment.
	offlineReason string
}

// A single subject filter.
type subjectFilter struct {
	subject          string
	tokenizedSubject []string
	hasWildcard      bool
}

type subjectFilters []*subjectFilter

// subjects is a helper function used for updating consumers.
// It is not used and should not be used in hotpath.
func (s subjectFilters) subjects() []string {
	subjects := make([]string, 0, len(s))
	for _, filter := range s {
		subjects = append(subjects, filter.subject)
	}
	return subjects
}

type proposal struct {
	data []byte
	next *proposal
}

const (
	// JsAckWaitDefault is the default AckWait, only applicable on explicit ack policy consumers.
	JsAckWaitDefault = 30 * time.Second
	// JsDeleteWaitTimeDefault is the default amount of time we will wait for non-durable
	// consumers to be in an inactive state before deleting them.
	JsDeleteWaitTimeDefault = 5 * time.Second
	// JsFlowControlMaxPending specifies default pending bytes during flow control that can be outstanding.
	JsFlowControlMaxPending = 32 * 1024 * 1024
	// JsDefaultMaxAckPending is set for consumers with explicit ack that do not set the max ack pending.
	JsDefaultMaxAckPending = 1000
	// JsDefaultPinnedTTL is the default grace period for the pinned consumer to send a new request before a new pin
	// is picked by a server.
	JsDefaultPinnedTTL = 2 * time.Minute
)

// Helper function to set consumer config defaults from above.
func setConsumerConfigDefaults(config *ConsumerConfig, streamCfg *StreamConfig, lim *JSLimitOpts, accLim *JetStreamAccountLimits, pedantic bool) *ApiError {
	// Setup default of -1, meaning no limit for MaxDeliver.
	if config.MaxDeliver == 0 || config.MaxDeliver < -1 {
		if pedantic && config.MaxDeliver < -1 {
			return NewJSPedanticError(errors.New("max_deliver must be set to -1"))
		}
		config.MaxDeliver = -1
	}
	// Setup zero defaults.
	if config.MaxWaiting < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("max_waiting must not be negative"))
		}
		config.MaxWaiting = 0
	}
	if config.MaxAckPending < -1 {
		if pedantic {
			return NewJSPedanticError(errors.New("max_ack_pending must be set to -1"))
		}
		config.MaxAckPending = -1
	}
	if config.MaxRequestBatch < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("max_batch must not be negative"))
		}
		config.MaxRequestBatch = 0
	}
	if config.MaxRequestExpires < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("max_expires must not be negative"))
		}
		config.MaxRequestExpires = 0
	}
	if config.MaxRequestMaxBytes < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("max_bytes must not be negative"))
		}
		config.MaxRequestMaxBytes = 0
	}
	if config.Heartbeat < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("idle_heartbeat must not be negative"))
		}
		config.Heartbeat = 0
	}
	if config.InactiveThreshold < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("inactive_threshold must not be negative"))
		}
		config.InactiveThreshold = 0
	}
	if config.PinnedTTL < 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("priority_timeout must not be negative"))
		}
		config.PinnedTTL = 0
	}

	// Set to default if not specified.
	if config.DeliverSubject == _EMPTY_ && config.MaxWaiting == 0 {
		config.MaxWaiting = JSWaitQueueDefaultMax
	}
	// Setup proper default for ack wait if we are in explicit ack mode.
	if config.AckWait == 0 && (config.AckPolicy == AckExplicit || config.AckPolicy == AckAll) {
		config.AckWait = JsAckWaitDefault
	}
	// If BackOff was specified that will override the AckWait and the MaxDeliver.
	if len(config.BackOff) > 0 {
		if pedantic && config.AckWait != config.BackOff[0] {
			return NewJSPedanticError(errors.New("first backoff value has to equal batch AckWait"))
		}
		config.AckWait = config.BackOff[0]
	}
	if config.MaxAckPending == 0 {
		if pedantic && streamCfg.ConsumerLimits.MaxAckPending > 0 {
			return NewJSPedanticError(errors.New("max_ack_pending must be set if it's configured in stream limits"))
		}
		config.MaxAckPending = streamCfg.ConsumerLimits.MaxAckPending
	}
	if config.InactiveThreshold == 0 {
		if pedantic && streamCfg.ConsumerLimits.InactiveThreshold > 0 {
			return NewJSPedanticError(errors.New("inactive_threshold must be set if it's configured in stream limits"))
		}
		config.InactiveThreshold = streamCfg.ConsumerLimits.InactiveThreshold
	}
	// Set proper default for max ack pending if we are ack explicit and none has been set.
	if config.MaxAckPending == 0 && config.AckPolicy != AckNone {
		ackPending := JsDefaultMaxAckPending
		if lim.MaxAckPending > 0 && lim.MaxAckPending < ackPending {
			ackPending = lim.MaxAckPending
		}
		if accLim.MaxAckPending > 0 && accLim.MaxAckPending < ackPending {
			ackPending = accLim.MaxAckPending
		}
		config.MaxAckPending = ackPending
	}
	// if applicable set max request batch size
	if config.DeliverSubject == _EMPTY_ && config.MaxRequestBatch == 0 && lim.MaxRequestBatch > 0 {
		if pedantic {
			return NewJSPedanticError(errors.New("max_request_batch must be set if it's JetStream limits are set"))
		}
		config.MaxRequestBatch = lim.MaxRequestBatch
	}

	// set the default value only if pinned policy is used.
	if config.PriorityPolicy == PriorityPinnedClient && config.PinnedTTL == 0 {
		config.PinnedTTL = JsDefaultPinnedTTL
	}

	// Set default values for flow control policy.
	if config.AckPolicy == AckFlowControl && !pedantic {
		config.FlowControl = true
		config.Heartbeat = sourceHealthHB
	}
	return nil
}

// Check the consumer config. If we are recovering don't check filter subjects.
func checkConsumerCfg(
	config *ConsumerConfig,
	srvLim *JSLimitOpts,
	cfg *StreamConfig,
	_ *Account,
	accLim *JetStreamAccountLimits,
	isRecovering bool,
) *ApiError {

	if config.Name != _EMPTY_ && !isValidAssetName(config.Name) {
		return NewJSStreamInvalidConfigError(errors.New("consumer name can not contain '.', '*', '>', '\\', '/'"))
	}
	if config.Durable != _EMPTY_ && !isValidAssetName(config.Durable) {
		return NewJSStreamInvalidConfigError(errors.New("consumer durable name can not contain '.', '*', '>', '\\', '/'"))
	}

	// Check if replicas is defined but exceeds parent stream.
	if config.Replicas > 0 && config.Replicas > cfg.Replicas {
		return NewJSConsumerReplicasExceedsStreamError()
	}
	// Check that it is not negative
	if config.Replicas < 0 {
		return NewJSReplicasCountCannotBeNegativeError()
	}
	// If the stream is interest or workqueue retention make sure the replicas
	// match that of the stream. This is REQUIRED for now.
	if cfg.Retention == InterestPolicy || cfg.Retention == WorkQueuePolicy {
		// Only error here if not recovering.
		// We handle recovering in a different spot to allow consumer to come up
		// if previous version allowed it to be created. We do not want it to not come up.
		if !isRecovering && config.Replicas != 0 && config.Replicas != cfg.Replicas {
			return NewJSConsumerReplicasShouldMatchStreamError()
		}
	}

	if _, err := config.AckPolicy.MarshalJSON(); err != nil {
		return NewJSConsumerAckPolicyInvalidError()
	}
	if _, err := config.ReplayPolicy.MarshalJSON(); err != nil {
		return NewJSConsumerReplayPolicyInvalidError()
	}

	// Check not negative AckWait/BackOff
	for _, backoff := range config.BackOff {
		if backoff < 0 {
			return NewJSConsumerBackOffNegativeError()
		}
	}
	if config.AckWait < 0 {
		return NewJSConsumerAckWaitNegativeError()
	}

	// Ack Flow Control policy requires push-based flow-controlled consumer.
	if config.AckPolicy == AckFlowControl {
		if config.DeliverSubject == _EMPTY_ {
			return NewJSConsumerAckFCRequiresPushError()
		}
		if !config.FlowControl {
			return NewJSConsumerAckFCRequiresFCError()
		}
		// We currently limit using heartbeat of 1s, since those are used for ephemeral sourcing consumers as well.
		// We could decide to relax this in the future, but need to be careful to not allow a heartbeat larger
		// than the stalled source timeout.
		if config.Heartbeat != sourceHealthHB {
			return NewJSStreamInvalidConfigError(fmt.Errorf("flow control ack policy heartbeat needs to be 1s"))
		}
		if config.MaxAckPending <= 0 {
			return NewJSConsumerAckFCRequiresMaxAckPendingError()
		}
		if config.AckWait != 0 || len(config.BackOff) > 0 {
			return NewJSConsumerAckFCRequiresNoAckWaitError()
		}
		if config.MaxDeliver > 0 {
			return NewJSConsumerAckFCRequiresNoMaxDeliverError()
		}
	}

	// Check if we have a BackOff defined that MaxDeliver is within range etc.
	if lbo := len(config.BackOff); lbo > 0 && config.MaxDeliver != -1 && lbo > config.MaxDeliver {
		return NewJSConsumerMaxDeliverBackoffError()
	}

	if len(config.Description) > JSMaxDescriptionLen {
		return NewJSConsumerDescriptionTooLongError(JSMaxDescriptionLen)
	}

	// For now expect a literal subject if its not empty. Empty means work queue mode (pull mode).
	if config.DeliverSubject != _EMPTY_ {
		if !subjectIsLiteral(config.DeliverSubject) {
			return NewJSConsumerDeliverToWildcardsError()
		}
		if !IsValidSubject(config.DeliverSubject) {
			return NewJSConsumerInvalidDeliverSubjectError()
		}
		if deliveryFormsCycle(cfg, config.DeliverSubject) {
			return NewJSConsumerDeliverCycleError()
		}
		if config.MaxWaiting != 0 {
			return NewJSConsumerPushMaxWaitingError()
		}
		if config.MaxAckPending > 0 && config.AckPolicy == AckNone {
			return NewJSConsumerMaxPendingAckPolicyRequiredError()
		}
		if config.Heartbeat > 0 && config.Heartbeat < 100*time.Millisecond {
			return NewJSConsumerSmallHeartbeatError()
		}
	} else {
		// Pull mode with work queue retention from the stream requires an explicit ack.
		if config.AckPolicy == AckNone && cfg.Retention == WorkQueuePolicy {
			return NewJSConsumerPullRequiresAckError()
		}
		if config.RateLimit > 0 {
			return NewJSConsumerPullWithRateLimitError()
		}
		if config.MaxWaiting < 0 {
			return NewJSConsumerMaxWaitingNegativeError()
		}
		if config.Heartbeat > 0 {
			return NewJSConsumerHBRequiresPushError()
		}
		if config.FlowControl {
			return NewJSConsumerFCRequiresPushError()
		}
		if config.MaxRequestBatch < 0 {
			return NewJSConsumerMaxRequestBatchNegativeError()
		}
		if config.MaxRequestExpires != 0 && config.MaxRequestExpires < time.Millisecond {
			return NewJSConsumerMaxRequestExpiresTooSmallError()
		}
		if srvLim.MaxRequestBatch > 0 && config.MaxRequestBatch > srvLim.MaxRequestBatch {
			return NewJSConsumerMaxRequestBatchExceededError(srvLim.MaxRequestBatch)
		}
	}
	if srvLim.MaxAckPending > 0 && config.MaxAckPending > srvLim.MaxAckPending {
		return NewJSConsumerMaxPendingAckExcessError(srvLim.MaxAckPending)
	}
	if accLim.MaxAckPending > 0 && config.MaxAckPending > accLim.MaxAckPending {
		return NewJSConsumerMaxPendingAckExcessError(accLim.MaxAckPending)
	}
	if cfg.ConsumerLimits.MaxAckPending > 0 && config.MaxAckPending > cfg.ConsumerLimits.MaxAckPending {
		return NewJSConsumerMaxPendingAckExcessError(cfg.ConsumerLimits.MaxAckPending)
	}
	if cfg.ConsumerLimits.InactiveThreshold > 0 && config.InactiveThreshold > cfg.ConsumerLimits.InactiveThreshold {
		return NewJSConsumerInactiveThresholdExcessError(cfg.ConsumerLimits.InactiveThreshold)
	}

	// Direct need to be non-mapped ephemerals.
	if config.Direct {
		if config.DeliverSubject == _EMPTY_ {
			return NewJSConsumerDirectRequiresPushError()
		}
		if isDurableConsumer(config) {
			return NewJSConsumerDirectRequiresEphemeralError()
		}
	}

	// Do not allow specifying both FilterSubject and FilterSubjects,
	// as that's probably unintentional without any difference from passing
	// all filters in FilterSubjects.
	if config.FilterSubject != _EMPTY_ && len(config.FilterSubjects) > 0 {
		return NewJSConsumerDuplicateFilterSubjectsError()
	}

	if config.FilterSubject != _EMPTY_ && !IsValidSubject(config.FilterSubject) {
		return NewJSStreamInvalidConfigError(ErrBadSubject)
	}

	// We treat FilterSubjects: []string{""} as a misconfig, so we validate against it.
	for _, filter := range config.FilterSubjects {
		if filter == _EMPTY_ {
			return NewJSConsumerEmptyFilterError()
		}
	}
	subjectFilters := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)

	// Check subject filters do not overlap.
	for outer, subject := range subjectFilters {
		if !IsValidSubject(subject) {
			return NewJSStreamInvalidConfigError(ErrBadSubject)
		}
		for inner, ssubject := range subjectFilters {
			if inner != outer && subjectIsSubsetMatch(subject, ssubject) {
				return NewJSConsumerOverlappingSubjectFiltersError()
			}
		}
	}

	// Helper function to formulate similar errors.
	badStart := func(dp, start string) error {
		return fmt.Errorf("consumer delivery policy is deliver %s, but optional start %s is also set", dp, start)
	}
	notSet := func(dp, notSet string) error {
		return fmt.Errorf("consumer delivery policy is deliver %s, but optional %s is not set", dp, notSet)
	}

	// Check on start position conflicts.
	switch config.DeliverPolicy {
	case DeliverAll:
		if config.OptStartSeq > 0 {
			return NewJSConsumerInvalidPolicyError(badStart("all", "sequence"))
		}
		if config.OptStartTime != nil {
			return NewJSConsumerInvalidPolicyError(badStart("all", "time"))
		}
	case DeliverLast:
		if config.OptStartSeq > 0 {
			return NewJSConsumerInvalidPolicyError(badStart("last", "sequence"))
		}
		if config.OptStartTime != nil {
			return NewJSConsumerInvalidPolicyError(badStart("last", "time"))
		}
	case DeliverLastPerSubject:
		if config.OptStartSeq > 0 {
			return NewJSConsumerInvalidPolicyError(badStart("last per subject", "sequence"))
		}
		if config.OptStartTime != nil {
			return NewJSConsumerInvalidPolicyError(badStart("last per subject", "time"))
		}
		if config.FilterSubject == _EMPTY_ && len(config.FilterSubjects) == 0 {
			return NewJSConsumerInvalidPolicyError(notSet("last per subject", "filter subject"))
		}
	case DeliverNew:
		if config.OptStartSeq > 0 {
			return NewJSConsumerInvalidPolicyError(badStart("new", "sequence"))
		}
		if config.OptStartTime != nil {
			return NewJSConsumerInvalidPolicyError(badStart("new", "time"))
		}
	case DeliverByStartSequence:
		if config.OptStartSeq == 0 {
			return NewJSConsumerInvalidPolicyError(notSet("by start sequence", "start sequence"))
		}
		if config.OptStartTime != nil {
			return NewJSConsumerInvalidPolicyError(badStart("by start sequence", "time"))
		}
	case DeliverByStartTime:
		if config.OptStartTime == nil {
			return NewJSConsumerInvalidPolicyError(notSet("by start time", "start time"))
		}
		if config.OptStartSeq != 0 {
			return NewJSConsumerInvalidPolicyError(badStart("by start time", "start sequence"))
		}
	}

	if config.SampleFrequency != _EMPTY_ {
		s := strings.TrimSuffix(config.SampleFrequency, "%")
		if sampleFreq, err := strconv.Atoi(s); err != nil || sampleFreq < 0 {
			return NewJSConsumerInvalidSamplingError(err)
		}
	}

	// We reject if flow control is set without heartbeats.
	if config.FlowControl && config.Heartbeat == 0 {
		return NewJSConsumerWithFlowControlNeedsHeartbeatsError()
	}

	if config.Durable != _EMPTY_ && config.Name != _EMPTY_ {
		if config.Name != config.Durable {
			return NewJSConsumerCreateDurableAndNameMismatchError()
		}
	}

	var metadataLen int
	for k, v := range config.Metadata {
		metadataLen += len(k) + len(v)
	}
	if metadataLen > JSMaxMetadataLen {
		return NewJSConsumerMetadataLengthError(fmt.Sprintf("%dKB", JSMaxMetadataLen/1024))
	}

	if config.PriorityPolicy != PriorityNone {
		if config.DeliverSubject != "" {
			return NewJSConsumerPushWithPriorityGroupError()
		}
		if len(config.PriorityGroups) == 0 {
			return NewJSConsumerPriorityPolicyWithoutGroupError()
		}

		for _, group := range config.PriorityGroups {
			if group == _EMPTY_ {
				return NewJSConsumerEmptyGroupNameError()
			}
			if !validGroupName.MatchString(group) {
				return NewJSConsumerInvalidGroupNameError()
			}
		}
	} else {
		// If PriorityPolicy is None or not set, reject if PriorityGroups or PinnedTTL are set
		if len(config.PriorityGroups) > 0 {
			return NewJSConsumerPriorityGroupWithPolicyNoneError()
		}
		if config.PinnedTTL > 0 {
			return NewJSConsumerPinnedTTLWithoutPriorityPolicyNoneError()
		}
	}

	// For now don't allow preferred server in placement.
	if cfg.Placement != nil && cfg.Placement.Preferred != _EMPTY_ {
		return NewJSStreamInvalidConfigError(fmt.Errorf("preferred server not permitted in placement"))
	}

	return nil
}

func (mset *stream) addConsumerWithAction(config *ConsumerConfig, action ConsumerAction, pedantic bool) (*consumer, error) {
	return mset.addConsumerWithAssignment(config, _EMPTY_, nil, false, action, pedantic)
}

func (mset *stream) addConsumer(config *ConsumerConfig) (*consumer, error) {
	return mset.addConsumerWithAction(config, ActionCreateOrUpdate, false)
}

func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname string, ca *consumerAssignment, isRecovering bool, action ConsumerAction, pedantic bool) (*consumer, error) {
	// Check if this stream has closed.
	if mset.closed.Load() {
		return nil, NewJSStreamInvalidError()
	}

	mset.mu.RLock()
	s, js, jsa, cfg, acc, lseq := mset.srv, mset.js, mset.jsa, mset.cfg, mset.acc, mset.lseq
	mset.mu.RUnlock()

	// If we do not have the consumer currently assigned to us in cluster mode we will proceed but warn.
	// This can happen on startup with restored state where on meta replay we still do not have
	// the assignment. Running in single server mode this always returns true.
	if oname != _EMPTY_ && !jsa.consumerAssigned(mset.name(), oname) {
		s.Debugf("Consumer %q > %q does not seem to be assigned to this server", mset.name(), oname)
	}

	if config == nil {
		return nil, NewJSConsumerConfigRequiredError()
	}

	selectedLimits, _, _, _ := acc.selectLimits(config.replicas(&cfg))
	if selectedLimits == nil {
		return nil, NewJSNoLimitsError()
	}

	srvLim := &s.getOpts().JetStreamLimits
	// Make sure we have sane defaults. Do so with the JS lock, otherwise a
	// badly timed meta snapshot can result in a race condition.
	mset.js.mu.Lock()
	err := setConsumerConfigDefaults(config, &cfg, srvLim, selectedLimits, pedantic)
	mset.js.mu.Unlock()
	if err != nil {
		return nil, err
	}

	if err := checkConsumerCfg(config, srvLim, &cfg, acc, selectedLimits, isRecovering); err != nil {
		return nil, err
	}
	sampleFreq := 0
	if config.SampleFrequency != _EMPTY_ {
		// Can't fail as checkConsumerCfg checks correct format
		sampleFreq, _ = strconv.Atoi(strings.TrimSuffix(config.SampleFrequency, "%"))
	}

	// Grab the client, account and server reference.
	c := mset.client
	if c == nil {
		return nil, NewJSStreamInvalidError()
	}
	var accName string
	c.mu.Lock()
	s, a := c.srv, c.acc
	if a != nil {
		accName = a.Name
	}
	c.mu.Unlock()

	// Hold mset lock here.
	mset.mu.Lock()
	if mset.client == nil || mset.store == nil || mset.consumers == nil {
		mset.mu.Unlock()
		return nil, NewJSStreamInvalidError()
	}

	// If this one is durable and already exists, we let that be ok as long as only updating what should be allowed.
	var cName string
	if isDurableConsumer(config) {
		cName = config.Durable
	} else if config.Name != _EMPTY_ {
		cName = config.Name
	}
	if cName != _EMPTY_ {
		if eo, ok := mset.consumers[cName]; ok {
			if action == ActionCreate {
				ocfg := eo.config()
				copyConsumerMetadata(config, &ocfg)
				if !reflect.DeepEqual(config, &ocfg) {
					mset.mu.Unlock()
					return nil, NewJSConsumerAlreadyExistsError()
				}
			}
			// Check for overlapping subjects if we are a workqueue
			if cfg.Retention == WorkQueuePolicy {
				subjects := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)
				if !mset.partitionUnique(cName, subjects) {
					mset.mu.Unlock()
					return nil, NewJSConsumerWQConsumerNotUniqueError()
				}
			}
			mset.mu.Unlock()
			err := eo.updateConfig(config)
			if err == nil {
				return eo, nil
			}
			return nil, NewJSConsumerCreateError(err, Unless(err))
		}
	}
	if action == ActionUpdate {
		mset.mu.Unlock()
		return nil, NewJSConsumerDoesNotExistError()
	}

	standalone := !s.JetStreamIsClustered() && s.standAloneMode()

	// If we're clustered we've already done this check, only do this if we're a standalone server.
	// But if we're standalone, only enforce if we're not recovering, since the MaxConsumers could've
	// been updated while we already had more consumers on disk.
	if standalone && !isRecovering {
		// Check for any limits, if the config for the consumer sets a limit we check against that
		// but if not we use the value from account limits, if account limits is more restrictive
		// than stream config we prefer the account limits to handle cases where account limits are
		// updated during the lifecycle of the stream
		maxc := cfg.MaxConsumers
		if maxc <= 0 || (selectedLimits.MaxConsumers > 0 && selectedLimits.MaxConsumers < maxc) {
			maxc = selectedLimits.MaxConsumers
		}
		if maxc > 0 && mset.numLimitableConsumers() >= maxc {
			mset.mu.Unlock()
			return nil, NewJSMaximumConsumersLimitError()
		}
	}

	// Check on stream type conflicts with WorkQueues.
	if cfg.Retention == WorkQueuePolicy && !config.Direct && !config.Sourcing {
		// Force explicit acks here.
		if config.AckPolicy != AckExplicit && config.AckPolicy != AckFlowControl {
			mset.mu.Unlock()
			return nil, NewJSConsumerWQRequiresExplicitAckError()
		}

		if mset.numLimitableConsumers() > 0 {
			subjects := gatherSubjectFilters(config.FilterSubject, config.FilterSubjects)
			if len(subjects) == 0 {
				mset.mu.Unlock()
				return nil, NewJSConsumerWQMultipleUnfilteredError()
			} else if !mset.partitionUnique(cName, subjects) {
				// Prior to v2.9.7, on a stream with WorkQueue policy, the servers
				// were not catching the error of having multiple consumers with
				// overlapping filter subjects depending on the scope, for instance
				// creating "foo.*.bar" and then "foo.>" was not detected, while
				// "foo.>" and then "foo.*.bar" would have been. Failing here
				// in recovery mode would leave the rejected consumer in a bad state,
				// so we will simply warn here, asking the user to remove this
				// consumer administratively. Otherwise, if this is the creation
				// of a new consumer, we will return the error.
				if isRecovering {
					s.Warnf("Consumer %q > %q has a filter subject that overlaps "+
						"with other consumers, which is not allowed for a stream "+
						"with WorkQueue policy, it should be administratively deleted",
						cfg.Name, cName)
				} else {
					// We have a partition but it is not unique amongst the others.
					mset.mu.Unlock()
					return nil, NewJSConsumerWQConsumerNotUniqueError()
				}
			}
		}
		if config.DeliverPolicy != DeliverAll {
			mset.mu.Unlock()
			return nil, NewJSConsumerWQConsumerNotDeliverAllError()
		}
	}

	// Set name, which will be durable name if set, otherwise we create one at random.
	o := &consumer{
		mset:      mset,
		js:        s.getJetStream(),
		acc:       a,
		srv:       s,
		client:    s.createInternalJetStreamClient(),
		sysc:      s.createInternalJetStreamClient(),
		cfg:       *config,
		dsubj:     config.DeliverSubject,
		outq:      mset.outq,
		active:    true,
		qch:       make(chan struct{}),
		mqch:      make(chan struct{}),
		uch:       make(chan struct{}, 1),
		mch:       make(chan struct{}, 1),
		sfreq:     int32(sampleFreq),
		maxdc:     uint64(max(config.MaxDeliver, 0)), // MaxDeliver is negative (-1) when infinite.
		maxp:      config.MaxAckPending,
		retention: cfg.Retention,
		created:   time.Now().UTC(),
	}

	// Add created timestamp used for the store, must match that of the consumer assignment if it exists.
	if ca != nil {
		js.mu.RLock()
		o.created = ca.Created
		js.mu.RUnlock()
	}

	// Bind internal client to the user account.
	o.client.registerWithAccount(a)
	// Bind to the system account.
	o.sysc.registerWithAccount(s.SystemAccount())

	if isDurableConsumer(config) {
		if len(config.Durable) > JSMaxNameLen {
			mset.mu.Unlock()
			o.deleteWithoutAdvisory()
			return nil, NewJSConsumerNameTooLongError(JSMaxNameLen)
		}
		o.name = config.Durable
	} else if oname != _EMPTY_ {
		o.name = oname
	} else {
		if config.Name != _EMPTY_ {
			o.name = config.Name
		} else {
			// Legacy ephemeral auto-generated.
			for {
				o.name = createConsumerName()
				if _, ok := mset.consumers[o.name]; !ok {
					break
				}
			}
			config.Name = o.name
		}
	}
	// Create ackMsgs queue now that we have a consumer name
	o.ackMsgs = newIPQueue[*jsAckMsg](s, fmt.Sprintf("[ACC:%s] consumer '%s' on stream '%s' ackMsgs", accName, o.name, cfg.Name))

	// Create our request waiting queue.
	if o.isPullMode() {
		o.waiting = newWaitQueue(config.MaxWaiting)
		// Create our internal queue for next msg requests.
		o.nextMsgReqs = newIPQueue[*nextMsgReq](s, fmt.Sprintf("[ACC:%s] consumer '%s' on stream '%s' pull requests", accName, o.name, cfg.Name))
	}

	// already under lock, mset.Name() would deadlock
	o.stream = cfg.Name
	o.ackEventT = JSMetricConsumerAckPre + "." + o.stream + "." + o.name
	o.nakEventT = JSAdvisoryConsumerMsgNakPre + "." + o.stream + "." + o.name
	o.deliveryExcEventT = JSAdvisoryConsumerMaxDeliveryExceedPre + "." + o.stream + "." + o.name

	if !isValidAssetName(o.name) {
		mset.mu.Unlock()
		o.deleteWithoutAdvisory()
		return nil, NewJSConsumerBadDurableNameError()
	}

	// Setup our storage if not a direct consumer.
	if !config.Direct {
		store, err := mset.store.ConsumerStore(o.name, o.created, config)
		if err != nil {
			mset.mu.Unlock()
			o.deleteWithoutAdvisory()
			return nil, NewJSConsumerStoreFailedError(err)
		}
		o.store = store
	}

	for _, filter := range gatherSubjectFilters(o.cfg.FilterSubject, o.cfg.FilterSubjects) {
		sub := &subjectFilter{
			subject:          filter,
			hasWildcard:      subjectHasWildcard(filter),
			tokenizedSubject: tokenizeSubjectIntoSlice(nil, filter),
		}
		o.subjf = append(o.subjf, sub)
	}

	// If we have multiple filter subjects, create a sublist which we will use
	// in calling store.LoadNextMsgMulti.
	if len(o.subjf) <= 1 {
		o.filters = nil
	} else {
		o.filters = gsl.NewSublist[struct{}]()
		for _, filter := range o.subjf {
			o.filters.Insert(filter.subject, struct{}{})
		}
	}

	if o.store != nil && o.store.HasState() {
		// Restore our saved state.
		o.mu.Lock()
		o.readStoredState()

		replicas := o.cfg.replicas(&mset.cfg)

		// Starting sequence represents the next sequence to be delivered, so decrement it
		// since that's the minimum amount the stream should have as its last sequence.
		sseq := o.sseq
		if sseq > 0 {
			sseq--
		}

		o.mu.Unlock()

		// A stream observing data loss rolls back in its sequence. Check if we need to reconcile the consumer state
		// to ensure new messages aren't skipped.
		// Only performed for non-replicated consumers for now.
		if replicas == 1 && lseq < sseq && isRecovering {
			s.Warnf("JetStream consumer '%s > %s > %s' delivered sequence %d past last stream sequence of %d",
				o.acc.Name, o.stream, o.name, sseq, lseq)

			o.mu.Lock()
			o.reconcileStateWithStream(lseq)

			// Save the reconciled state
			state := &ConsumerState{
				Delivered: SequencePair{
					Stream:   o.sseq - 1,
					Consumer: o.dseq - 1,
				},
				AckFloor: SequencePair{
					Stream:   o.asflr,
					Consumer: o.adflr,
				},
				Pending:     o.pending,
				Redelivered: o.rdc,
			}
			err := o.store.ForceUpdate(state)
			o.mu.Unlock()
			if err != nil {
				s.Errorf("JetStream consumer '%s > %s > %s' errored while updating state: %v", o.acc.Name, o.stream, o.name, err)
				mset.mu.Unlock()
				return nil, NewJSConsumerStoreFailedError(err)
			}
		}
	} else if config.Direct || standalone {
		// Clustered non-direct consumers defer this to setLeader so the
		// expensive store scans don't block the meta apply goroutine.
		if err := o.selectStartingSeqNo(); err != nil {
			return nil, err
		}
	}

	// Now register with mset and create the ack subscription.
	// Check if we already have this one registered.
	if eo, ok := mset.consumers[o.name]; ok {
		mset.mu.Unlock()
		if !o.isDurable() || !o.isPushMode() {
			o.name = _EMPTY_ // Prevent removal since same name.
			o.deleteWithoutAdvisory()
			return nil, NewJSConsumerNameExistError()
		}
		// If we are here we have already registered this durable. If it is still active that is an error.
		if eo.isActive() {
			o.name = _EMPTY_ // Prevent removal since same name.
			o.deleteWithoutAdvisory()
			return nil, NewJSConsumerExistingActiveError()
		}
		// Since we are here this means we have a potentially new durable so we should update here.
		// Check that configs are the same.
		if !configsEqualSansDelivery(o.cfg, eo.cfg) {
			o.name = _EMPTY_ // Prevent removal since same name.
			o.deleteWithoutAdvisory()
			return nil, NewJSConsumerReplacementWithDifferentNameError()
		}
		// Once we are here we have a replacement push-based durable.
		eo.updateDeliverSubject(o.cfg.DeliverSubject)
		return eo, nil
	}

	// Set up the ack subscription for this consumer. Will use wildcard for all acks.
	// We will remember the template to generate replies with sequence numbers and use
	// that to scanf them back in.
	// Escape '%' in consumer and stream names, as `pre` is used as a template later
	// in consumer.ackReply(), resulting in erroneous formatting of the ack subject.
	mn := strings.ReplaceAll(cfg.Name, "%", "%%")
	on := strings.ReplaceAll(o.name, "%", "%%")
	domain := strings.ReplaceAll(o.srv.getOpts().JetStreamDomain, "%", "%%")
	if domain == _EMPTY_ {
		domain = "_"
	}
	accHash := getHash(accName)

	o.useV2Ack = s.getOpts().getFeatureFlag(FeatureFlagJsAckFormatV2)

	// v1 format: $JS.(ACK|FC).<stream>.<consumer>.etc.
	o.fcPreOld = jsFlowControlPre
	o.fcSubjOld = fmt.Sprintf(jsFlowControl, cfg.Name, o.name)
	preOld := fmt.Sprintf(jsAckT, mn, on)
	o.ackReplyOldT = fmt.Sprintf("%s.%%d.%%d.%%d.%%d.%%d", preOld)
	o.ackSubjOld = fmt.Sprintf("%s.*.*.*.*.*", preOld)

	// v2 format: $JS.(ACK|FC).<domain>.<accHash>.<stream>.<consumer>.etc.
	o.fcPre = fmt.Sprintf("%s%s.%s.", jsFlowControlPre, domain, accHash)
	o.fcSubj = fmt.Sprintf(jsFlowControlV2, domain, accHash, cfg.Name, o.name)
	pre := fmt.Sprintf(jsAckTv2, domain, accHash, mn, on)
	o.ackReplyT = fmt.Sprintf("%s.%%d.%%d.%%d.%%d.%%d", pre)
	// Subscribe on this ack subject for v2, we require 11 tokens, but allow for more tokens/extension.
	o.ackSubj = fmt.Sprintf("%s.*.*.*.*.>", pre)

	o.nextMsgSubj = fmt.Sprintf(JSApiRequestNextT, mn, o.name)
	o.resetSubj = fmt.Sprintf(JSApiConsumerResetT, mn, o.name)

	// Check/update the inactive threshold
	o.updateInactiveThreshold(&o.cfg)

	if o.isPushMode() {
		// Check if we are running only 1 replica and that the delivery subject has interest.
		// Check in place here for interest. Will setup properly in setLeader.
		if config.replicas(&cfg) == 1 {
			interest := o.acc.sl.HasInterest(o.cfg.DeliverSubject)
			if !o.hasDeliveryInterest(interest) {
				// Let the interest come to us eventually, but setup delete timer.
				o.updateDeliveryInterest(false)
			}
		}
	}

	// Set our ca.
	if ca != nil {
		o.setConsumerAssignment(ca)
	}

	// Check if we have a rate limit set.
	if config.RateLimit != 0 {
		o.setRateLimit(config.RateLimit)
	}

	mset.setConsumer(o)
	mset.mu.Unlock()

	if config.Sourcing && standalone {
		o.resetStartingSeq(0, _EMPTY_, false)
	}
	if config.Direct || standalone {
		o.setLeader(true)
	}

	// This is always true in single server mode.
	if o.IsLeader() {
		// Send advisory.
		var suppress bool
		if !s.standAloneMode() && ca == nil {
			suppress = true
		} else if ca != nil {
			suppress = ca.hasResponded()
		}
		if !suppress {
			o.sendCreateAdvisory()
		}
	}

	return o, nil
}

// Updates the consumer `dthresh` delete timer duration and set
// cfg.InactiveThreshold to JsDeleteWaitTimeDefault for ephemerals
// if not explicitly already specified by the user.
// Lock should be held.
func (o *consumer) updateInactiveThreshold(cfg *ConsumerConfig) {
	// Ephemerals will always have inactive thresholds.
	if !o.isDurable() && cfg.InactiveThreshold <= 0 {
		// Add in 1 sec of jitter above and beyond the default of 5s.
		o.dthresh = JsDeleteWaitTimeDefault + 100*time.Millisecond + time.Duration(rand.Int63n(900))*time.Millisecond
		// Only stamp config with default sans jitter.
		cfg.InactiveThreshold = JsDeleteWaitTimeDefault
	} else if cfg.InactiveThreshold > 0 {
		// Add in up to 1 sec of jitter if pull mode.
		if o.isPullMode() {
			o.dthresh = cfg.InactiveThreshold + 100*time.Millisecond + time.Duration(rand.Int63n(900))*time.Millisecond
		} else {
			o.dthresh = cfg.InactiveThreshold
		}
	} else if cfg.InactiveThreshold <= 0 {
		// We accept InactiveThreshold be set to 0 (for durables)
		o.dthresh = 0
	}
}

// Updates the paused state. If we are the leader and the pause deadline
// hasn't passed yet then we will start a timer to kick the consumer once
// that deadline is reached. Lock should be held.
func (o *consumer) updatePauseState(cfg *ConsumerConfig) {
	if o.uptmr != nil {
		stopAndClearTimer(&o.uptmr)
	}
	if !o.isLeader() {
		// Only the leader will run the timer as only the leader will run
		// loopAndGatherMsgs.
		return
	}
	if cfg.PauseUntil == nil || cfg.PauseUntil.IsZero() || cfg.PauseUntil.Before(time.Now()) {
		// Either the PauseUntil is unset (is effectively zero) or the
		// deadline has already passed, in which case there is nothing
		// to do.
		return
	}
	o.uptmr = time.AfterFunc(time.Until(*cfg.PauseUntil), func() {
		o.mu.Lock()
		defer o.mu.Unlock()

		stopAndClearTimer(&o.uptmr)
		o.sendPauseAdvisoryLocked(&o.cfg)
		o.signalNewMessages()
	})
}

func (o *consumer) consumerAssignment() *consumerAssignment {
	o.mu.RLock()
	defer o.mu.RUnlock()
	return o.ca
}

func (o *consumer) setConsumerAssignment(ca *consumerAssignment) {
	o.mu.Lock()
	defer o.mu.Unlock()

	o.ca = ca
	if ca == nil {
		return
	}
	// Set our node.
	o.node = ca.Group.node

	// Trigger update chan.
	select {
	case o.uch <- struct{}{}:
	default:
	}
}

func (o *consumer) monitorQuitC() <-chan struct{} {
	if o == nil {
		return nil
	}
	o.mu.Lock()
	defer o.mu.Unlock()
	// Recreate if a prior monitor routine was stopped.
	if o.mqch == nil {
		o.mqch = make(chan struct{})
	}
	return o.mqch
}

// signalMonitorQuit signals to exit the monitor loop. If there's no Raft node,
// this will be the only way to stop the monitor goroutine.
func (o *consumer) signalMonitorQuit() {
	o.mu.Lock()
	defer o.mu.Unlock()
	if o.mqch != nil {
		close(o.mqch)
		o.mqch = nil
	}
}

func (o *consumer) updateC() <-chan struct{} {
	o.mu.RLock()
	defer o.mu.RUnlock()
	return o.uch
}

// checkQueueInterest will check on our interest's queue group status.
// Lock should be held.
func (o *consumer) checkQueueInterest() {
	if !o.active || o.cfg.DeliverSubject == _EMPTY_ {
		return
	}
	subj := o.dsubj
	if subj == _EMPTY_ {
		subj = o.cfg.DeliverSubject
	}

	if rr := o.acc.sl.Match(subj); len(rr.qsubs) > 0 {
		// Just grab first
		if qsubs := rr.qsubs[0]; len(qsubs) > 0 {
			if sub := rr.qsubs[0][0]; len(sub.queue) > 0 {
				o.qgroup = string(sub.queue)
			}
		}
	}
}

// clears our node if we have one. When we scale down to 1.
func (o *consumer) clearNode() {
	o.mu.Lock()
	defer o.mu.Unlock()
	if o.node != nil {
		o.node.Delete()
		o.node = nil
	}
}

// IsLeader will return if we are the current leader.
func (o *consumer) IsLeader() bool {
	return o.isLeader()
}

// Lock should be held.
func (o *consumer) isLeader() bool {
	return o.leader.Load()
}

func (o *consumer) setLeader(isLeader bool) error {
	o.mu.RLock()
	mset, closed := o.mset, o.closed
	movingToClustered := o.node != nil && o.pch == nil
	movingToNonClustered := o.node == nil && o.pch != nil
	wasLeader := o.leader.Swap(isLeader)

	// For clustered new consumers, starting seq selection was deferred from
	// addConsumerWithAssignment so the scan wouldn't block the meta apply
	// goroutine, run it here on leader-elect instead.
	needsSelect := isLeader && !wasLeader && o.dseq == 0 && (o.store == nil || !o.store.HasState())
	o.mu.RUnlock()

	// If we are here we have a change in leader status.
	if isLeader {
		if closed || mset == nil {
			return nil
		}

		if needsSelect {
			o.mu.Lock()
			if err := o.selectStartingSeqNo(); err != nil {
				o.srv.Errorf("JetStream consumer '%s > %s > %s' select starting seq failed: %v",
					o.acc.Name, o.stream, o.name, err)
				o.leader.Store(false)
				node := o.node
				o.mu.Unlock()
				if node != nil {
					_ = node.StepDown()
				}
				return err
			}
			o.mu.Unlock()
		}

		if wasLeader {
			// If we detect we are scaling up, make sure to create clustered routines and channels.
			if movingToClustered {
				o.mu.Lock()
				// We are moving from R1 to clustered.
				o.pch = make(chan struct{}, 1)
				go o.loopAndForwardProposals(o.qch)
				if o.phead != nil {
					select {
					case o.pch <- struct{}{}:
					default:
					}
				}
				o.mu.Unlock()
			} else if movingToNonClustered {
				// We are moving from clustered to non-clustered now.
				// Set pch to nil so if we scale back up we will recreate the loopAndForward from above.
				o.mu.Lock()
				pch := o.pch
				o.pch = nil
				select {
				case pch <- struct{}{}:
				default:
				}
				o.mu.Unlock()
			}
			return nil
		}

		mset.mu.RLock()
		s, jsa, stream := mset.srv, mset.jsa, mset.getCfgName()
		mset.mu.RUnlock()

		o.mu.Lock()
		o.rdq = nil
		o.rdqi.Empty()

		// Restore our saved state.
		// During non-leader status we just update our underlying store when not clustered.
		// If clustered we need to propose our initial (possibly skipped ahead) o.sseq to the group.
		if o.node == nil || o.dseq > 1 || (o.store != nil && o.store.HasState()) {
			o.readStoredState()
		} else if o.node != nil && o.sseq >= 1 {
			o.updateSkipped(o.sseq)
		}

		// Setup initial num pending.
		o.streamNumPending()

		// Cleanup lss when we take over in clustered mode.
		if o.hasSkipListPending() && o.sseq >= o.lss.resume {
			o.lss = nil
		}

		// Do info sub.
		if o.infoSub == nil && jsa != nil {
			isubj := fmt.Sprintf(clusterConsumerInfoT, jsa.acc(), stream, o.name)
			// Note below the way we subscribe here is so that we can send requests to ourselves.
			o.infoSub, _ = s.systemSubscribe(isubj, _EMPTY_, false, o.sysc, o.handleClusterConsumerInfoRequest)
		}

		var err error
		if o.cfg.AckPolicy != AckNone && o.cfg.AckPolicy != AckFlowControl {
			if o.ackSubOld, err = o.subscribeInternal(o.ackSubjOld, o.pushAck); err != nil {
				o.mu.Unlock()
				return nil
			}
			if o.ackSub, err = o.subscribeInternal(o.ackSubj, o.pushAck); err != nil {
				o.mu.Unlock()
				return nil
			}
		}

		// Setup the internal sub for next message requests regardless.
		// Will error if wrong mode to provide feedback to users.
		if o.reqSub, err = o.subscribeInternal(o.nextMsgSubj, o.processNextMsgReq); err != nil {
			o.mu.Unlock()
			return nil
		}
		if o.resetSub, err = o.subscribeInternal(o.resetSubj, o.processResetReq); err != nil {
			o.mu.Unlock()
			return nil
		}

		// Check on flow control settings.
		if o.cfg.FlowControl {
			o.setMaxPendingBytes(JsFlowControlMaxPending)
			if o.fcSubOld, err = o.subscribeInternal(o.fcSubjOld, o.processFlowControl); err != nil {
				o.mu.Unlock()
				return nil
			}
			if o.fcSub, err = o.subscribeInternal(o.fcSubj, o.processFlowControl); err != nil {
				o.mu.Unlock()
				return nil
			}
		}

		// If push mode, register for notifications on interest.
		if o.isPushMode() {
			o.inch = make(chan bool, 8)
			o.acc.sl.registerNotification(o.cfg.DeliverSubject, o.cfg.DeliverGroup, o.inch)
			if o.active = <-o.inch; o.active {
				o.checkQueueInterest()
			}

			// Check gateways in case they are enabled.
			if s.gateway.enabled {
				if !o.active {
					o.active = s.hasGatewayInterest(o.acc.Name, o.cfg.DeliverSubject)
				}
				stopAndClearTimer(&o.gwdtmr)
				o.gwdtmr = time.AfterFunc(time.Second, func() { o.watchGWinterest() })
			}
		}

		if o.dthresh > 0 && (o.isPullMode() || !o.active) {
			// Pull consumer. We run the dtmr all the time for this one.
			stopAndClearTimer(&o.dtmr)
			o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
		}

		// Update the consumer pause tracking.
		o.updatePauseState(&o.cfg)

		// If we are not in ReplayInstant mode mark us as in replay state until resolved.
		if o.cfg.ReplayPolicy != ReplayInstant {
			o.replay = true
		}

		// Recreate quit channel.
		o.qch = make(chan struct{})
		qch := o.qch
		node := o.node
		if node != nil && o.pch == nil {
			o.pch = make(chan struct{}, 1)
		}
		pullMode := o.isPullMode()
		o.mu.Unlock()

		// Check if there are any pending we might need to clean up etc.
		o.checkPending()

		// Snapshot initial info.
		o.infoWithSnap(true)

		// These are the labels we will use to annotate our goroutines.
		labels := pprofLabels{
			"type":     "consumer",
			"account":  mset.accName(),
			"stream":   mset.name(),
			"consumer": o.name,
		}

		// Now start up Go routine to deliver msgs.
		go func() {
			setGoRoutineLabels(labels)
			o.loopAndGatherMsgs(qch)
		}()

		// Now start up Go routine to process acks.
		go func() {
			setGoRoutineLabels(labels)
			o.processInboundAcks(qch)
		}()

		if pullMode {
			// Now start up Go routine to process inbound next message requests.
			go func() {
				setGoRoutineLabels(labels)
				o.processInboundNextMsgReqs(qch)
			}()
		}

		// If we are R>1 spin up our proposal loop.
		if node != nil {
			// Determine if we can send pending requests info to the group.
			// They must be on server versions >= 2.7.1
			o.checkAndSetPendingRequestsOk()
			o.checkPendingRequests()
			go func() {
				setGoRoutineLabels(labels)
				o.loopAndForwardProposals(qch)
			}()
		}

	} else {
		// Shutdown the go routines and the subscriptions.
		o.mu.Lock()
		if o.qch != nil {
			close(o.qch)
			o.qch = nil
		}
		// Stop any inactivity timers. Should only be running on leaders.
		stopAndClearTimer(&o.dtmr)
		// Stop any unpause timers. Should only be running on leaders.
		stopAndClearTimer(&o.uptmr)
		// Make sure to clear out any re-deliver queues
		o.stopAndClearPtmr()
		o.rdq = nil
		o.rdqi.Empty()
		o.pending = nil
		o.rsm = nil
		o.resetPendingDeliveries()
		// ok if they are nil, we protect inside unsubscribe()
		o.unsubscribe(o.ackSubOld)
		o.unsubscribe(o.ackSub)
		o.unsubscribe(o.reqSub)
		o.unsubscribe(o.resetSub)
		o.unsubscribe(o.fcSubOld)
		o.unsubscribe(o.fcSub)
		o.ackSubOld, o.ackSub, o.reqSub, o.resetSub, o.fcSubOld, o.fcSub = nil, nil, nil, nil, nil, nil
		if o.infoSub != nil {
			o.srv.sysUnsubscribe(o.infoSub)
			o.infoSub = nil
		}
		// Reset waiting if we are in pull mode.
		if o.isPullMode() {
			o.waiting = newWaitQueue(o.cfg.MaxWaiting)
			o.nextMsgReqs.drain()
		} else if o.srv.gateway.enabled {
			stopAndClearTimer(&o.gwdtmr)
		}
		o.unassignPinId()
		// If we were the leader make sure to drain queued up acks.
		if wasLeader {
			o.ackMsgs.drain()
			// Reset amount of acks that need to be processed.
			atomic.StoreInt64(&o.awl, 0)
			// Also remove any pending replies since we should not be the one to respond at this point.
			o.replies = nil
		}
		o.mu.Unlock()
	}
	return nil
}

// This is coming on the wire so do not block here.
func (o *consumer) handleClusterConsumerInfoRequest(sub *subscription, c *client, _ *Account, subject, reply string, msg []byte) {
	go o.infoWithSnapAndReply(false, reply)
}

// Lock should be held.
func (o *consumer) subscribeInternal(subject string, cb msgHandler) (*subscription, error) {
	c := o.client
	if c == nil {
		return nil, fmt.Errorf("invalid consumer")
	}
	if !c.srv.EventsEnabled() {
		return nil, ErrNoSysAccount
	}
	if cb == nil {
		return nil, fmt.Errorf("undefined message handler")
	}

	o.sid++

	// Now create the subscription
	return c.processSub([]byte(subject), nil, []byte(strconv.Itoa(o.sid)), cb, false)
}

// Unsubscribe from our subscription.
// Lock should be held.
func (o *consumer) unsubscribe(sub *subscription) {
	if sub == nil || o.client == nil {
		return
	}
	o.client.processUnsub(sub.sid)
}

// We need to make sure we protect access to the outq.
// Do all advisory sends here.
func (o *consumer) sendAdvisory(subject string, e any) {
	if o.acc == nil {
		return
	}

	// If there is no one listening for this advisory then save ourselves the effort
	// and don't bother encoding the JSON or sending it.
	if sl := o.acc.sl; (sl != nil && !sl.HasInterest(subject)) && !o.srv.hasGatewayInterest(o.acc.Name, subject) {
		return
	}

	j, err := json.Marshal(e)
	if err != nil {
		return
	}

	o.outq.sendMsg(subject, j)
}

func (o *consumer) sendDeleteAdvisoryLocked() {
	e := JSConsumerActionAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerActionAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Stream:   o.stream,
		Consumer: o.name,
		Action:   DeleteEvent,
		Domain:   o.srv.getOpts().JetStreamDomain,
	}

	subj := JSAdvisoryConsumerDeletedPre + "." + o.stream + "." + o.name
	o.sendAdvisory(subj, e)
}

func (o *consumer) sendPinnedAdvisoryLocked(group string) {
	e := JSConsumerGroupPinnedAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerGroupPinnedAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Account:        o.acc.Name,
		Stream:         o.stream,
		Consumer:       o.name,
		Domain:         o.srv.getOpts().JetStreamDomain,
		PinnedClientId: o.currentPinId,
		Group:          group,
	}

	subj := JSAdvisoryConsumerPinnedPre + "." + o.stream + "." + o.name
	o.sendAdvisory(subj, e)

}
func (o *consumer) sendUnpinnedAdvisoryLocked(group string, reason string) {
	e := JSConsumerGroupUnpinnedAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerGroupUnpinnedAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Account:  o.acc.Name,
		Stream:   o.stream,
		Consumer: o.name,
		Domain:   o.srv.getOpts().JetStreamDomain,
		Group:    group,
		Reason:   reason,
	}

	subj := JSAdvisoryConsumerUnpinnedPre + "." + o.stream + "." + o.name
	o.sendAdvisory(subj, e)

}

func (o *consumer) sendCreateAdvisory() {
	o.mu.Lock()
	defer o.mu.Unlock()

	e := JSConsumerActionAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerActionAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Stream:   o.stream,
		Consumer: o.name,
		Action:   CreateEvent,
		Domain:   o.srv.getOpts().JetStreamDomain,
	}

	subj := JSAdvisoryConsumerCreatedPre + "." + o.stream + "." + o.name
	o.sendAdvisory(subj, e)
}

func (o *consumer) sendPauseAdvisoryLocked(cfg *ConsumerConfig) {
	e := JSConsumerPauseAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerPauseAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Stream:   o.stream,
		Consumer: o.name,
		Domain:   o.srv.getOpts().JetStreamDomain,
	}

	if cfg.PauseUntil != nil {
		e.PauseUntil = *cfg.PauseUntil
		e.Paused = time.Now().Before(e.PauseUntil)
	}

	subj := JSAdvisoryConsumerPausePre + "." + o.stream + "." + o.name
	o.sendAdvisory(subj, e)
}

// Created returns created time.
func (o *consumer) createdTime() time.Time {
	o.mu.Lock()
	created := o.created
	o.mu.Unlock()
	return created
}

// Internal to allow creation time to be restored.
func (o *consumer) setCreatedTime(created time.Time) {
	o.mu.Lock()
	o.created = created
	o.mu.Unlock()
}

// This will check for extended interest in a subject. If we have local interest we just return
// that, but in the absence of local interest and presence of gateways or service imports we need
// to check those as well.
func (o *consumer) hasDeliveryInterest(localInterest bool) bool {
	o.mu.RLock()
	mset := o.mset
	if mset == nil {
		o.mu.RUnlock()
		return false
	}
	acc := o.acc
	deliver := o.cfg.DeliverSubject
	o.mu.RUnlock()

	if localInterest {
		return true
	}

	// If we are here check gateways.
	if s := acc.srv; s != nil && s.hasGatewayInterest(acc.Name, deliver) {
		return true
	}
	return false
}

func (s *Server) hasGatewayInterest(account, subject string) bool {
	gw := s.gateway
	if !gw.enabled {
		return false
	}
	gw.RLock()
	defer gw.RUnlock()
	for _, gwc := range gw.outo {
		psi, qr := gwc.gatewayInterest(account, stringToBytes(subject))
		if psi || qr != nil {
			return true
		}
	}
	return false
}

// This processes an update to the local interest for a deliver subject.
func (o *consumer) updateDeliveryInterest(localInterest bool) bool {
	interest := o.hasDeliveryInterest(localInterest)

	o.mu.Lock()
	defer o.mu.Unlock()

	mset := o.mset
	if mset == nil || o.isPullMode() {
		return false
	}

	if interest && !o.active {
		o.signalNewMessages()
	}
	// Update active status, if not active clear any queue group we captured.
	if o.active = interest; !o.active {
		o.qgroup = _EMPTY_
	} else {
		o.checkQueueInterest()
	}

	// If the delete timer has already been set do not clear here and return.
	// Note that durable can now have an inactive threshold, so don't check
	// for durable status, instead check for dthresh > 0.
	if o.dtmr != nil && o.dthresh > 0 && !interest {
		return true
	}

	// Stop and clear the delete timer always.
	stopAndClearTimer(&o.dtmr)

	// If we do not have interest anymore and have a delete threshold set, then set
	// a timer to delete us. We wait for a bit in case of server reconnect.
	if !interest && o.dthresh > 0 {
		o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
		return true
	}
	return false
}

const (
	defaultConsumerNotActiveStartInterval = 30 * time.Second
	defaultConsumerNotActiveMaxInterval   = 5 * time.Minute
)

var (
	consumerNotActiveStartInterval = defaultConsumerNotActiveStartInterval
	consumerNotActiveMaxInterval   = defaultConsumerNotActiveMaxInterval
)

// deleteNotActive must only be called from time.AfterFunc or in its own
// goroutine, as it can block on clean-up.
func (o *consumer) deleteNotActive() {
	// Take a copy of these when the goroutine starts, mostly it avoids a
	// race condition with tests that modify these consts, such as
	// TestJetStreamClusterGhostEphemeralsAfterRestart.
	cnaMax := consumerNotActiveMaxInterval
	cnaStart := consumerNotActiveStartInterval

	o.mu.Lock()
	if o.mset == nil {
		o.mu.Unlock()
		return
	}
	// Push mode just look at active.
	if o.isPushMode() {
		// If we are active simply return.
		if o.active {
			o.mu.Unlock()
			return
		}
	} else {
		// Pull mode.
		elapsed := time.Since(o.waiting.last)
		if elapsed < o.dthresh {
			// These need to keep firing so reset but use delta.
			if o.dtmr != nil {
				o.dtmr.Reset(o.dthresh - elapsed)
			} else {
				o.dtmr = time.AfterFunc(o.dthresh-elapsed, o.deleteNotActive)
			}
			o.mu.Unlock()
			return
		}
		// Check if we still have valid requests waiting.
		if o.checkWaitingForInterest() {
			if o.dtmr != nil {
				o.dtmr.Reset(o.dthresh)
			} else {
				o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
			}
			o.mu.Unlock()
			return
		}

		// We now know we have no waiting requests, and our last request was long ago.
		// However, based on AckWait the consumer could still be actively processing,
		// even if we haven't been informed if there were no acks in the meantime.
		// We must wait for the message that expires last and start counting down the
		// inactive threshold from there.
		now := time.Now().UnixNano()
		l := len(o.cfg.BackOff)
		var delay time.Duration
		var ackWait time.Duration
		for _, p := range o.pending {
			if l == 0 {
				ackWait = o.ackWait(0)
			} else {
				bi := int(o.rdc[p.Sequence])
				if bi < 0 {
					bi = 0
				} else if bi >= l {
					bi = l - 1
				}
				ackWait = o.ackWait(o.cfg.BackOff[bi])
			}
			if ts := p.Timestamp + ackWait.Nanoseconds() + o.dthresh.Nanoseconds(); ts > now {
				delay = max(delay, time.Duration(ts-now))
			}
		}
		// We'll wait for the latest time we expect an ack, plus the inactive threshold.
		// Acknowledging a message will reset this back down to just the inactive threshold.
		if delay > 0 {
			if o.dtmr != nil {
				o.dtmr.Reset(delay)
			} else {
				o.dtmr = time.AfterFunc(delay, o.deleteNotActive)
			}
			o.mu.Unlock()
			return
		}
	}

	s, js := o.mset.srv, o.srv.js.Load()
	acc, stream, name, isDirect := o.acc.Name, o.stream, o.name, o.cfg.Direct
	var qch, cqch chan struct{}
	if o.srv != nil {
		qch = o.srv.quitCh
	}
	oqch := o.qch
	o.mu.Unlock()
	if js != nil {
		cqch = js.clusterQuitC()
	}

	// Useful for pprof.
	setGoRoutineLabels(pprofLabels{
		"account":  acc,
		"stream":   stream,
		"consumer": name,
	})

	// We will delete locally regardless.
	defer o.delete()

	// If we are clustered, check if we still have this consumer assigned.
	// If we do forward a proposal to delete ourselves to the metacontroller leader.
	if !isDirect && s.JetStreamIsClustered() {
		js.mu.RLock()
		var (
			meta        RaftNode
			removeEntry []byte
		)
		ca, cc := js.consumerAssignment(acc, stream, name), js.cluster
		if ca != nil && cc != nil {
			meta = cc.meta
			cca := ca.clone()
			cca.Reply = _EMPTY_
			removeEntry = encodeDeleteConsumerAssignment(cca)
			meta.ForwardProposal(removeEntry)
		}
		js.mu.RUnlock()

		if ca != nil && cc != nil {
			// Check to make sure we went away.
			// Don't think this needs to be a monitored go routine.
			jitter := time.Duration(rand.Int63n(int64(cnaStart)))
			interval := cnaStart + jitter
			ticker := time.NewTicker(interval)
			defer ticker.Stop()
			for {
				select {
				case <-ticker.C:
				case <-qch:
					return
				case <-cqch:
					return
				case <-oqch:
					// The consumer has stopped already, likely by an earlier delete proposal being applied.
					return
				}
				js.mu.RLock()
				if js.shuttingDown {
					js.mu.RUnlock()
					return
				}
				nca := js.consumerAssignment(acc, stream, name)
				js.mu.RUnlock()
				// Make sure this is the same consumer assignment, and not a new consumer with the same name.
				if nca != nil && reflect.DeepEqual(nca, ca) {
					s.Warnf("Consumer assignment for '%s > %s > %s' not cleaned up, retrying", acc, stream, name)
					meta.ForwardProposal(removeEntry)
					if interval < cnaMax {
						interval *= 2
						ticker.Reset(interval)
					}
					continue
				}
				// We saw that consumer has been removed, all done.
				return
			}
		}
	}
}

func (o *consumer) watchGWinterest() {
	pa := o.isActive()
	// If there is no local interest...
	if o.hasNoLocalInterest() {
		o.updateDeliveryInterest(false)
		if !pa && o.isActive() {
			o.signalNewMessages()
		}
	}

	// We want this to always be running so we can also pick up on interest returning.
	o.mu.Lock()
	if o.gwdtmr != nil {
		o.gwdtmr.Reset(time.Second)
	} else {
		stopAndClearTimer(&o.gwdtmr)
		o.gwdtmr = time.AfterFunc(time.Second, func() { o.watchGWinterest() })
	}
	o.mu.Unlock()
}

// Config returns the consumer's configuration.
func (o *consumer) config() ConsumerConfig {
	o.mu.Lock()
	defer o.mu.Unlock()
	return o.cfg
}

// Check if we have hit max deliveries. If so do notification and cleanup.
// Return whether or not the max was hit.
// Lock should be held.
func (o *consumer) hasMaxDeliveries(seq uint64) bool {
	if o.maxdc == 0 {
		return false
	}
	if dc := o.deliveryCount(seq); dc >= o.maxdc {
		// We have hit our max deliveries for this sequence.
		// Only send the advisory once.
		if dc == o.maxdc {
			o.notifyDeliveryExceeded(seq, dc)
		}
		// Determine if we signal to start flow of messages again.
		if o.maxp > 0 && len(o.pending) >= o.maxp {
			o.signalNewMessages()
		}
		// Make sure to remove from pending.
		if p, ok := o.pending[seq]; ok && p != nil {
			delete(o.pending, seq)
			o.updateDelivered(p.Sequence, seq, dc, p.Timestamp)
		}
		// Ensure redelivered state is set, if not already.
		if o.rdc == nil {
			o.rdc = make(map[uint64]uint64)
		}
		o.rdc[seq] = dc
		return true
	}
	return false
}

// Force expiration of all pending.
// Lock should be held.
func (o *consumer) forceExpirePending() {
	var expired []uint64
	for seq := range o.pending {
		if !o.onRedeliverQueue(seq) && !o.hasMaxDeliveries(seq) {
			expired = append(expired, seq)
		}
	}
	if len(expired) > 0 {
		slices.Sort(expired)
		o.addToRedeliverQueue(expired...)
		// Now we should update the timestamp here since we are redelivering.
		// We will use an incrementing time to preserve order for any other redelivery.
		off := time.Now().UnixNano() - o.pending[expired[0]].Timestamp
		for _, seq := range expired {
			if p, ok := o.pending[seq]; ok && p != nil {
				p.Timestamp += off
			}
		}
		o.resetPtmr(o.ackWait(0))
	}
	o.signalNewMessages()
}

// Acquire proper locks and update rate limit.
// Will use what is in config.
func (o *consumer) setRateLimitNeedsLocks() {
	o.mu.RLock()
	mset := o.mset
	o.mu.RUnlock()

	if mset == nil {
		return
	}

	mset.mu.RLock()
	o.mu.Lock()
	o.setRateLimit(o.cfg.RateLimit)
	o.mu.Unlock()
	mset.mu.RUnlock()
}

// Set the rate limiter
// Both mset and consumer lock should be held.
func (o *consumer) setRateLimit(bps uint64) {
	if bps == 0 {
		o.rlimit = nil
		return
	}

	// TODO(dlc) - Make sane values or error if not sane?
	// We are configured in bits per sec so adjust to bytes.
	rl := rate.Limit(bps / 8)
	mset := o.mset

	// Burst should be set to maximum msg size for this account, etc.
	var burst int
	// We don't need to get cfgMu's rlock here since this function
	// is already invoked under mset.mu.RLock(), which superseeds cfgMu.
	if mset.cfg.MaxMsgSize > 0 {
		burst = int(mset.cfg.MaxMsgSize)
	} else if mset.jsa.account.limits.mpay > 0 {
		burst = int(mset.jsa.account.limits.mpay)
	} else {
		s := mset.jsa.account.srv
		burst = int(s.getOpts().MaxPayload)
	}

	o.rlimit = rate.NewLimiter(rl, burst)
}

// Check if new consumer config allowed vs old.
func (acc *Account) checkNewConsumerConfig(cfg, ncfg *ConsumerConfig) error {
	if reflect.DeepEqual(cfg, ncfg) {
		return nil
	}
	// Something different, so check since we only allow certain things to be updated.
	if cfg.DeliverPolicy != ncfg.DeliverPolicy {
		return errors.New("deliver policy can not be updated")
	}
	if cfg.OptStartSeq != ncfg.OptStartSeq {
		return errors.New("start sequence can not be updated")
	}
	if cfg.OptStartTime != nil && ncfg.OptStartTime != nil {
		// Both have start times set, compare them directly:
		if !cfg.OptStartTime.Equal(*ncfg.OptStartTime) {
			return errors.New("start time can not be updated")
		}
	} else if cfg.OptStartTime != nil || ncfg.OptStartTime != nil {
		// At least one start time is set and the other is not
		return errors.New("start time can not be updated")
	}
	if cfg.AckPolicy != ncfg.AckPolicy {
		return errors.New("ack policy can not be updated")
	}
	if cfg.ReplayPolicy != ncfg.ReplayPolicy {
		return errors.New("replay policy can not be updated")
	}
	if cfg.Heartbeat != ncfg.Heartbeat {
		return errors.New("heart beats can not be updated")
	}
	if cfg.FlowControl != ncfg.FlowControl {
		return errors.New("flow control can not be updated")
	}

	// Deliver Subject is conditional on if its bound.
	if cfg.DeliverSubject != ncfg.DeliverSubject {
		if cfg.DeliverSubject == _EMPTY_ {
			return errors.New("can not update pull consumer to push based")
		}
		if ncfg.DeliverSubject == _EMPTY_ {
			return errors.New("can not update push consumer to pull based")
		}
		if acc.sl.HasInterest(cfg.DeliverSubject) {
			return NewJSConsumerNameExistError()
		}
	}

	if cfg.MaxWaiting != ncfg.MaxWaiting {
		return errors.New("max waiting can not be updated")
	}

	// Check if BackOff is defined, MaxDeliver is within range.
	if lbo := len(ncfg.BackOff); lbo > 0 && ncfg.MaxDeliver != -1 && lbo > ncfg.MaxDeliver {
		return NewJSConsumerMaxDeliverBackoffError()
	}

	return nil
}

// Update the config based on the new config, or error if update not allowed.
func (o *consumer) updateConfig(cfg *ConsumerConfig) error {
	o.mu.Lock()
	defer o.mu.Unlock()

	if o.closed || o.mset == nil {
		return NewJSConsumerDoesNotExistError()
	}

	if err := o.acc.checkNewConsumerConfig(&o.cfg, cfg); err != nil {
		return err
	}

	// Make sure we always store PauseUntil in UTC.
	if cfg.PauseUntil != nil {
		utc := (*cfg.PauseUntil).UTC()
		cfg.PauseUntil = &utc
	}

	if o.store != nil {
		// Update local state always.
		if err := o.store.UpdateConfig(cfg); err != nil {
			return err
		}
	}

	// DeliverSubject
	if cfg.DeliverSubject != o.cfg.DeliverSubject {
		o.updateDeliverSubjectLocked(cfg.DeliverSubject)
	}

	// MaxAckPending
	if cfg.MaxAckPending != o.cfg.MaxAckPending {
		o.maxp = cfg.MaxAckPending
		o.signalNewMessages()
		// If MaxAckPending is lowered, we could have allocated a pending deliveries map of larger size.
		// Reset it here, so we can shrink the map.
		if cfg.MaxAckPending < o.cfg.MaxAckPending {
			o.resetPendingDeliveries()
		}
	}
	// AckWait
	if cfg.AckWait != o.cfg.AckWait {
		if o.ptmr != nil {
			o.resetPtmr(100 * time.Millisecond)
		}
	}
	// Rate Limit
	if cfg.RateLimit != o.cfg.RateLimit {
		// We need both locks here so do in Go routine.
		go o.setRateLimitNeedsLocks()
	}
	if cfg.SampleFrequency != o.cfg.SampleFrequency {
		s := strings.TrimSuffix(cfg.SampleFrequency, "%")
		if sampleFreq, err := strconv.ParseInt(s, 10, 32); err == nil {
			o.sfreq = int32(sampleFreq)
		}
	}
	// Set MaxDeliver if changed
	if cfg.MaxDeliver != o.cfg.MaxDeliver {
		// MaxDeliver is negative (-1) when infinite.
		o.maxdc = uint64(max(cfg.MaxDeliver, 0))
	}
	// Set InactiveThreshold if changed.
	if val := cfg.InactiveThreshold; val != o.cfg.InactiveThreshold {
		o.updateInactiveThreshold(cfg)
		stopAndClearTimer(&o.dtmr)
		// Restart timer only if we are the leader.
		if o.isLeader() && o.dthresh > 0 {
			o.dtmr = time.AfterFunc(o.dthresh, o.deleteNotActive)
		}
	}
	// Check whether the pause has changed
	{
		var old, new time.Time
		if o.cfg.PauseUntil != nil {
			old = *o.cfg.PauseUntil
		}
		if cfg.PauseUntil != nil {
			new = *cfg.PauseUntil
		}
		if !old.Equal(new) {
			o.updatePauseState(cfg)
			if o.isLeader() {
				o.sendPauseAdvisoryLocked(cfg)
			}
		}
	}

	// Check for Subject Filters update.
	newSubjects := gatherSubjectFilters(cfg.FilterSubject, cfg.FilterSubjects)
	updatedFilters := !subjectSliceEqual(newSubjects, o.subjf.subjects())
	if updatedFilters {
		newSubjf := make(subjectFilters, 0, len(newSubjects))
		for _, newFilter := range newSubjects {
			fs := &subjectFilter{
				subject:          newFilter,
				hasWildcard:      subjectHasWildcard(newFilter),
				tokenizedSubject: tokenizeSubjectIntoSlice(nil, newFilter),
			}
			newSubjf = append(newSubjf, fs)
		}
		// Make sure we have correct signaling setup.
		// Consumer lock can not be held.
		mset := o.mset
		o.mu.Unlock()
		mset.swapSigSubs(o, newSubjf.subjects())
		o.mu.Lock()

		// When we're done with signaling, we can replace the subjects.
		// If filters were removed, set `o.subjf` to nil.
		if len(newSubjf) == 0 {
			o.subjf = nil
			o.filters = nil
		} else {
			o.subjf = newSubjf
			if len(o.subjf) == 1 {
				o.filters = nil
			} else {
				o.filters = gsl.NewSublist[struct{}]()
				for _, filter := range o.subjf {
					o.filters.Insert(filter.subject, struct{}{})
				}
			}
		}
	}

	// Record new config for others that do not need special handling.
	// Allowed but considered no-op, [Description, SampleFrequency, MaxWaiting, HeadersOnly]
	o.cfg = *cfg

	if cfg.Sourcing && (!o.srv.JetStreamIsClustered() && o.srv.standAloneMode()) {
		o.resetStartingSeqLocked(0, _EMPTY_, false)
	}
	if updatedFilters {
		// Cleanup messages that lost interest.
		if o.retention == InterestPolicy {
			o.mu.Unlock()
			o.cleanupNoInterestMessages(o.mset, false)
			o.mu.Lock()
		}

		// Re-calculate num pending on update.
		o.streamNumPending()
	}

	return nil
}

// This is a config change for the delivery subject for a
// push based consumer.
func (o *consumer) updateDeliverSubject(newDeliver string) {
	// Update the config and the dsubj
	o.mu.Lock()
	defer o.mu.Unlock()
	o.updateDeliverSubjectLocked(newDeliver)
}

// This is a config change for the delivery subject for a
// push based consumer.
func (o *consumer) updateDeliverSubjectLocked(newDeliver string) {
	if o.closed || o.isPullMode() || o.cfg.DeliverSubject == newDeliver {
		return
	}

	// Force redeliver of all pending on change of delivery subject.
	if len(o.pending) > 0 {
		o.forceExpirePending()
	}

	o.acc.sl.clearNotification(o.dsubj, o.cfg.DeliverGroup, o.inch)
	o.dsubj, o.cfg.DeliverSubject = newDeliver, newDeliver
	// When we register new one it will deliver to update state loop.
	o.acc.sl.registerNotification(newDeliver, o.cfg.DeliverGroup, o.inch)
}

// Check that configs are equal but allow delivery subjects to be different.
func configsEqualSansDelivery(a, b ConsumerConfig) bool {
	// These were copied in so can set Delivery here.
	a.DeliverSubject, b.DeliverSubject = _EMPTY_, _EMPTY_
	return reflect.DeepEqual(a, b)
}

// Helper to send a reply to an ack.
func (o *consumer) sendAckReply(subj string) {
	o.mu.RLock()
	defer o.mu.RUnlock()
	o.outq.sendMsg(subj, nil)
}

type jsAckMsg struct {
	subject string
	reply   string
	hdr     int
	msg     []byte
}

var jsAckMsgPool sync.Pool

func newJSAckMsg(subj, reply string, hdr int, msg []byte) *jsAckMsg {
	var m *jsAckMsg
	am := jsAckMsgPool.Get()
	if am != nil {
		m = am.(*jsAckMsg)
	} else {
		m = &jsAckMsg{}
	}
	// When getting something from a pool it is critical that all fields are
	// initialized. Doing this way guarantees that if someone adds a field to
	// the structure, the compiler will fail the build if this line is not updated.
	(*m) = jsAckMsg{subj, reply, hdr, msg}
	return m
}

func (am *jsAckMsg) returnToPool() {
	if am == nil {
		return
	}
	am.subject, am.reply, am.hdr, am.msg = _EMPTY_, _EMPTY_, -1, nil
	jsAckMsgPool.Put(am)
}

// Push the ack message to the consumer's ackMsgs queue
func (o *consumer) pushAck(_ *subscription, c *client, _ *Account, subject, reply string, rmsg []byte) {
	atomic.AddInt64(&o.awl, 1)
	o.ackMsgs.push(newJSAckMsg(subject, reply, c.pa.hdr, copyBytes(rmsg)))
}

// Processes a message for the ack reply subject delivered with a message.
func (o *consumer) processAck(subject, reply string, hdr int, rmsg []byte) {
	defer atomic.AddInt64(&o.awl, -1)

	var msg []byte
	if hdr > 0 {
		msg = rmsg[hdr:]
	} else {
		msg = rmsg
	}

	sseq, dseq, dc, _, _ := ackReplyInfo(subject)

	skipAckReply := sseq == 0

	switch {
	case len(msg) == 0, bytes.Equal(msg, AckAck), bytes.Equal(msg, AckOK):
		if !o.processAckMsg(sseq, dseq, dc, reply, true) {
			// We handle replies for acks in updateAcks
			skipAckReply = true
		}
	case bytes.HasPrefix(msg, AckNext):
		o.processAckMsg(sseq, dseq, dc, _EMPTY_, true)
		o.processNextMsgRequest(reply, msg[len(AckNext):])
		skipAckReply = true
	case bytes.HasPrefix(msg, AckNak):
		o.processNak(sseq, dseq, dc, msg)
	case bytes.Equal(msg, AckProgress):
		o.progressUpdate(sseq)
	case bytes.HasPrefix(msg, AckTerm):
		var reason string
		if buf := msg[len(AckTerm):]; len(buf) > 0 {
			reason = string(bytes.TrimSpace(buf))
		}
		if !o.processTerm(sseq, dseq, dc, reason, reply) {
			// We handle replies for acks in updateAcks
			skipAckReply = true
		}
	}

	// Ack the ack if requested.
	if len(reply) > 0 && !skipAckReply {
		o.sendAckReply(reply)
	}
}

// Used to process a working update to delay redelivery.
func (o *consumer) progressUpdate(seq uint64) {
	o.mu.Lock()
	defer o.mu.Unlock()

	if p, ok := o.pending[seq]; ok {
		p.Timestamp = time.Now().UnixNano()
		// Update store system.
		o.updateDelivered(p.Sequence, seq, 1, p.Timestamp)
	}
}

// Lock should be held.
func (o *consumer) updateSkipped(seq uint64) {
	// Clustered mode and R>1 only.
	if o.node == nil || !o.isLeader() {
		return
	}
	var b [1 + 8]byte
	b[0] = byte(updateSkipOp)
	var le = binary.LittleEndian
	le.PutUint64(b[1:], seq)
	o.propose(b[:])
}

func (o *consumer) resetStartingSeq(seq uint64, reply string, internal bool) (uint64, bool, error) {
	o.mu.Lock()
	defer o.mu.Unlock()
	return o.resetStartingSeqLocked(seq, reply, internal)
}

// Lock should be held.
func (o *consumer) resetStartingSeqLocked(seq uint64, reply string, internal bool) (uint64, bool, error) {
	// Reset to a specific sequence, or back to the ack floor.
	if seq == 0 {
		seq = o.asflr + 1
	} else if o.cfg.DeliverPolicy == DeliverAll {
		// Always allowed.
		goto VALID
	} else if o.cfg.DeliverPolicy == DeliverByStartSequence {
		// Only allowed if not going below what's configured.
		if seq < o.cfg.OptStartSeq {
			return 0, false, errors.New("below start seq")
		}
		goto VALID
	} else if o.cfg.DeliverPolicy == DeliverByStartTime && o.mset != nil {
		// Only allowed if not going below what's configured.
		nseq := o.mset.store.GetSeqFromTime(*o.cfg.OptStartTime)
		if seq < nseq {
			return 0, false, errors.New("below start time")
		}
		goto VALID
	} else {
		return 0, false, errors.New("not allowed")
	}

VALID:
	// Must be a minimum of 1.
	if seq <= 0 {
		seq = 1
	}
	// The replicated path requires quorum first before the reset actually takes effect.
	if o.node != nil {
		if !o.isLeader() {
			return 0, false, nil
		}
		b := make([]byte, 1+8+len(reply))
		b[0] = byte(resetSeqOp)
		var le = binary.LittleEndian
		le.PutUint64(b[1:], seq)
		copy(b[1+8:], reply)
		o.propose(b[:])
		if reply != _EMPTY_ {
			if o.rsm == nil {
				o.rsm = make(map[string]bool, 1)
			}
			o.rsm[reply] = internal
		}
		return seq, false, nil
	}
	o.resetLocalStartingSeq(seq)
	if o.store != nil {
		o.store.Reset(seq - 1)
		// Cleanup messages that lost interest.
		if o.retention == InterestPolicy {
			if mset := o.mset; mset != nil {
				o.mu.Unlock()
				ss := mset.state()
				o.checkStateForInterestStream(&ss)
				o.mu.Lock()
			}
		}

		// Recalculate pending, and re-trigger message delivery.
		o.streamNumPending()
		o.signalNewMessages()
		return seq, true, nil
	}
	return seq, false, nil
}

// Lock should be held.
func (o *consumer) resetLocalStartingSeq(seq uint64) {
	o.pending, o.rdc = nil, nil
	o.rdq = nil
	o.rdqi.Empty()
	o.sseq, o.dseq = seq, 1
	o.adflr, o.asflr = o.dseq-1, o.sseq-1
	o.ldt, o.lat = time.Time{}, time.Time{}
}

func (o *consumer) loopAndForwardProposals(qch chan struct{}) {
	// On exit make sure we nil out pch.
	defer func() {
		o.mu.Lock()
		o.pch = nil
		o.mu.Unlock()
	}()

	o.mu.RLock()
	node, pch := o.node, o.pch
	o.mu.RUnlock()

	if node == nil || pch == nil {
		return
	}

	forwardProposals := func() error {
		o.mu.Lock()
		if o.node == nil || !o.node.Leader() {
			o.mu.Unlock()
			return errors.New("no longer leader")
		}
		proposal := o.phead
		o.phead, o.ptail = nil, nil
		o.mu.Unlock()
		// 256k max for now per batch.
		const maxBatch = 256 * 1024
		var entries []*Entry
		for sz := 0; proposal != nil; proposal = proposal.next {
			entries = append(entries, newEntry(EntryNormal, proposal.data))
			sz += len(proposal.data)
			if sz > maxBatch {
				node.ProposeMulti(entries)
				// We need to re-create `entries` because there is a reference
				// to it in the node's pae map.
				sz, entries = 0, nil
			}
		}
		if len(entries) > 0 {
			node.ProposeMulti(entries)
		}
		return nil
	}

	// In case we have anything pending on entry.
	forwardProposals()

	for {
		select {
		case <-qch:
			forwardProposals()
			return
		case <-pch:
			if err := forwardProposals(); err != nil {
				return
			}
		}
	}
}

// Lock should be held.
func (o *consumer) propose(entry []byte) {
	p := &proposal{data: entry}
	if o.phead == nil {
		o.phead = p
	} else {
		o.ptail.next = p
	}
	o.ptail = p

	// Kick our looper routine.
	select {
	case o.pch <- struct{}{}:
	default:
	}
}

// Lock should be held.
func (o *consumer) updateDelivered(dseq, sseq, dc uint64, ts int64) {
	// Clustered mode and R>1.
	if o.node != nil {
		// Inline for now, use variable compression.
		var b [4*binary.MaxVarintLen64 + 1]byte
		b[0] = byte(updateDeliveredOp)
		n := 1
		n += binary.PutUvarint(b[n:], dseq)
		n += binary.PutUvarint(b[n:], sseq)
		n += binary.PutUvarint(b[n:], dc)
		n += binary.PutVarint(b[n:], ts)
		o.propose(b[:n])
	} else if o.store != nil {
		o.store.UpdateDelivered(dseq, sseq, dc, ts)
	}
	// Update activity.
	o.ldt = time.Now()
}

// Used to remember a pending ack reply in a replicated consumer.
// Lock should be held.
func (o *consumer) addAckReply(sseq uint64, reply string) {
	if o.replies == nil {
		o.replies = make(map[uint64]string)
	}
	o.replies[sseq] = reply
}

// Used to remember messages that need to be sent for a replicated consumer, after delivered quorum.
// Lock should be held.
func (o *consumer) addReplicatedQueuedMsg(pmsg *jsPubMsg) {
	// Is not explicitly limited in size, but will at most hold maximum ack pending.
	if o.pendingDeliveries == nil {
		o.pendingDeliveries = make(map[uint64]*jsPubMsg)
	}
	o.pendingDeliveries[pmsg.seq] = pmsg

	// Is not explicitly limited in size, but will at most hold maximum waiting requests.
	if o.waitingDeliveries == nil {
		o.waitingDeliveries = make(map[string]*waitingDelivery)
	}
	if wd, ok := o.waitingDeliveries[pmsg.dsubj]; ok {
		wd.seq = pmsg.seq
	} else {
		wd := wdPool.Get().(*waitingDelivery)
		wd.seq = pmsg.seq
		o.waitingDeliveries[pmsg.dsubj] = wd
	}
}

// Lock should be held.
func (o *consumer) updateAcks(dseq, sseq uint64, reply string) {
	if o.node != nil {
		// Inline for now, use variable compression.
		var b [2*binary.MaxVarintLen64 + 1]byte
		b[0] = byte(updateAcksOp)
		n := 1
		n += binary.PutUvarint(b[n:], dseq)
		n += binary.PutUvarint(b[n:], sseq)
		o.propose(b[:n])
		if reply != _EMPTY_ {
			o.addAckReply(sseq, reply)
		}
	} else if o.store != nil {
		o.store.UpdateAcks(dseq, sseq)
		if reply != _EMPTY_ {
			// Already locked so send direct.
			o.outq.sendMsg(reply, nil)
		}
	}
	// Update activity.
	o.lat = time.Now()
}

// Communicate to the cluster an addition of a pending request.
// Lock should be held.
func (o *consumer) addClusterPendingRequest(reply string) {
	if o.node == nil || !o.pendingRequestsOk() {
		return
	}
	b := make([]byte, len(reply)+1)
	b[0] = byte(addPendingRequest)
	copy(b[1:], reply)
	o.propose(b)
}

// Communicate to the cluster a removal of a pending request.
// Lock should be held.
func (o *consumer) removeClusterPendingRequest(reply string) {
	if o.node == nil || !o.pendingRequestsOk() {
		return
	}
	b := make([]byte, len(reply)+1)
	b[0] = byte(removePendingRequest)
	copy(b[1:], reply)
	o.propose(b)
}

// Set whether or not we can send pending requests to followers.
func (o *consumer) setPendingRequestsOk(ok bool) {
	o.mu.Lock()
	o.prOk = ok
	o.mu.Unlock()
}

// Lock should be held.
func (o *consumer) pendingRequestsOk() bool {
	return o.prOk
}

// Set whether or not we can send info about pending pull requests to our group.
// Will require all peers have a minimum version.
func (o *consumer) checkAndSetPendingRequestsOk() {
	o.mu.RLock()
	s, isValid := o.srv, o.mset != nil
	o.mu.RUnlock()
	if !isValid {
		return
	}

	if ca := o.consumerAssignment(); ca != nil && len(ca.Group.Peers) > 1 {
		for _, pn := range ca.Group.Peers {
			if si, ok := s.nodeToInfo.Load(pn); ok {
				if !versionAtLeast(si.(nodeInfo).version, 2, 7, 1) {
					// We expect all of our peers to eventually be up to date.
					// So check again in awhile.
					time.AfterFunc(eventsHBInterval, func() { o.checkAndSetPendingRequestsOk() })
					o.setPendingRequestsOk(false)
					return
				}
			}
		}
	}
	o.setPendingRequestsOk(true)
}

// On leadership change make sure we alert the pending requests that they are no longer valid.
func (o *consumer) checkPendingRequests() {
	o.mu.Lock()
	defer o.mu.Unlock()
	if o.mset == nil || o.outq == nil {
		return
	}
	hdr := []byte("NATS/1.0 409 Leadership Change\r\n\r\n")
	for reply := range o.prm {
		o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
	}
	o.prm = nil
}

// This will release any pending pull requests if applicable.
// Should be called only by the leader being deleted or stopped.
// Lock should be held.
func (o *consumer) releaseAnyPendingRequests(isAssigned bool) {
	if o.mset == nil || o.outq == nil || o.waiting.len() == 0 {
		return
	}
	wq := o.waiting
	for wr := wq.head; wr != nil; {
		if !isAssigned {
			hdr := []byte("NATS/1.0 409 Consumer Deleted\r\n\r\n")
			o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
		}
		next := wr.next
		wr.recycle()
		wr = next
	}
	// Nil out old queue.
	o.waiting = nil
}

// Process a NAK.
func (o *consumer) processNak(sseq, dseq, dc uint64, nak []byte) {
	o.mu.Lock()
	defer o.mu.Unlock()

	// Check for out of range.
	if dseq <= o.adflr || dseq > o.dseq {
		return
	}
	// If we are explicit ack make sure this is still on our pending list.
	if _, ok := o.pending[sseq]; !ok {
		return
	}

	// Deliver an advisory
	e := JSConsumerDeliveryNakAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerDeliveryNakAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Stream:      o.stream,
		Consumer:    o.name,
		ConsumerSeq: dseq,
		StreamSeq:   sseq,
		Deliveries:  dc,
		Domain:      o.srv.getOpts().JetStreamDomain,
	}

	o.sendAdvisory(o.nakEventT, e)

	// Check to see if we have delays attached.
	if len(nak) > len(AckNak) {
		arg := bytes.TrimSpace(nak[len(AckNak):])
		if len(arg) > 0 {
			var d time.Duration
			var err error
			if arg[0] == '{' {
				var nd ConsumerNakOptions
				if err = json.Unmarshal(arg, &nd); err == nil {
					d = nd.Delay
				}
			} else {
				d, err = time.ParseDuration(string(arg))
			}
			if err != nil {
				// Treat this as normal NAK.
				o.srv.Warnf("JetStream consumer '%s > %s > %s' bad NAK delay value: %q", o.acc.Name, o.stream, o.name, arg)
			} else {
				// We have a parsed duration that the user wants us to wait before retrying.
				// Make sure we are not on the rdq.
				o.removeFromRedeliverQueue(sseq)
				if p, ok := o.pending[sseq]; ok {
					// now - ackWait is expired now, so offset from there.
					p.Timestamp = time.Now().Add(-o.cfg.AckWait).Add(d).UnixNano()
					// Update store system which will update followers as well.
					o.updateDelivered(p.Sequence, sseq, dc, p.Timestamp)
					if o.ptmr != nil {
						// Want checkPending to run and figure out the next timer ttl.
						// TODO(dlc) - We could optimize this maybe a bit more and track when we expect the timer to fire.
						o.resetPtmr(10 * time.Millisecond)
					}
				}
				// Nothing else for use to do now so return.
				return
			}
		}
	}

	// If already queued up also ignore.
	if !o.onRedeliverQueue(sseq) {
		o.addToRedeliverQueue(sseq)
	}

	o.signalNewMessages()
}

// Process a TERM
// Returns `true` if the ack was processed in place and the sender can now respond
// to the client, or `false` if there was an error or the ack is replicated (in which
// case the reply will be sent later).
func (o *consumer) processTerm(sseq, dseq, dc uint64, reason, reply string) bool {
	return o.processTermLocked(sseq, dseq, dc, reason, reply, true)
}

func (o *consumer) processTermLocked(sseq, dseq, dc uint64, reason, reply string, needLock bool) bool {
	// Treat like an ack to suppress redelivery.
	ackedInPlace := o.processAckMsgLocked(sseq, dseq, dc, reply, false, needLock)

	if needLock {
		o.mu.Lock()
		defer o.mu.Unlock()
	}

	// Deliver an advisory
	e := JSConsumerDeliveryTerminatedAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerDeliveryTerminatedAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Stream:      o.stream,
		Consumer:    o.name,
		ConsumerSeq: dseq,
		StreamSeq:   sseq,
		Deliveries:  dc,
		Reason:      reason,
		Domain:      o.srv.getOpts().JetStreamDomain,
	}

	subj := JSAdvisoryConsumerMsgTerminatedPre + "." + o.stream + "." + o.name
	o.sendAdvisory(subj, e)
	return ackedInPlace
}

// Introduce a small delay in when timer fires to check pending.
// Allows bursts to be treated in same time frame.
const ackWaitDelay = time.Millisecond

// ackWait returns how long to wait to fire the pending timer.
func (o *consumer) ackWait(next time.Duration) time.Duration {
	if next > 0 {
		return next + ackWaitDelay
	}
	return o.cfg.AckWait + ackWaitDelay
}

// Due to bug in calculation of sequences on restoring redelivered let's do quick sanity check.
// Lock should be held.
func (o *consumer) checkRedelivered() {
	var shouldUpdateState bool
	for sseq := range o.rdc {
		if sseq <= o.asflr {
			delete(o.rdc, sseq)
			o.removeFromRedeliverQueue(sseq)
			shouldUpdateState = true
		}
	}
	if shouldUpdateState {
		if err := o.writeStoreStateUnlocked(); err != nil && o.srv != nil && o.mset != nil && !o.closed {
			s, acc, mset, name := o.srv, o.acc, o.mset, o.name
			s.Warnf("Consumer '%s > %s > %s' error on write store state from check redelivered: %v", acc, mset.getCfgName(), name, err)
		}
	}
}

// This will restore the state from disk.
// Lock should be held.
func (o *consumer) readStoredState() error {
	if o.store == nil {
		return nil
	}
	state, err := o.store.State()
	if err == nil {
		o.applyState(state)
		if len(o.rdc) > 0 {
			o.checkRedelivered()
		}
	}
	return err
}

// Apply the consumer stored state.
// Lock should be held.
func (o *consumer) applyState(state *ConsumerState) {
	if state == nil {
		return
	}

	o.sseq = state.Delivered.Stream + 1
	o.dseq = state.Delivered.Consumer + 1
	o.adflr = state.AckFloor.Consumer
	o.asflr = state.AckFloor.Stream
	o.pending = state.Pending
	o.rdc = state.Redelivered

	// Setup tracking timer if we have restored pending.
	if o.isLeader() && len(o.pending) > 0 {
		// This is on startup or leader change. We want to check pending
		// sooner in case there are inconsistencies etc. Pick between 500ms - 1.5s
		delay := 500*time.Millisecond + time.Duration(rand.Int63n(1000))*time.Millisecond

		// If normal is lower than this just use that.
		if o.cfg.AckWait < delay {
			delay = o.ackWait(0)
		}
		o.resetPtmr(delay)
	}
}

// Sets our store state from another source. Used in clustered mode on snapshot restore.
// Lock should be held.
func (o *consumer) setStoreState(state *ConsumerState) error {
	if state == nil || o.store == nil {
		return nil
	}
	err := o.store.Update(state)
	if err == nil {
		o.applyState(state)
	} else if err == ErrStoreOldUpdate {
		// Our store already has a newer state, which is normal during recovery
		// when the consumer was loaded from disk before the meta snapshot state
		// was applied.
		return nil
	}
	return err
}

// Update our state to the store.
func (o *consumer) writeStoreState() error {
	o.mu.Lock()
	defer o.mu.Unlock()
	return o.writeStoreStateUnlocked()
}

// Update our state to the store.
// Lock should be held.
func (o *consumer) writeStoreStateUnlocked() error {
	if o.store == nil {
		return nil
	}
	state := ConsumerState{
		Delivered: SequencePair{
			Consumer: o.dseq - 1,
			Stream:   o.sseq - 1,
		},
		AckFloor: SequencePair{
			Consumer: o.adflr,
			Stream:   o.asflr,
		},
		Pending:     o.pending,
		Redelivered: o.rdc,
	}
	return o.store.Update(&state)
}

// Returns an initial info. Only applicable for non-clustered consumers.
// We will clear after we return it, so one shot.
func (o *consumer) initialInfo() *ConsumerInfo {
	o.mu.Lock()
	ici := o.ici
	o.ici = nil // gc friendly
	o.mu.Unlock()
	if ici == nil {
		ici = o.info()
	}
	return ici
}

// Clears our initial info.
// Used when we have a leader change in cluster mode but do not send a response.
func (o *consumer) clearInitialInfo() {
	o.mu.Lock()
	o.ici = nil // gc friendly
	o.mu.Unlock()
}

// Info returns our current consumer state.
func (o *consumer) info() *ConsumerInfo {
	return o.infoWithSnap(false)
}

func (o *consumer) infoWithSnap(snap bool) *ConsumerInfo {
	return o.infoWithSnapAndReply(snap, _EMPTY_)
}

func (o *consumer) infoWithSnapAndReply(snap bool, reply string) *ConsumerInfo {
	o.mu.Lock()
	mset := o.mset
	if o.closed || mset == nil || mset.srv == nil {
		o.mu.Unlock()
		return nil
	}
	js := o.js
	if js == nil {
		o.mu.Unlock()
		return nil
	}

	// Capture raftGroup.
	var rg *raftGroup
	if o.ca != nil {
		rg = o.ca.Group
	}

	priorityGroups := []PriorityGroupState{}
	// TODO(jrm): when we introduce supporting many priority groups, we need to update assigning `o.currentNuid` for each group.
	if len(o.cfg.PriorityGroups) > 0 {
		priorityGroups = append(priorityGroups, PriorityGroupState{
			Group:          o.cfg.PriorityGroups[0],
			PinnedClientID: o.currentPinId,
			PinnedTS:       o.pinnedTS,
		})
	}

	np, err := o.checkNumPending()
	if err != nil {
		o.mu.Unlock()
		return nil
	}

	dseq, sseq := o.dseq, o.sseq
	if dseq <= 0 {
		dseq = 1
	}
	if sseq <= 0 {
		sseq = 1
	}

	cfg := o.cfg
	info := &ConsumerInfo{
		Stream:  o.stream,
		Name:    o.name,
		Created: o.created,
		Config:  &cfg,
		Delivered: SequenceInfo{
			Consumer: dseq - 1,
			Stream:   sseq - 1,
		},
		AckFloor: SequenceInfo{
			Consumer: o.adflr,
			Stream:   o.asflr,
		},
		NumAckPending:  len(o.pending),
		NumRedelivered: len(o.rdc),
		NumPending:     np,
		PushBound:      o.isPushMode() && o.active,
		TimeStamp:      time.Now().UTC(),
		PriorityGroups: priorityGroups,
	}
	// Reset redelivered for MaxDeliver 1. Redeliveries are disabled so must not report it (is confusing otherwise).
	// The state does still keep track of these messages.
	if o.cfg.MaxDeliver == 1 {
		info.NumRedelivered = 0
	}
	if o.cfg.PauseUntil != nil {
		p := *o.cfg.PauseUntil
		if info.Paused = time.Now().Before(p); info.Paused {
			info.PauseRemaining = time.Until(p)
		}
	}

	// We always need to pull certain data from our store.
	if o.store != nil {
		state, err := o.store.BorrowState()
		if err != nil {
			o.mu.Unlock()
			return nil
		}

		// If we are the leader we could have o.sseq that is skipped ahead.
		// To maintain consistency in reporting (e.g. jsz) we always take the state for our delivered/ackfloor stream sequence.
		// Only use skipped ahead o.sseq if we're a new consumer and have not yet replicated this state yet.
		leader := o.isLeader()
		if !leader || o.store.HasState() {
			info.Delivered.Consumer, info.Delivered.Stream = state.Delivered.Consumer, state.Delivered.Stream
		}
		info.AckFloor.Consumer, info.AckFloor.Stream = state.AckFloor.Consumer, state.AckFloor.Stream
		if !leader {
			info.NumAckPending = len(state.Pending)
			info.NumRedelivered = len(state.Redelivered)
		}
	}

	// Adjust active based on non-zero etc. Also make UTC here.
	if !o.ldt.IsZero() {
		ldt := o.ldt.UTC() // This copies as well.
		info.Delivered.Last = &ldt
	}
	if !o.lat.IsZero() {
		lat := o.lat.UTC() // This copies as well.
		info.AckFloor.Last = &lat
	}

	// If we are a pull mode consumer, report on number of waiting requests.
	if o.isPullMode() {
		o.processWaiting(false)
		info.NumWaiting = o.waiting.len()
	}
	// If we were asked to snapshot do so here.
	if snap {
		o.ici = info
	}
	sysc := o.sysc
	o.mu.Unlock()

	// Do cluster.
	if rg != nil {
		info.Cluster = js.clusterInfo(rg)
	}

	// If we have a reply subject send the response here.
	if reply != _EMPTY_ && sysc != nil {
		sysc.sendInternalMsg(reply, _EMPTY_, nil, info)
	}

	return info
}

// Will signal us that new messages are available. Will break out of waiting.
func (o *consumer) signalNewMessages() {
	// Kick our new message channel
	select {
	case o.mch <- struct{}{}:
	default:
	}
}

// shouldSample lets us know if we are sampling metrics on acks.
func (o *consumer) shouldSample() bool {
	switch {
	case o.sfreq <= 0:
		return false
	case o.sfreq >= 100:
		return true
	}

	// TODO(ripienaar) this is a tad slow so we need to rethink here, however this will only
	// hit for those with sampling enabled and its not the default
	return rand.Int31n(100) <= o.sfreq
}

func (o *consumer) sampleAck(sseq, dseq, dc uint64) {
	if !o.shouldSample() {
		return
	}

	now := time.Now().UTC()
	unow := now.UnixNano()

	e := JSConsumerAckMetric{
		TypedEvent: TypedEvent{
			Type: JSConsumerAckMetricType,
			ID:   nuid.Next(),
			Time: now,
		},
		Stream:      o.stream,
		Consumer:    o.name,
		ConsumerSeq: dseq,
		StreamSeq:   sseq,
		Delay:       unow - o.pending[sseq].Timestamp,
		Deliveries:  dc,
		Domain:      o.srv.getOpts().JetStreamDomain,
	}

	o.sendAdvisory(o.ackEventT, e)
}

// Process an ACK.
// Returns `true` if the ack was processed in place and the sender can now respond
// to the client, or `false` if there was an error or the ack is replicated (in which
// case the reply will be sent later).
func (o *consumer) processAckMsg(sseq, dseq, dc uint64, reply string, doSample bool) bool {
	return o.processAckMsgLocked(sseq, dseq, dc, reply, doSample, true)
}

func (o *consumer) processAckMsgLocked(sseq, dseq, dc uint64, reply string, doSample bool, needLock bool) bool {
	lock := func() {
		if needLock {
			o.mu.Lock()
		}
	}
	unlock := func() {
		if needLock {
			o.mu.Unlock()
		}
	}
	lock()
	if o.closed {
		unlock()
		return false
	}

	mset := o.mset
	if mset == nil || mset.closed.Load() {
		unlock()
		return false
	}

	// Check if this ack is above the current pointer to our next to deliver.
	// Ignore if it's a flow-controlled consumer, its state could end up further ahead
	// since its state is not replicated before delivery.
	if sseq >= o.sseq && !o.cfg.FlowControl {
		// Let's make sure this is valid.
		// This is only received on the consumer leader, so should never be higher
		// than the last stream sequence. But could happen if we've just become
		// consumer leader, and we are not up-to-date on the stream yet.
		var ss StreamState
		mset.store.FastState(&ss)
		if sseq > ss.LastSeq {
			o.srv.Warnf("JetStream consumer '%s > %s > %s' ACK sequence %d past last stream sequence of %d",
				o.acc.Name, o.stream, o.name, sseq, ss.LastSeq)
			// FIXME(dlc) - For 2.11 onwards should we return an error here to the caller?
		}
		// Even though another leader must have delivered a message with this sequence, we must not adjust
		// the current pointer. This could otherwise result in a stuck consumer, where messages below this
		// sequence can't be redelivered, and we'll have incorrect pending state and ack floors.
		unlock()
		return false
	}

	// Let the owning stream know if we are interest or workqueue retention based.
	// If this consumer is clustered (o.node != nil) this will be handled by
	// processReplicatedAck after the ack has propagated.
	// If we're already holding the lock we can't ack in place, since that will
	// violate lock ordering with respect to the stream.
	ackInPlace := o.node == nil && o.retention != LimitsPolicy && needLock

	var sgap, floor uint64
	var needSignal bool

	switch o.cfg.AckPolicy {
	case AckExplicit:
		if p, ok := o.pending[sseq]; ok {
			if doSample {
				o.sampleAck(sseq, dseq, dc)
			}
			if o.maxp > 0 && len(o.pending) >= o.maxp {
				needSignal = true
			}
			delete(o.pending, sseq)
			// Use the original deliver sequence from our pending record.
			dseq = p.Sequence

			// Only move floors if we matched an existing pending.
			if len(o.pending) == 0 {
				o.adflr = o.dseq - 1
				o.asflr = o.sseq - 1
			} else if dseq == o.adflr+1 {
				o.adflr, o.asflr = dseq, sseq
				for ss := sseq + 1; ss < o.sseq; ss++ {
					if p, ok := o.pending[ss]; ok {
						if p.Sequence > 0 {
							o.adflr, o.asflr = p.Sequence-1, ss-1
						}
						break
					}
				}
			}
		}
		delete(o.rdc, sseq)
		o.removeFromRedeliverQueue(sseq)
	case AckAll, AckFlowControl:
		// no-op
		if dseq <= o.adflr || sseq <= o.asflr {
			unlock()
			// Return true to let caller respond back to the client.
			return true
		}
		if o.maxp > 0 && len(o.pending) >= o.maxp {
			needSignal = true
		}
		sgap = sseq - o.asflr
		floor = sseq // start at same and set lower as we go.
		o.adflr, o.asflr = dseq, sseq

		remove := func(seq uint64) {
			delete(o.pending, seq)
			delete(o.rdc, seq)
			o.removeFromRedeliverQueue(seq)
			if seq < floor {
				floor = seq
			}
		}
		// Determine if smarter to walk all of pending vs the sequence range.
		if sgap > uint64(len(o.pending)) {
			for seq := range o.pending {
				if seq <= sseq {
					remove(seq)
				}
			}
		} else {
			for seq := sseq; seq > sseq-sgap && len(o.pending) > 0; seq-- {
				remove(seq)
			}
		}
	case AckNone:
		// FIXME(dlc) - This is error but do we care?
		unlock()
		return ackInPlace
	}

	// No ack replication, so we set reply to "" so that updateAcks does not
	// send the reply. The caller will.
	if ackInPlace {
		reply = _EMPTY_
	}
	// Update underlying store.
	o.updateAcks(dseq, sseq, reply)
	unlock()

	if ackInPlace {
		if sgap > 1 {
			// FIXME(dlc) - This can very inefficient, will need to fix.
			for seq := sseq; seq >= floor; seq-- {
				mset.ackMsg(o, seq)
			}
		} else {
			mset.ackMsg(o, sseq)
		}
	}

	// If we had max ack pending set and were at limit we need to unblock ourselves.
	if needSignal {
		o.signalNewMessages()
	}
	return ackInPlace
}

// Determine if this is a truly filtered consumer. Modern clients will place filtered subjects
// even if the stream only has a single non-wildcard subject designation.
// Read lock should be held.
func (o *consumer) isFiltered() bool {
	if o.subjf == nil {
		return false
	}
	// If we are here we want to check if the filtered subject is
	// a direct match for our only listed subject.
	mset := o.mset
	if mset == nil {
		return true
	}

	// Protect access to mset.cfg with the cfgMu mutex.
	mset.cfgMu.RLock()
	msetSubjects := mset.cfg.Subjects
	mset.cfgMu.RUnlock()

	// `isFiltered` need to be performant, so we do
	// as any checks as possible to avoid unnecessary work.
	// Here we avoid iteration over slices if there is only one subject in stream
	// and one filter for the consumer.
	if len(msetSubjects) == 1 && len(o.subjf) == 1 {
		return msetSubjects[0] != o.subjf[0].subject
	}

	// if the list is not equal length, we can return early, as this is filtered.
	if len(msetSubjects) != len(o.subjf) {
		return true
	}

	// if in rare case scenario that user passed all stream subjects as consumer filters,
	// we need to do a more expensive operation.
	// reflect.DeepEqual would return false if the filters are the same, but in different order
	// so it can't be used here.
	cfilters := make(map[string]struct{}, len(o.subjf))
	for _, val := range o.subjf {
		cfilters[val.subject] = struct{}{}
	}
	for _, val := range msetSubjects {
		if _, ok := cfilters[val]; !ok {
			return true
		}
	}
	return false
}

// Check if we need an ack for this store seq.
// This is called for interest based retention streams to remove messages.
func (o *consumer) needAck(sseq uint64, subj string) bool {
	var needAck bool
	var asflr, osseq uint64
	var pending map[uint64]*Pending
	var rdc map[uint64]uint64

	o.mu.RLock()
	defer o.mu.RUnlock()

	isFiltered := o.isFiltered()
	if isFiltered && o.mset == nil {
		return false
	}

	// Check if we are filtered, and if so check if this is even applicable to us.
	if isFiltered {
		if subj == _EMPTY_ {
			var err error
			if subj, err = o.mset.store.SubjectForSeq(sseq); err != nil {
				return false
			}
		}
		if !o.isFilteredMatch(subj) {
			return false
		}
	}
	if o.isLeader() {
		asflr, osseq = o.asflr, o.sseq
		pending, rdc = o.pending, o.rdc
	} else {
		if o.store == nil {
			return false
		}
		state, err := o.store.BorrowState()
		if err != nil || state == nil {
			// Fall back to what we track internally for now.
			return sseq > o.asflr && !o.isFiltered()
		}
		// If loading state as here, the osseq is +1.
		asflr, osseq, pending, rdc = state.AckFloor.Stream, state.Delivered.Stream+1, state.Pending, state.Redelivered
	}

	switch o.cfg.AckPolicy {
	case AckNone, AckAll, AckFlowControl:
		needAck = sseq > asflr
	case AckExplicit:
		if sseq > asflr {
			if sseq >= osseq {
				needAck = true
			} else {
				_, needAck = pending[sseq]
			}
		}
	}

	// Finally check if redelivery of this message is tracked.
	// If the message is not pending, it should be preserved if it reached max delivery.
	if !needAck {
		_, needAck = rdc[sseq]
	}

	return needAck
}

type PriorityGroup struct {
	Group         string `json:"group,omitempty"`
	MinPending    int64  `json:"min_pending,omitempty"`
	MinAckPending int64  `json:"min_ack_pending,omitempty"`
	Id            string `json:"id,omitempty"`
	Priority      int    `json:"priority,omitempty"`
}

// Used in nextReqFromMsg, since the json.Unmarshal causes the request
// struct to escape to the heap always. This should reduce GC pressure.
var jsGetNextPool = sync.Pool{
	New: func() any {
		return &JSApiConsumerGetNextRequest{}
	},
}

// Helper for the next message requests.
func nextReqFromMsg(msg []byte) (time.Time, int, int, bool, time.Duration, time.Time, *PriorityGroup, error) {
	req := bytes.TrimSpace(msg)

	switch {
	case len(req) == 0:
		return time.Time{}, 1, 0, false, 0, time.Time{}, nil, nil

	case req[0] == '{':
		cr := jsGetNextPool.Get().(*JSApiConsumerGetNextRequest)
		defer func() {
			*cr = JSApiConsumerGetNextRequest{}
			jsGetNextPool.Put(cr)
		}()
		if err := json.Unmarshal(req, &cr); err != nil {
			return time.Time{}, -1, 0, false, 0, time.Time{}, nil, err
		}
		var hbt time.Time
		if cr.Heartbeat > 0 {
			if cr.Heartbeat*2 > cr.Expires {
				return time.Time{}, 1, 0, false, 0, time.Time{}, nil, errors.New("heartbeat value too large")
			}
			hbt = time.Now().Add(cr.Heartbeat)
		}
		priorityGroup := cr.PriorityGroup
		if cr.Expires == time.Duration(0) {
			return time.Time{}, cr.Batch, cr.MaxBytes, cr.NoWait, cr.Heartbeat, hbt, &priorityGroup, nil
		}
		return time.Now().Add(cr.Expires), cr.Batch, cr.MaxBytes, cr.NoWait, cr.Heartbeat, hbt, &priorityGroup, nil
	default:
		if n, err := strconv.Atoi(string(req)); err == nil {
			return time.Time{}, n, 0, false, 0, time.Time{}, nil, nil
		}
	}

	return time.Time{}, 1, 0, false, 0, time.Time{}, nil, nil
}

// Represents a request that is on the internal waiting queue
type waitingRequest struct {
	next          *waitingRequest
	acc           *Account
	interest      string
	reply         string
	n             int // For batching
	d             int // num delivered
	b             int // For max bytes tracking
	expires       time.Time
	received      time.Time
	hb            time.Duration
	hbt           time.Time
	noWait        bool
	priorityGroup *PriorityGroup
}

// sync.Pool for waiting requests.
var wrPool = sync.Pool{
	New: func() any {
		return new(waitingRequest)
	},
}

// Recycle this request. This request can not be accessed after this call.
func (wr *waitingRequest) recycleIfDone() bool {
	if wr != nil && wr.n <= 0 {
		wr.recycle()
		return true
	}
	return false
}

// Force a recycle.
func (wr *waitingRequest) recycle() {
	if wr != nil {
		wr.next, wr.acc, wr.interest, wr.reply = nil, nil, _EMPTY_, _EMPTY_
		wrPool.Put(wr)
	}
}

// Represents an (optional) request timeout that's sent after waiting for replicated deliveries.
type waitingDelivery struct {
	seq uint64
	pn  int // Pending messages.
	pb  int // Pending bytes.
}

// sync.Pool for waiting deliveries.
var wdPool = sync.Pool{
	New: func() any {
		return new(waitingDelivery)
	},
}

// Force a recycle.
func (wd *waitingDelivery) recycle() {
	if wd != nil {
		wd.seq, wd.pn, wd.pb = 0, 0, 0
		wdPool.Put(wd)
	}
}

// waiting queue for requests that are waiting for new messages to arrive.
type waitQueue struct {
	n, max int
	last   time.Time
	head   *waitingRequest
	tail   *waitingRequest
}

// Create a new ring buffer with at most max items.
func newWaitQueue(max int) *waitQueue {
	return &waitQueue{max: max}
}

var (
	errWaitQueueFull = errors.New("wait queue is full")
	errWaitQueueNil  = errors.New("wait queue is nil")
)

// insertSorted inserts wr at the correct position based on priority
func (wq *waitQueue) insertSorted(wr *waitingRequest) {

	// Handle empty queue
	if wq.head == nil {
		wq.head = wr
		wq.tail = wr
		wr.next = nil
		return
	}
	insertAtPosition(wr, wq)
}

func (wq *waitQueue) addPrioritized(wr *waitingRequest) error {
	if wq == nil {
		return errWaitQueueNil
	}
	if wq.isFull() {
		return errWaitQueueFull
	}

	wq.insertSorted(wr)
	wq.n++
	wq.last = wr.received
	return nil
}

// Adds in a new request.
func (wq *waitQueue) add(wr *waitingRequest) error {
	if wq == nil {
		return errWaitQueueNil
	}
	if wq.isFull() {
		return errWaitQueueFull
	}
	if wq.head == nil {
		wq.head = wr
	} else {
		wq.tail.next = wr
	}
	// Always set tail.
	wq.tail = wr
	// Make sure nil
	wr.next = nil

	// Track last active via when we receive a request.
	wq.last = wr.received
	wq.n++
	return nil
}

func (wq *waitQueue) isFull() bool {
	if wq == nil {
		return false
	}
	return wq.n == wq.max
}

func (wq *waitQueue) isEmpty() bool {
	if wq == nil {
		return true
	}
	return wq.n == 0
}

func (wq *waitQueue) len() int {
	if wq == nil {
		return 0
	}
	return wq.n
}

// Peek will return the next request waiting or nil if empty.
func (wq *waitQueue) peek() *waitingRequest {
	if wq == nil {
		return nil
	}
	return wq.head
}

func (wq *waitQueue) cycle() {
	wr := wq.peek()
	if wr != nil {
		// Always remove current now on a pop, and move to end if still valid.
		// If we were the only one don't need to remove since this can be a no-op.
		wq.removeCurrent()
		wq.add(wr)
	}
}

func (wq *waitQueue) popOrPopAndRequeue(priority PriorityPolicy) *waitingRequest {
	if wq == nil || wq.head == nil {
		return nil
	}

	if priority == PriorityPrioritized {
		return wq.popAndRequeue()
	}
	return wq.pop()
}

// pop will return the next request and move the read cursor.
// This will now place a request that still has pending items at the ends of the list.
func (wq *waitQueue) pop() *waitingRequest {
	wr := wq.peek()
	if wr != nil {
		wr.d++
		wr.n--
		// Always remove current now on a pop, and move to end if still valid.
		// If we were the only one don't need to remove since this can be a no-op.
		if wr.n > 0 && wq.n > 1 {
			wq.removeCurrent()
			wq.add(wr)
		} else if wr.n <= 0 {
			wq.removeCurrent()
		}
	}
	return wr
}

// popAndRequeue pops the head element and requeues it at the end of its priority group.
// This maintains FIFO order within the same priority level.
func (wq *waitQueue) popAndRequeue() *waitingRequest {
	if wq == nil || wq.head == nil {
		return nil
	}

	// Save the head
	wr := wq.head

	if wr == nil {
		return wr
	}

	wr.d++
	wr.n--

	if wr.n > 0 && wq.n > 1 {
		if wr.next == nil {
			return wr
		}
		wq.head = wq.head.next
		wr.next = nil

		insertAtPosition(wr, wq)

	} else if wr.n <= 0 {
		wq.removeCurrent()
		return wr

	}

	return wr
}

func insertAtPosition(wr *waitingRequest, wq *waitQueue) {
	priority := math.MaxInt32
	if wr.priorityGroup != nil {
		priority = wr.priorityGroup.Priority
	}

	var prev *waitingRequest
	current := wq.head
	for current != nil {
		currentPriority := math.MaxInt32
		if current.priorityGroup != nil {
			currentPriority = current.priorityGroup.Priority
		}
		if currentPriority > priority {
			break
		}
		prev = current
		current = current.next
	}

	if prev == nil {
		// All remaining elements have higher priority
		wr.next = wq.head
		wq.head = wr
	} else {
		wr.next = prev.next
		prev.next = wr
		if wr.next == nil {
			wq.tail = wr
		}
	}
}

// Removes the current read pointer (head FIFO) entry.
func (wq *waitQueue) removeCurrent() {
	wq.remove(nil, wq.head)
}

// Remove the wr element from the wait queue.
func (wq *waitQueue) remove(pre, wr *waitingRequest) {
	if wr == nil {
		return
	}
	if pre != nil {
		pre.next = wr.next
	} else if wr == wq.head {
		// We are removing head here.
		wq.head = wr.next
	}
	// Check if wr was our tail.
	if wr == wq.tail {
		// Check if we need to assign to pre.
		if wr.next == nil {
			wq.tail = pre
		} else {
			wq.tail = wr.next
		}
	}
	wq.n--
}

// Return the map of pending requests keyed by the reply subject.
// No-op if push consumer or invalid etc.
func (o *consumer) pendingRequests() map[string]*waitingRequest {
	if o.waiting == nil {
		return nil
	}
	wq, m := o.waiting, make(map[string]*waitingRequest)
	for wr := wq.head; wr != nil; wr = wr.next {
		m[wr.reply] = wr
	}

	return m
}

func (o *consumer) setPinnedTimer(priorityGroup string) {
	if o.pinnedTtl != nil {
		o.pinnedTtl.Reset(o.cfg.PinnedTTL)
	} else {
		o.pinnedTtl = time.AfterFunc(o.cfg.PinnedTTL, func() {
			o.mu.Lock()
			// Skip if already unset.
			if o.currentPinId == _EMPTY_ {
				o.mu.Unlock()
				return
			}
			o.unassignPinId()
			o.sendUnpinnedAdvisoryLocked(priorityGroup, "timeout")
			o.mu.Unlock()
			o.signalNewMessages()
		})
	}
}

// Lock should be held.
func (o *consumer) assignNewPinId(wr *waitingRequest) {
	if wr.priorityGroup == nil || wr.priorityGroup.Group == _EMPTY_ {
		return
	}
	o.currentPinId = nuid.Next()
	o.pinnedTS = time.Now().UTC()
	wr.priorityGroup.Id = o.currentPinId
	o.setPinnedTimer(wr.priorityGroup.Group)
	o.sendPinnedAdvisoryLocked(wr.priorityGroup.Group)
}

// Lock should be held.
func (o *consumer) unassignPinId() {
	o.currentPinId = _EMPTY_
	o.pinnedTS = time.Time{}
	if o.pinnedTtl != nil {
		o.pinnedTtl.Stop()
		o.pinnedTtl = nil
	}
}

// Return next waiting request. This will check for expirations but not noWait or interest.
// That will be handled by processWaiting.
// Lock should be held.
func (o *consumer) nextWaiting(sz int) *waitingRequest {
	if o.waiting == nil || o.waiting.isEmpty() {
		return nil
	}

	// Check if server needs to assign a new pin id.
	needNewPin := o.currentPinId == _EMPTY_ && o.cfg.PriorityPolicy == PriorityPinnedClient

	numCycled := 0
	for wr := o.waiting.peek(); !o.waiting.isEmpty(); wr = o.waiting.peek() {
		if wr == nil {
			break
		}
		// Check if we have max bytes set.
		if wr.b > 0 {
			if sz <= wr.b {
				wr.b -= sz
				// If we are right now at zero, set batch to 1 to deliver this one but stop after.
				if wr.b == 0 {
					wr.n = 1
				}
			} else {
				// Since we can't send that message to the requestor, we need to
				// notify that we are closing the request.
				const maxBytesT = "NATS/1.0 409 Message Size Exceeds MaxBytes\r\n%s: %d\r\n%s: %d\r\n\r\n"
				hdr := fmt.Appendf(nil, maxBytesT, JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
				o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
				// Remove the current one, no longer valid due to max bytes limit.
				o.waiting.removeCurrent()
				if o.node != nil {
					o.removeClusterPendingRequest(wr.reply)
				}
				wr.recycle()
				continue
			}
		}

		if wr.expires.IsZero() || time.Now().Before(wr.expires) {
			if needNewPin {
				if wr.priorityGroup.Id == _EMPTY_ {
					o.assignNewPinId(wr)
				} else {
					// There is pin id set, but not a matching one. Send a notification to the client and remove the request.
					// Probably this is the old pin id.
					hdr := fmt.Appendf(nil, "NATS/1.0 423 Nats-Wrong-Pin-Id\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
					o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
					o.waiting.removeCurrent()
					if o.node != nil {
						o.removeClusterPendingRequest(wr.reply)
					}
					wr.recycle()
					continue
				}
			} else if o.currentPinId != _EMPTY_ {
				// Check if we have a match on the currentNuid
				if wr.priorityGroup != nil && wr.priorityGroup.Id == o.currentPinId {
					// If we have a match, we do nothing here and will deliver the message later down the code path.
				} else if wr.priorityGroup.Id == _EMPTY_ {
					o.waiting.cycle()
					numCycled++
					if numCycled >= o.waiting.len() {
						return nil
					}
					continue
				} else {
					// There is pin id set, but not a matching one. Send a notification to the client and remove the request.
					hdr := fmt.Appendf(nil, "NATS/1.0 423 Nats-Wrong-Pin-Id\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
					o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
					o.waiting.removeCurrent()
					if o.node != nil {
						o.removeClusterPendingRequest(wr.reply)
					}
					wr.recycle()
					continue
				}
			}

			if o.cfg.PriorityPolicy == PriorityOverflow {
				if wr.priorityGroup != nil &&
					// If both limits are zero we don't cycle and the request will be fulfilled.
					(wr.priorityGroup.MinPending > 0 || wr.priorityGroup.MinAckPending > 0) &&
					// We need to check o.npc+1, because before calling nextWaiting, we do o.npc--
					// If one OR the other limit is exceeded, we want to fulfill the request.
					// This is an inverted check. For clarity, we check the positive condition and negate.
					!((wr.priorityGroup.MinPending > 0 && wr.priorityGroup.MinPending <= o.npc+1) ||
						(wr.priorityGroup.MinAckPending > 0 && wr.priorityGroup.MinAckPending <= int64(len(o.pending)))) {
					o.waiting.cycle()
					numCycled++
					// We're done cycling through the requests.
					if numCycled >= o.waiting.len() {
						return nil
					}
					continue
				}
			}
			if wr.acc.sl.HasInterest(wr.interest) {
				return o.waiting.popOrPopAndRequeue(o.cfg.PriorityPolicy)
			} else if time.Since(wr.received) < defaultGatewayRecentSubExpiration && (o.srv.leafNodeEnabled || o.srv.gateway.enabled) {
				return o.waiting.popOrPopAndRequeue(o.cfg.PriorityPolicy)
			} else if o.srv.gateway.enabled && o.srv.hasGatewayInterest(wr.acc.Name, wr.interest) {
				return o.waiting.popOrPopAndRequeue(o.cfg.PriorityPolicy)
			}
		} else {
			// We do check for expiration in `processWaiting`, but it is possible to hit the expiry here, and not there.
			rdWait := o.replicateDeliveries()
			if rdWait {
				// Check if we need to send the timeout after pending replicated deliveries, or can do so immediately.
				if wd, ok := o.waitingDeliveries[wr.reply]; !ok {
					rdWait = false
				} else {
					wd.pn, wd.pb = wr.n, wr.b
				}
			}
			if !rdWait {
				hdr := fmt.Appendf(nil, "NATS/1.0 408 Request Timeout\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
				o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
			}
			o.waiting.removeCurrent()
			if o.node != nil {
				o.removeClusterPendingRequest(wr.reply)
			}
			wr.recycle()
			continue

		}
		if wr.interest != wr.reply {
			const intExpT = "NATS/1.0 408 Interest Expired\r\n%s: %d\r\n%s: %d\r\n\r\n"
			hdr := fmt.Appendf(nil, intExpT, JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
			o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
		}
		// Remove the current one, no longer valid.
		o.waiting.removeCurrent()
		if o.node != nil {
			o.removeClusterPendingRequest(wr.reply)
		}
		wr.recycle()
	}

	return nil
}

// Next message request.
type nextMsgReq struct {
	reply string
	msg   []byte
}

var nextMsgReqPool sync.Pool

func newNextMsgReq(reply string, msg []byte) *nextMsgReq {
	var nmr *nextMsgReq
	m := nextMsgReqPool.Get()
	if m != nil {
		nmr = m.(*nextMsgReq)
	} else {
		nmr = &nextMsgReq{}
	}
	// When getting something from a pool it is critical that all fields are
	// initialized. Doing this way guarantees that if someone adds a field to
	// the structure, the compiler will fail the build if this line is not updated.
	(*nmr) = nextMsgReq{reply, msg}
	return nmr
}

func (nmr *nextMsgReq) returnToPool() {
	if nmr == nil {
		return
	}
	nmr.reply, nmr.msg = _EMPTY_, nil
	nextMsgReqPool.Put(nmr)
}

// processNextMsgReq will process a request for the next message available. A nil message payload means deliver
// a single message. If the payload is a formal request or a number parseable with Atoi(), then we will send a
// batch of messages without requiring another request to this endpoint, or an ACK.
func (o *consumer) processNextMsgReq(_ *subscription, c *client, _ *Account, _, reply string, rmsg []byte) {
	if reply == _EMPTY_ {
		return
	}

	// Short circuit error here.
	if o.nextMsgReqs == nil {
		hdr := []byte("NATS/1.0 409 Consumer is push based\r\n\r\n")
		o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
		return
	}

	hdr, msg := c.msgParts(rmsg)
	if errorOnRequiredApiLevel(hdr) {
		hdr = []byte("NATS/1.0 412 Required Api Level\r\n\r\n")
		o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
		return
	}
	o.nextMsgReqs.push(newNextMsgReq(reply, copyBytes(msg)))
}

// processResetReq will reset a consumer to a new starting sequence.
func (o *consumer) processResetReq(_ *subscription, c *client, a *Account, _, reply string, rmsg []byte) {
	if reply == _EMPTY_ {
		return
	}

	s := o.srv
	var resp = JSApiConsumerResetResponse{ApiResponse: ApiResponse{Type: JSApiConsumerResetResponseType}}

	hdr, msg := c.msgParts(rmsg)
	if errorOnRequiredApiLevel(hdr) {
		resp.Error = NewJSRequiredApiLevelError()
		s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
		return
	}

	// An empty message resets back to the ack floor, otherwise a custom sequence can be used.
	var req JSApiConsumerResetRequest
	if len(msg) > 0 {
		if err := json.Unmarshal(msg, &req); err != nil {
			resp.Error = NewJSInvalidJSONError(err)
			s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
			return
		}
	}
	resetSeq, canRespond, err := o.resetStartingSeq(req.Seq, reply, false)
	if err != nil {
		resp.Error = NewJSConsumerInvalidResetError(err)
		s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
	} else if canRespond {
		resp.ConsumerInfo = setDynamicConsumerInfoMetadata(o.info())
		resp.ResetSeq = resetSeq
		s.sendInternalAccountMsg(a, reply, s.jsonResponse(&resp))
	}
}

func (o *consumer) processNextMsgRequest(reply string, msg []byte) {
	o.mu.Lock()
	defer o.mu.Unlock()

	mset := o.mset
	if mset == nil {
		return
	}

	sendErr := func(status int, description string) {
		hdr := fmt.Appendf(nil, "NATS/1.0 %d %s\r\n\r\n", status, description)
		o.outq.send(newJSPubMsg(reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
	}

	if o.isPushMode() || o.waiting == nil {
		sendErr(409, "Consumer is push based")
		return
	}

	// Check payload here to see if they sent in batch size or a formal request.
	expires, batchSize, maxBytes, noWait, hb, hbt, priorityGroup, err := nextReqFromMsg(msg)
	if err != nil {
		sendErr(400, fmt.Sprintf("Bad Request - %v", err))
		return
	}

	// Check for request limits
	if o.cfg.MaxRequestBatch > 0 && batchSize > o.cfg.MaxRequestBatch {
		sendErr(409, fmt.Sprintf("Exceeded MaxRequestBatch of %d", o.cfg.MaxRequestBatch))
		return
	}

	if !expires.IsZero() && o.cfg.MaxRequestExpires > 0 && expires.After(time.Now().Add(o.cfg.MaxRequestExpires)) {
		sendErr(409, fmt.Sprintf("Exceeded MaxRequestExpires of %v", o.cfg.MaxRequestExpires))
		return
	}

	if maxBytes > 0 && o.cfg.MaxRequestMaxBytes > 0 && maxBytes > o.cfg.MaxRequestMaxBytes {
		sendErr(409, fmt.Sprintf("Exceeded MaxRequestMaxBytes of %v", o.cfg.MaxRequestMaxBytes))
		return
	}

	if priorityGroup != nil {
		if (priorityGroup.MinPending != 0 || priorityGroup.MinAckPending != 0) && o.cfg.PriorityPolicy != PriorityOverflow {
			sendErr(400, "Bad Request - Not a Overflow Priority consumer")
		}

		if priorityGroup.Id != _EMPTY_ && o.cfg.PriorityPolicy != PriorityPinnedClient {
			sendErr(400, "Bad Request - Not a Pinned Client Priority consumer")
		}
		if priorityGroup.Priority < 0 || priorityGroup.Priority > 9 {
			sendErr(400, "Bad Request - Priority must be between 0 and 9")
			return
		}
	}

	if priorityGroup != nil && o.cfg.PriorityPolicy != PriorityNone {
		if priorityGroup.Group == _EMPTY_ {
			sendErr(400, "Bad Request - Priority Group missing")
			return
		}
		if !slices.Contains(o.cfg.PriorityGroups, priorityGroup.Group) {
			sendErr(400, "Bad Request - Invalid Priority Group")
			return
		}

		if o.currentPinId != _EMPTY_ {
			if priorityGroup.Id == o.currentPinId {
				o.setPinnedTimer(priorityGroup.Group)
			} else if priorityGroup.Id != _EMPTY_ {
				sendErr(423, "Nats-Pin-Id mismatch")
				return
			}
		}
	}

	// If we have the max number of requests already pending try to expire.
	if o.waiting.isFull() {
		// Try to expire some of the requests.
		// We do not want to push too hard here so at maximum process once per sec.
		if time.Since(o.lwqic) > time.Second {
			o.processWaiting(false)
		}
	}

	// If the request is for noWait and we have pending requests already, check if we have room.
	if noWait {
		msgsPending := o.numPending() + uint64(len(o.rdq))
		// If no pending at all, decide what to do with request.
		// If no expires was set then fail.
		if msgsPending == 0 && expires.IsZero() {
			o.waiting.last = time.Now()
			sendErr(404, "No Messages")
			return
		}
		if msgsPending > 0 {
			_, _, batchPending, _ := o.processWaiting(false)
			if msgsPending < uint64(batchPending) {
				o.waiting.last = time.Now()
				sendErr(408, "Requests Pending")
				return
			}
		}
		// If we are here this should be considered a one-shot situation.
		// We will wait for expires but will return as soon as we have any messages.
	}

	// If we receive this request though an account export, we need to track that interest subject and account.
	acc, interest := trackDownAccountAndInterest(o.acc, reply)

	// Create a waiting request.
	wr := wrPool.Get().(*waitingRequest)
	wr.acc, wr.interest, wr.reply, wr.n, wr.d, wr.noWait, wr.expires, wr.hb, wr.hbt, wr.priorityGroup = acc, interest, reply, batchSize, 0, noWait, expires, hb, hbt, priorityGroup
	wr.b = maxBytes
	wr.received = time.Now()

	if o.cfg.PriorityPolicy == PriorityPrioritized {
		if err := o.waiting.addPrioritized(wr); err != nil {
			if hb == 0 {
				sendErr(409, "Exceeded MaxWaiting")
			}
			wr.recycle()
			return
		}
	} else {
		if err := o.waiting.add(wr); err != nil {
			// If the client has a heartbeat interval set, don't bother responding with a 409,
			// otherwise we can end up in a hot loop with the client re-requesting instead of
			// waiting for the missing heartbeats instead and retrying.
			if hb == 0 {
				sendErr(409, "Exceeded MaxWaiting")
			}
			wr.recycle()
			return
		}
	}
	o.signalNewMessages()
	// If we are clustered update our followers about this request.
	if o.node != nil {
		o.addClusterPendingRequest(wr.reply)
	}
}

func trackDownAccountAndInterest(acc *Account, interest string) (*Account, string) {
	for strings.HasPrefix(interest, replyPrefix) {
		oa := acc
		oa.mu.RLock()
		if oa.exports.responses == nil {
			oa.mu.RUnlock()
			break
		}
		si := oa.exports.responses[interest]
		if si == nil {
			oa.mu.RUnlock()
			break
		}
		acc, interest = si.acc, si.to
		oa.mu.RUnlock()
	}
	return acc, interest
}

// Return current delivery count for a given sequence.
func (o *consumer) deliveryCount(seq uint64) uint64 {
	if o.rdc == nil {
		return 1
	}
	if dc := o.rdc[seq]; dc >= 1 {
		return dc
	}
	return 1
}

// Increase the delivery count for this message.
// ONLY used on redelivery semantics.
// Lock should be held.
func (o *consumer) incDeliveryCount(sseq uint64) uint64 {
	if o.rdc == nil {
		o.rdc = make(map[uint64]uint64)
	}
	o.rdc[sseq] += 1
	return o.rdc[sseq] + 1
}

// Used if we have to adjust on failed delivery or bad lookups.
// Those failed attempts should not increase deliver count.
// Lock should be held.
func (o *consumer) decDeliveryCount(sseq uint64) {
	if o.rdc == nil {
		o.rdc = make(map[uint64]uint64)
	}
	o.rdc[sseq] -= 1
}

// send a delivery exceeded advisory.
func (o *consumer) notifyDeliveryExceeded(sseq, dc uint64) {
	e := JSConsumerDeliveryExceededAdvisory{
		TypedEvent: TypedEvent{
			Type: JSConsumerDeliveryExceededAdvisoryType,
			ID:   nuid.Next(),
			Time: time.Now().UTC(),
		},
		Stream:     o.stream,
		Consumer:   o.name,
		StreamSeq:  sseq,
		Deliveries: dc,
		Domain:     o.srv.getOpts().JetStreamDomain,
	}

	o.sendAdvisory(o.deliveryExcEventT, e)
}

// Check if the candidate subject matches a filter if its present.
// Lock should be held.
func (o *consumer) isFilteredMatch(subj string) bool {
	// No filter is automatic match.
	if o.subjf == nil {
		return true
	}
	for _, filter := range o.subjf {
		if !filter.hasWildcard && subj == filter.subject {
			return true
		}
	}
	// It's quicker to first check for non-wildcard filters, then
	// iterate again to check for subset match.
	tsa := [32]string{}
	tts := tokenizeSubjectIntoSlice(tsa[:0], subj)
	for _, filter := range o.subjf {
		if isSubsetMatchTokenized(tts, filter.tokenizedSubject) {
			return true
		}
	}
	return false
}

// Check if the candidate filter subject is equal to or a subset match
// of one of the filter subjects.
// Lock should be held.
func (o *consumer) isEqualOrSubsetMatch(subj string) bool {
	for _, filter := range o.subjf {
		if !filter.hasWildcard && subj == filter.subject {
			return true
		}
	}
	tsa := [32]string{}
	tts := tokenizeSubjectIntoSlice(tsa[:0], subj)
	for _, filter := range o.subjf {
		if isSubsetMatchTokenized(filter.tokenizedSubject, tts) {
			return true
		}
	}
	return false
}

var (
	errMaxAckPending = errors.New("max ack pending reached")
	errBadConsumer   = errors.New("consumer not valid")
	errNoInterest    = errors.New("consumer requires interest for delivery subject when ephemeral")
)

// Get next available message from underlying store.
// Is partition aware and redeliver aware.
// Lock should be held.
func (o *consumer) getNextMsg() (*jsPubMsg, uint64, error) {
	if o.mset == nil || o.mset.store == nil {
		return nil, 0, errBadConsumer
	}
	// Process redelivered messages before looking at possibly "skip list" (deliver last per subject)
	if o.hasRedeliveries() {
		var seq, dc uint64
		for seq = o.getNextToRedeliver(); seq > 0; seq = o.getNextToRedeliver() {
			dc = o.incDeliveryCount(seq)
			if o.maxdc > 0 && dc > o.maxdc {
				// Only send once
				if dc == o.maxdc+1 {
					o.notifyDeliveryExceeded(seq, dc-1)
				}
				// Make sure to remove from pending.
				if p, ok := o.pending[seq]; ok && p != nil {
					delete(o.pending, seq)
					o.updateDelivered(p.Sequence, seq, dc, p.Timestamp)
				}
				continue
			}
			pmsg := getJSPubMsgFromPool()
			sm, err := o.mset.store.LoadMsg(seq, &pmsg.StoreMsg)
			if sm == nil || err != nil {
				pmsg.returnToPool()
				pmsg = nil
				// Adjust back deliver count.
				o.decDeliveryCount(seq)
			}
			// Message was scheduled for redelivery but was removed in the meantime.
			if err == ErrStoreMsgNotFound || err == errDeletedMsg {
				// This is a race condition where the message is still in o.pending and
				// scheduled for redelivery, but it has been removed from the stream.
				// o.processTerm is called in a goroutine so would normally run. However,
				// if we get here this likely didn't fire, or we are replicated and changed leaders.
				// That will correct the pending state and delivery/ack floors, so just skip here.
				pmsg.returnToPool()
				pmsg = nil
				if p, ok := o.pending[seq]; ok {
					o.processTermLocked(seq, p.Sequence, dc-1, ackTermUnackedLimitsReason, _EMPTY_, false)
				}
				continue
			}
			return pmsg, dc, err
		}
	}

	// Check if we have max pending.
	if o.maxp > 0 && len(o.pending) >= o.maxp {
		// maxp only set when ack policy != AckNone and user set MaxAckPending
		// Stall if we have hit max pending.
		return nil, 0, errMaxAckPending
	}

	if o.hasSkipListPending() {
		seq := o.lss.seqs[0]
		if len(o.lss.seqs) == 1 {
			o.sseq = o.lss.resume
			o.lss = nil
			o.updateSkipped(o.sseq)
		} else {
			o.lss.seqs = o.lss.seqs[1:]
			o.sseq = seq
		}
		pmsg := getJSPubMsgFromPool()
		sm, err := o.mset.store.LoadMsg(seq, &pmsg.StoreMsg)
		if sm == nil || err != nil {
			pmsg.returnToPool()
			pmsg = nil
		}
		o.sseq++
		return pmsg, 1, err
	}

	var sseq uint64
	var err error
	var sm *StoreMsg
	var pmsg = getJSPubMsgFromPool()

	// Grab next message applicable to us.
	filters, subjf, fseq := o.filters, o.subjf, o.sseq
	// Check if we are multi-filtered or not.
	if filters != nil {
		sm, sseq, err = o.mset.store.LoadNextMsgMulti(filters, fseq, &pmsg.StoreMsg)
	} else if len(subjf) > 0 { // Means single filtered subject since o.filters means > 1.
		filter, wc := subjf[0].subject, subjf[0].hasWildcard
		sm, sseq, err = o.mset.store.LoadNextMsg(filter, wc, fseq, &pmsg.StoreMsg)
	} else {
		// No filter here.
		sm, sseq, err = o.mset.store.LoadNextMsg(_EMPTY_, false, fseq, &pmsg.StoreMsg)
	}
	if sm == nil {
		pmsg.returnToPool()
		pmsg = nil
	}
	// Check if we should move our o.sseq.
	if sseq >= o.sseq {
		// If we are moving step by step then sseq == o.sseq.
		// If we have jumped we should update skipped for other replicas.
		if sseq != o.sseq && err == ErrStoreEOF {
			o.updateSkipped(sseq + 1)
		}
		o.sseq = sseq + 1
	}
	return pmsg, 1, err
}

// Will check for expiration and lack of interest on waiting requests.
// Will also do any heartbeats and return the next expiration or HB interval.
func (o *consumer) processWaiting(eos bool) (int, int, int, time.Time) {
	var fexp time.Time
	if o.srv == nil || o.waiting.isEmpty() {
		return 0, 0, 0, fexp
	}
	// Mark our last check time.
	o.lwqic = time.Now()

	var expired, brp int
	s, now := o.srv, time.Now()

	wq := o.waiting
	remove := func(pre, wr *waitingRequest) *waitingRequest {
		expired++
		if o.node != nil {
			o.removeClusterPendingRequest(wr.reply)
		}
		next := wr.next
		wq.remove(pre, wr)
		wr.recycle()
		return next
	}

	var pre *waitingRequest
	for wr := wq.head; wr != nil; {
		// Check expiration.
		expires := !wr.expires.IsZero() && now.After(wr.expires)
		if (eos && wr.noWait) || expires {
			rdWait := o.replicateDeliveries()
			if rdWait {
				// Check if we need to send the timeout after pending replicated deliveries, or can do so immediately.
				if wd, ok := o.waitingDeliveries[wr.reply]; !ok {
					rdWait = false
				} else {
					wd.pn, wd.pb = wr.n, wr.b
				}
				// If we still need to wait for replicated deliveries, remove from waiting list.
				if rdWait {
					wr = remove(pre, wr)
					continue
				}
			}
			// Normally it's a timeout.
			if expires {
				hdr := fmt.Appendf(nil, "NATS/1.0 408 Request Timeout\r\n%s: %d\r\n%s: %d\r\n\r\n", JSPullRequestPendingMsgs, wr.n, JSPullRequestPendingBytes, wr.b)
				o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
				wr = remove(pre, wr)
				continue
			} else if wr.expires.IsZero() || wr.d > 0 {
				// But if we're NoWait without expiry, we've reached the end of the stream, and we've not delivered any messages.
				// Return no messages instead, which is the same as if we'd rejected the pull request initially.
				hdr := fmt.Appendf(nil, "NATS/1.0 404 No Messages\r\n\r\n")
				o.outq.send(newJSPubMsg(wr.reply, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
				wr = remove(pre, wr)
				continue
			}
		}
		// Now check interest.
		interest := wr.acc.sl.HasInterest(wr.interest)
		if !interest && (s.leafNodeEnabled || s.gateway.enabled) {
			// If we are here check on gateways and leaf nodes (as they can mask gateways on the other end).
			// If we have interest or the request is too young break and do not expire.
			if time.Since(wr.received) < defaultGatewayRecentSubExpiration {
				interest = true
			} else if s.gateway.enabled && s.hasGatewayInterest(wr.acc.Name, wr.interest) {
				interest = true
			}
		}
		// Check if we have interest.
		if !interest {
			// No more interest here so go ahead and remove this one from our list.
			wr = remove(pre, wr)
			continue
		}

		// If interest, update batch pending requests counter and update fexp timer.
		brp += wr.n
		if !wr.hbt.IsZero() {
			if now.After(wr.hbt) {
				// Fire off a heartbeat here.
				o.sendIdleHeartbeat(wr.reply)
				// Update next HB.
				wr.hbt = now.Add(wr.hb)
			}
			if fexp.IsZero() || wr.hbt.Before(fexp) {
				fexp = wr.hbt
			}
		}
		if !wr.expires.IsZero() && (fexp.IsZero() || wr.expires.Before(fexp)) {
			fexp = wr.expires
		}
		// Update pre and wr here.
		pre = wr
		wr = wr.next
	}

	return expired, wq.len(), brp, fexp
}

// Will check to make sure those waiting still have registered interest.
func (o *consumer) checkWaitingForInterest() bool {
	o.processWaiting(true)
	return o.waiting.len() > 0
}

// Lock should be held.
func (o *consumer) hbTimer() (time.Duration, *time.Timer) {
	if o.cfg.Heartbeat == 0 {
		return 0, nil
	}
	return o.cfg.Heartbeat, time.NewTimer(o.cfg.Heartbeat)
}

// Check here for conditions when our ack floor may have drifted below the streams first sequence.
// In general this is accounted for in normal operations, but if the consumer misses the signal from
// the stream it will not clear the message and move the ack state.
// Should only be called from consumer leader.
func (o *consumer) checkAckFloor() {
	o.mu.RLock()
	mset, closed, asflr, numPending := o.mset, o.closed, o.asflr, len(o.pending)
	o.mu.RUnlock()

	if asflr == 0 || closed || mset == nil {
		return
	}

	var ss StreamState
	mset.store.FastState(&ss)

	// If our floor is equal or greater that is normal and nothing for us to do.
	if ss.FirstSeq == 0 || asflr >= ss.FirstSeq-1 {
		return
	}

	// Check which linear space is less to walk.
	if ss.FirstSeq-asflr-1 < uint64(numPending) {
		// Process all messages that no longer exist.
		for seq := asflr + 1; seq < ss.FirstSeq; seq++ {
			// Check if this message was pending.
			o.mu.RLock()
			p, isPending := o.pending[seq]
			rdc := o.deliveryCount(seq)
			o.mu.RUnlock()
			// If it was pending for us, get rid of it.
			if isPending {
				o.processTerm(seq, p.Sequence, rdc, ackTermLimitsReason, _EMPTY_)
			}
		}
	} else if numPending > 0 {
		// here it is shorter to walk pending.
		// toTerm is seq, dseq, rcd for each entry.
		toTerm := make([]uint64, 0, numPending*3)
		o.mu.RLock()
		for seq, p := range o.pending {
			if seq < ss.FirstSeq {
				var dseq uint64 = 1
				if p != nil {
					dseq = p.Sequence
				}
				rdc := o.deliveryCount(seq)
				toTerm = append(toTerm, seq, dseq, rdc)
			}
		}
		o.mu.RUnlock()

		for i := 0; i < len(toTerm); i += 3 {
			seq, dseq, rdc := toTerm[i], toTerm[i+1], toTerm[i+2]
			o.processTerm(seq, dseq, rdc, ackTermLimitsReason, _EMPTY_)
		}
	}

	// Do one final check here.
	o.mu.Lock()
	defer o.mu.Unlock()

	// If we are closed do not change anything and simply return.
	if o.closed {
		return
	}

	// If we are here, and this should be rare, we still are off with our ack floor.
	// We will make sure we are not doing un-necessary work here if only off by a bit
	// since this could be normal for a high activity wq or stream.
	// We will set it explicitly to 1 behind our current lowest in pending, or if
	// pending is empty, to our current delivered -1.
	const minOffThreshold = 50
	if ss.FirstSeq >= minOffThreshold && o.asflr < ss.FirstSeq-minOffThreshold {
		var psseq, pdseq uint64
		for seq, p := range o.pending {
			if psseq == 0 || seq < psseq {
				psseq, pdseq = seq, p.Sequence
			}
		}
		// If we still have none, set to current delivered -1.
		if psseq == 0 {
			psseq, pdseq = o.sseq-1, o.dseq-1
			// If still not adjusted.
			if psseq < ss.FirstSeq-1 {
				psseq = ss.FirstSeq - 1
			}
		} else {
			// Since this was set via the pending, we should not include
			// it directly but set floors to -1.
			psseq, pdseq = psseq-1, pdseq-1
		}
		o.asflr, o.adflr = psseq, pdseq
	}
}

func (o *consumer) processInboundAcks(qch chan struct{}) {
	// Grab the server lock to watch for server quit.
	o.mu.RLock()
	s, mset := o.srv, o.mset
	hasInactiveThresh := o.cfg.InactiveThreshold > 0

	o.mu.RUnlock()

	if s == nil || mset == nil {
		return
	}

	// We will check this on entry and periodically.
	o.checkAckFloor()

	// How often we will check for ack floor drift.
	// Spread these out for large numbers on a server restart.
	delta := time.Duration(rand.Int63n(int64(time.Minute)))
	ticker := time.NewTicker(time.Minute + delta)
	defer ticker.Stop()

	for {
		select {
		case <-o.ackMsgs.ch:
			// If we have an inactiveThreshold set, mark our activity.
			// Do this before processing acks, otherwise we might race if there are no pending messages
			// anymore and the inactivity threshold kicks in before we're able to mark activity.
			if hasInactiveThresh {
				o.suppressDeletion()
			}

			acks := o.ackMsgs.pop()
			for _, ack := range acks {
				o.processAck(ack.subject, ack.reply, ack.hdr, ack.msg)
				ack.returnToPool()
			}
			o.ackMsgs.recycle(&acks)
		case <-ticker.C:
			o.checkAckFloor()
		case <-qch:
			return
		case <-s.quitCh:
			return
		}
	}
}

// Process inbound next message requests.
func (o *consumer) processInboundNextMsgReqs(qch chan struct{}) {
	// Grab the server lock to watch for server quit.
	o.mu.RLock()
	s := o.srv
	o.mu.RUnlock()

	for {
		select {
		case <-o.nextMsgReqs.ch:
			reqs := o.nextMsgReqs.pop()
			for _, req := range reqs {
				o.processNextMsgRequest(req.reply, req.msg)
				req.returnToPool()
			}
			o.nextMsgReqs.recycle(&reqs)
		case <-qch:
			return
		case <-s.quitCh:
			return
		}
	}
}

// Suppress auto cleanup on ack activity of any kind.
func (o *consumer) suppressDeletion() {
	o.mu.Lock()
	defer o.mu.Unlock()

	if o.closed {
		return
	}

	if o.isPushMode() && o.dtmr != nil {
		// if dtmr is not nil we have started the countdown, simply reset to threshold.
		o.dtmr.Reset(o.dthresh)
	} else if o.isPullMode() && o.waiting != nil {
		// Pull mode always has timer running, update last on waiting queue.
		o.waiting.last = time.Now()
		if o.dtmr != nil {
			o.dtmr.Reset(o.dthresh)
		}
	}
}

// loopAndGatherMsgs waits for messages for the consumer. qch is the quit channel,
// upch is the unpause channel which fires when the PauseUntil deadline is reached.
func (o *consumer) loopAndGatherMsgs(qch chan struct{}) {
	// On startup check to see if we are in a reply situation where replay policy is not instant.
	var (
		lts  int64 // last time stamp seen, used for replay.
		lseq uint64
	)

	o.mu.RLock()
	mset := o.mset
	getLSeq := o.replay
	o.mu.RUnlock()
	// consumer is closed when mset is set to nil.
	if mset == nil {
		return
	}
	if getLSeq {
		lseq = mset.state().LastSeq
	}

	o.mu.Lock()
	s := o.srv
	// need to check again if consumer is closed
	if o.mset == nil {
		o.mu.Unlock()
		return
	}
	// For idle heartbeat support.
	var hbc <-chan time.Time
	hbd, hb := o.hbTimer()
	if hb != nil {
		hbc = hb.C
	}
	// Interest changes.
	inch := o.inch
	o.mu.Unlock()

	// Grab the stream's retention policy and name
	mset.cfgMu.RLock()
	stream, rp := mset.cfg.Name, mset.cfg.Retention
	mset.cfgMu.RUnlock()

	var err error

	// Deliver all the msgs we have now, once done or on a condition, we wait for new ones.
	for {
		var (
			pmsg     *jsPubMsg
			dc       uint64
			dsubj    string
			ackReply string
			delay    time.Duration
			sz       int
			wrn, wrb int
		)

		o.mu.Lock()

		// consumer is closed when mset is set to nil.
		if o.closed || o.mset == nil {
			o.mu.Unlock()
			return
		}

		// Clear last error.
		err = nil

		// If the consumer is paused then stop sending.
		if o.cfg.PauseUntil != nil && !o.cfg.PauseUntil.IsZero() && time.Now().Before(*o.cfg.PauseUntil) {
			// If the consumer is paused and we haven't reached the deadline yet then
			// go back to waiting.
			goto waitForMsgs
		}

		// If we are in push mode and not active or under flowcontrol let's stop sending.
		if o.isPushMode() {
			if !o.active || (o.maxpb > 0 && o.pbytes > o.maxpb) {
				goto waitForMsgs
			}
		} else if o.waiting.isEmpty() {
			// If we are in pull mode and no one is waiting already break and wait.
			goto waitForMsgs
		}

		// Grab our next msg.
		pmsg, dc, err = o.getNextMsg()

		// We can release the lock now under getNextMsg so need to check this condition again here.
		if o.closed || o.mset == nil {
			o.mu.Unlock()
			return
		}

		// On error either wait or return.
		if err != nil || pmsg == nil {
			// On EOF we can optionally fast sync num pending state.
			if err == ErrStoreEOF {
				o.checkNumPendingOnEOF()
			}
			if err == ErrStoreMsgNotFound || err == errDeletedMsg || err == ErrStoreEOF || err == errMaxAckPending {
				goto waitForMsgs
			} else {
				if pmsg != nil {
					s.Errorf("Received an error looking up message with sequence %d for consumer '%s > %s > %s': %v",
						pmsg.seq, o.mset.acc, stream, o.cfg.Name, err)
				} else {
					s.Errorf("Received an error looking up message for consumer '%s > %s > %s': %v",
						o.mset.acc, stream, o.cfg.Name, err)
				}
				goto waitForMsgs
			}
		}

		// Update our cached num pending here first.
		if dc == 1 {
			o.npc--
		}
		// Pre-calculate ackReply
		ackReply = o.ackReply(pmsg.seq, o.dseq, dc, pmsg.ts, o.numPending())

		// If headers only do not send msg payload.
		// Add in msg size itself as header.
		if o.cfg.HeadersOnly {
			convertToHeadersOnly(pmsg)
		}
		// Calculate payload size. This can be calculated on client side.
		// We do not include transport subject here since not generally known on client.
		sz = len(pmsg.subj) + len(ackReply) + len(pmsg.hdr) + len(pmsg.msg)

		if o.isPushMode() {
			dsubj = o.dsubj
		} else if wr := o.nextWaiting(sz); wr != nil {
			wrn, wrb = wr.n, wr.b
			dsubj = wr.reply
			if o.cfg.PriorityPolicy == PriorityPinnedClient {
				pmsg.hdr = genHeader(pmsg.hdr, JSPullRequestNatsPinId, o.currentPinId)
				pmsg.buf = append(pmsg.hdr, pmsg.msg...)
				sz = len(pmsg.subj) + len(ackReply) + len(pmsg.hdr) + len(pmsg.msg)
			}
			if done := wr.recycleIfDone(); done && o.node != nil {
				o.removeClusterPendingRequest(dsubj)
			} else if !done && wr.hb > 0 {
				wr.hbt = time.Now().Add(wr.hb)
			}
		} else {
			// We will redo this one as long as this is not a redelivery.
			// Need to also test that this is not going backwards since if
			// we fail to deliver we can end up here from rdq but we do not
			// want to decrement o.sseq if that is the case.
			if dc == 1 && pmsg.seq == o.sseq-1 {
				o.sseq--
				o.npc++
			} else if !o.onRedeliverQueue(pmsg.seq) {
				// We are not on the rdq so decrement the delivery count
				// and add it back.
				o.decDeliveryCount(pmsg.seq)
				o.addToRedeliverQueue(pmsg.seq)
			}
			pmsg.returnToPool()
			pmsg = nil
			goto waitForMsgs
		}

		// If we are in a replay scenario and have not caught up check if we need to delay here.
		if o.replay && lts > 0 {
			if delay = time.Duration(pmsg.ts - lts); delay > time.Millisecond {
				o.mu.Unlock()
				select {
				case <-qch:
					pmsg.returnToPool()
					pmsg = nil
					return
				case <-time.After(delay):
				}
				o.mu.Lock()
			}
		}

		// Track this regardless.
		lts = pmsg.ts

		// If we have a rate limit set make sure we check that here.
		if o.rlimit != nil {
			now := time.Now()
			r := o.rlimit.ReserveN(now, sz)
			delay := r.DelayFrom(now)
			if delay > 0 {
				o.mu.Unlock()
				select {
				case <-qch:
					pmsg.returnToPool()
					pmsg = nil
					return
				case <-time.After(delay):
				}
				o.mu.Lock()
			}
		}

		// Do actual delivery.
		o.deliverMsg(dsubj, ackReply, pmsg, dc, rp)

		// If given request fulfilled batch size, but there are still pending bytes, send information about it.
		if wrn <= 0 && wrb > 0 {
			msg := fmt.Appendf(nil, JsPullRequestRemainingBytesT, JSPullRequestPendingMsgs, wrn, JSPullRequestPendingBytes, wrb)
			o.outq.send(newJSPubMsg(dsubj, _EMPTY_, _EMPTY_, msg, nil, nil, 0))
		}
		// Reset our idle heartbeat timer if set.
		if hb != nil {
			hb.Reset(hbd)
		}

		o.mu.Unlock()
		continue

	waitForMsgs:
		// If we were in a replay state check to see if we are caught up. If so clear.
		if o.replay && o.sseq > lseq {
			o.replay = false
		}

		// Make sure to process any expired requests that are pending.
		var wrExp <-chan time.Time
		if o.isPullMode() {
			// Dont expire oneshots if we are here because of max ack pending limit.
			_, _, _, fexp := o.processWaiting(err != errMaxAckPending)
			if !fexp.IsZero() {
				expires := time.Until(fexp)
				if expires <= 0 {
					expires = time.Millisecond
				}
				wrExp = time.NewTimer(expires).C
			}
		}

		// We will wait here for new messages to arrive.
		mch, odsubj := o.mch, o.cfg.DeliverSubject
		o.mu.Unlock()

		select {
		case <-mch:
			// Messages are waiting.
		case interest := <-inch:
			// inch can be nil on pull-based, but then this will
			// just block and not fire.
			o.updateDeliveryInterest(interest)
		case <-qch:
			return
		case <-wrExp:
			o.mu.Lock()
			o.processWaiting(true)
			o.mu.Unlock()
		case <-hbc:
			if o.isActive() {
				o.mu.Lock()
				o.sendIdleHeartbeat(odsubj)
				// Send flow control on EOS if it's used for acknowledgements.
				if o.cfg.AckPolicy == AckFlowControl && len(o.pending) > 0 && o.fcid == _EMPTY_ {
					o.sendFlowControl()
				}
				o.mu.Unlock()
			}
			// Reset our idle heartbeat timer.
			hb.Reset(hbd)
		}
	}
}

// Lock should be held.
func (o *consumer) sendIdleHeartbeat(subj string) {
	const t = "NATS/1.0 100 Idle Heartbeat\r\n%s: %d\r\n%s: %d\r\n\r\n"
	sseq, dseq := o.sseq-1, o.dseq-1
	hdr := fmt.Appendf(nil, t, JSLastConsumerSeq, dseq, JSLastStreamSeq, sseq)
	if fcp := o.fcid; fcp != _EMPTY_ {
		// Add in that we are stalled on flow control here.
		addOn := fmt.Appendf(nil, "%s: %s\r\n\r\n", JSConsumerStalled, fcp)
		hdr = append(hdr[:len(hdr)-LEN_CR_LF], []byte(addOn)...)
	}
	o.outq.send(newJSPubMsg(subj, _EMPTY_, _EMPTY_, hdr, nil, nil, 0))
}

func (o *consumer) ackReply(sseq, dseq, dc uint64, ts int64, pending uint64) string {
	if o.useV2Ack {
		return fmt.Sprintf(o.ackReplyT, dc, sseq, dseq, ts, pending)
	}
	return fmt.Sprintf(o.ackReplyOldT, dc, sseq, dseq, ts, pending)
}

// Used mostly for testing. Sets max pending bytes for flow control setups.
func (o *consumer) setMaxPendingBytes(limit int) {
	o.pblimit = limit
	o.maxpb = limit / 16
	if o.maxpb == 0 {
		o.maxpb = 1
	}
}

// Does some sanity checks to see if we should re-calculate.
// Since there is a race when decrementing when there is contention at the beginning of the stream.
// The race is a getNextMsg skips a deleted msg, and then the decStreamPending call fires.
// This does some quick sanity checks to see if we should re-calculate num pending.
// Lock should be held.
func (o *consumer) checkNumPending() (uint64, error) {
	if o.mset != nil && o.mset.store != nil {
		var state StreamState
		o.mset.store.FastState(&state)
		npc := o.numPending()
		// Make sure we can't report more messages than there are.
		// TODO(nat): It's not great that this means consumer info has side effects,
		// since we can't know whether anyone will call it or not. The previous num
		// pending calculation that this replaces had the same problem though.
		if o.sseq > state.LastSeq {
			o.npc = 0
		} else if npc > 0 {
			o.npc = int64(min(npc, state.Msgs, state.LastSeq-o.sseq+1))
		}
	}
	return o.numPending(), nil
}

// Lock should be held.
func (o *consumer) numPending() uint64 {
	if o.npc < 0 {
		return 0
	}
	return uint64(o.npc)
}

// This will do a quick sanity check on num pending when we encounter
// and EOF in the loop and gather.
// Lock should be held.
func (o *consumer) checkNumPendingOnEOF() {
	if o.mset == nil {
		return
	}
	var state StreamState
	o.mset.store.FastState(&state)
	if o.sseq > state.LastSeq && o.npc != 0 {
		// We know here we can reset our running state for num pending.
		o.npc, o.npf = 0, state.LastSeq
	}
}

// Call into streamNumPending after acquiring the consumer lock.
func (o *consumer) streamNumPendingLocked() (uint64, error) {
	o.mu.Lock()
	defer o.mu.Unlock()
	return o.streamNumPending()
}

// Will force a set from the stream store of num pending.
// Depends on delivery policy, for last per subject we calculate differently.
// Lock should be held.
func (o *consumer) streamNumPending() (uint64, error) {
	if o.mset == nil || o.mset.store == nil {
		o.npc, o.npf = 0, 0
		return 0, nil
	}
	npc, npf, err := o.calculateNumPending()
	if err != nil {
		return 0, err
	}
	o.npc, o.npf = int64(npc), npf
	return o.numPending(), nil
}

// Will calculate num pending but only requires a read lock.
// Depends on delivery policy, for last per subject we calculate differently.
// At least RLock should be held.
func (o *consumer) calculateNumPending() (npc, npf uint64, err error) {
	if o.mset == nil || o.mset.store == nil {
		return 0, 0, nil
	}

	isLastPerSubject := o.cfg.DeliverPolicy == DeliverLastPerSubject
	filters, subjf := o.filters, o.subjf

	if filters != nil {
		return o.mset.store.NumPendingMulti(o.sseq, filters, isLastPerSubject)
	} else if len(subjf) > 0 {
		filter := subjf[0].subject
		return o.mset.store.NumPending(o.sseq, filter, isLastPerSubject)
	}
	return o.mset.store.NumPending(o.sseq, _EMPTY_, isLastPerSubject)
}

func convertToHeadersOnly(pmsg *jsPubMsg) {
	// If headers only do not send msg payload.
	// Add in msg size itself as header.
	hdr, msg := pmsg.hdr, pmsg.msg
	var bb bytes.Buffer
	if len(hdr) == 0 {
		bb.WriteString(hdrLine)
	} else {
		bb.Write(hdr)
		bb.Truncate(len(hdr) - LEN_CR_LF)
	}
	bb.WriteString(JSMsgSize)
	bb.WriteString(": ")
	bb.WriteString(strconv.FormatInt(int64(len(msg)), 10))
	bb.WriteString(CR_LF)
	bb.WriteString(CR_LF)
	// Replace underlying buf which we can use directly when we send.
	// TODO(dlc) - Probably just use directly when forming bytes.Buffer?
	pmsg.buf = pmsg.buf[:0]
	pmsg.buf = append(pmsg.buf, bb.Bytes()...)
	// Replace with new header.
	pmsg.hdr = pmsg.buf
	// Cancel msg payload
	pmsg.msg = nil
}

// Deliver a msg to the consumer.
// Lock should be held and o.mset validated to be non-nil.
func (o *consumer) deliverMsg(dsubj, ackReply string, pmsg *jsPubMsg, dc uint64, rp RetentionPolicy) {
	if o.mset == nil {
		pmsg.returnToPool()
		return
	}

	dseq := o.dseq
	o.dseq++

	pmsg.dsubj, pmsg.reply, pmsg.o = dsubj, ackReply, o
	psz := pmsg.size()

	if o.maxpb > 0 {
		o.pbytes += psz
	}

	mset := o.mset
	ap := o.cfg.AckPolicy

	// Cant touch pmsg after this sending so capture what we need.
	seq, ts := pmsg.seq, pmsg.ts

	// Update delivered first.
	o.updateDelivered(dseq, seq, dc, ts)

	if ap == AckNone {
		o.adflr = dseq
		o.asflr = seq
	} else {
		o.trackPending(seq, dseq)
	}

	// Send message.
	if o.replicateDeliveries() {
		o.addReplicatedQueuedMsg(pmsg)
	} else {
		o.outq.send(pmsg)
	}

	// Flow control.
	if o.maxpb > 0 && o.needFlowControl(psz) {
		o.sendFlowControl()
	}

	// If pull mode and we have inactivity threshold, signaled by dthresh, update last activity.
	if o.isPullMode() && o.dthresh > 0 {
		o.waiting.last = time.Now()
	}

	// If we are ack none and mset is interest only we should make sure stream removes interest.
	if ap == AckNone && rp != LimitsPolicy {
		if mset != nil && mset.ackq != nil && (o.node == nil || o.cfg.Direct) {
			mset.ackq.push(seq)
		} else {
			o.updateAcks(dseq, seq, _EMPTY_)
		}
	}
}

// replicateDeliveries returns whether deliveries should be replicated before sending them.
// If we're replicated we MUST only send the message AFTER we've got quorum for updating
// delivered state. Otherwise, we could be in an invalid state after a leader change.
// We can send immediately if not replicated, not using acks, or using flow control (incompatible).
// Lock should be held.
func (o *consumer) replicateDeliveries() bool {
	return o.node != nil && o.cfg.AckPolicy != AckNone && !o.cfg.FlowControl
}

func (o *consumer) needFlowControl(sz int) bool {
	if o.maxpb == 0 {
		return false
	}
	// Decide whether to send a flow control message which we will need the user to respond.
	// We send when we are over 50% of our current window limit.
	if o.fcid == _EMPTY_ && o.pbytes > o.maxpb/2 {
		return true
	}
	// Or, when acking based on flow control, we need to send it if we've hit the max pending limit earlier.
	if o.fcid == _EMPTY_ && o.cfg.AckPolicy == AckFlowControl && o.maxp > 0 && len(o.pending) >= o.maxp {
		return true
	}
	// If we have an existing outstanding FC, check to see if we need to expand the o.fcsz
	if o.fcid != _EMPTY_ && (o.pbytes-o.fcsz) >= o.maxpb {
		o.fcsz += sz
	}
	return false
}

func (o *consumer) processFlowControl(_ *subscription, c *client, _ *Account, subj, _ string, rmsg []byte) {
	o.mu.Lock()

	// Ignore if not the latest we have sent out.
	if subj != o.fcid {
		o.mu.Unlock()
		return
	}

	// For slow starts and ramping up.
	if o.maxpb < o.pblimit {
		o.maxpb *= 2
		if o.maxpb > o.pblimit {
			o.maxpb = o.pblimit
		}
	}

	// Update accounting.
	o.pbytes -= o.fcsz
	if o.pbytes < 0 {
		o.pbytes = 0
	}
	o.fcid, o.fcsz = _EMPTY_, 0

	o.signalNewMessages()
	ackFlowControl := o.cfg.AckPolicy == AckFlowControl
	o.mu.Unlock()

	if !ackFlowControl {
		return
	}
	hdr, _ := c.msgParts(rmsg)
	if len(hdr) > 0 {
		ldseq := parseInt64(sliceHeader(JSLastConsumerSeq, hdr))
		lsseq := parseInt64(sliceHeader(JSLastStreamSeq, hdr))
		if lsseq > 0 {
			// Delivered sequence is allowed to be zero as a response
			// to flow control without any deliveries.
			if ldseq <= 0 {
				ldseq = 0
			}
			o.processAckMsg(uint64(lsseq), uint64(ldseq), 1, _EMPTY_, false)
		}
	}
}

// Lock should be held.
func (o *consumer) fcReply() string {
	var sb strings.Builder
	if o.useV2Ack {
		sb.WriteString(o.fcPre)
	} else {
		sb.WriteString(o.fcPreOld)
	}
	sb.WriteString(o.stream)
	sb.WriteByte(btsep)
	sb.WriteString(o.name)
	sb.WriteByte(btsep)
	var b [4]byte
	rn := rand.Int63()
	for i, l := 0, rn; i < len(b); i++ {
		b[i] = digits[l%base]
		l /= base
	}
	sb.Write(b[:])
	return sb.String()
}

// sendFlowControl will send a flow control packet to the consumer.
// Lock should be held.
func (o *consumer) sendFlowControl() {
	if !o.isPushMode() {
		return
	}
	subj, rply := o.cfg.DeliverSubject, o.fcReply()
	o.fcsz, o.fcid = o.pbytes, rply
	hdr := []byte("NATS/1.0 100 FlowControl Request\r\n\r\n")
	o.outq.send(newJSPubMsg(subj, _EMPTY_, rply, hdr, nil, nil, 0))
}

// Tracks our outstanding pending acks. Only applicable to AckExplicit mode.
// Lock should be held.
func (o *consumer) trackPending(sseq, dseq uint64) {
	if o.pending == nil {
		o.pending = make(map[uint64]*Pending)
	}

	now := time.Now()
	if p, ok := o.pending[sseq]; ok {
		// Update timestamp but keep original consumer delivery sequence.
		// So do not update p.Sequence.
		p.Timestamp = now.UnixNano()
	} else {
		o.pending[sseq] = &Pending{dseq, now.UnixNano()}
	}

	// We could have a backoff that set a timer higher than what we need for this message.
	// In that case, reset to lowest backoff required for a message redelivery.
	minDelay := o.ackWait(0)
	if l := len(o.cfg.BackOff); l > 0 {
		bi := int(o.rdc[sseq])
		if bi < 0 {
			bi = 0
		} else if bi >= l {
			bi = l - 1
		}
		minDelay = o.ackWait(o.cfg.BackOff[bi])
	}
	minDeadline := now.Add(minDelay)
	if o.ptmr == nil || o.ptmrEnd.After(minDeadline) {
		o.resetPtmr(minDelay)
	}
}

// Credit back a failed delivery.
// lock should be held.
func (o *consumer) creditWaitingRequest(reply string) {
	wq := o.waiting
	for wr := wq.head; wr != nil; wr = wr.next {
		if wr.reply == reply {
			wr.n++
			wr.d--
			return
		}
	}
}

// didNotDeliver is called when a delivery for a consumer message failed.
// Depending on our state, we will process the failure.
func (o *consumer) didNotDeliver(seq uint64, subj string) {
	o.mu.Lock()
	mset := o.mset
	if mset == nil {
		o.mu.Unlock()
		return
	}
	// Adjust back deliver count.
	o.decDeliveryCount(seq)

	var checkDeliveryInterest bool
	if o.isPushMode() {
		o.active = false
		checkDeliveryInterest = true
	} else if o.pending != nil {
		// Good chance we did not deliver because no interest so force a check.
		o.processWaiting(false)
		// If it is still there credit it back.
		o.creditWaitingRequest(subj)
		// pull mode and we have pending.
		if _, ok := o.pending[seq]; ok {
			// We found this messsage on pending, we need
			// to queue it up for immediate redelivery since
			// we know it was not delivered
			if !o.onRedeliverQueue(seq) {
				o.addToRedeliverQueue(seq)
				if !o.waiting.isEmpty() {
					o.signalNewMessages()
				}
			}
		}
	}
	o.mu.Unlock()

	if checkDeliveryInterest {
		localInterest := !o.hasNoLocalInterest()
		o.updateDeliveryInterest(localInterest)
	}
}

// Lock should be held.
func (o *consumer) addToRedeliverQueue(seqs ...uint64) {
	o.rdq = append(o.rdq, seqs...)
	for _, seq := range seqs {
		o.rdqi.Insert(seq)
	}
}

// Lock should be held.
func (o *consumer) hasRedeliveries() bool {
	return len(o.rdq) > 0
}

func (o *consumer) getNextToRedeliver() uint64 {
	if len(o.rdq) == 0 {
		return 0
	}
	seq := o.rdq[0]
	if len(o.rdq) == 1 {
		o.rdq = nil
		o.rdqi.Empty()
	} else {
		o.rdq = append(o.rdq[:0], o.rdq[1:]...)
		o.rdqi.Delete(seq)
	}
	return seq
}

// This checks if we already have this sequence queued for redelivery.
// FIXME(dlc) - This is O(n) but should be fast with small redeliver size.
// Lock should be held.
func (o *consumer) onRedeliverQueue(seq uint64) bool {
	return o.rdqi.Exists(seq)
}

// Remove a sequence from the redelivery queue.
// Lock should be held.
func (o *consumer) removeFromRedeliverQueue(seq uint64) bool {
	if !o.onRedeliverQueue(seq) {
		return false
	}
	for i, rseq := range o.rdq {
		if rseq == seq {
			if len(o.rdq) == 1 {
				o.rdq = nil
				o.rdqi.Empty()
			} else {
				o.rdq = append(o.rdq[:i], o.rdq[i+1:]...)
				o.rdqi.Delete(seq)
			}
			return true
		}
	}
	return false
}

// Checks the pending messages.
func (o *consumer) checkPending() {
	o.mu.Lock()
	defer o.mu.Unlock()

	mset := o.mset
	ttl := int64(o.cfg.AckWait)
	// On stop, mset and timer will be nil.
	if o.closed || mset == nil || o.ptmr == nil || ttl == 0 {
		o.stopAndClearPtmr()
		return
	}

	var shouldUpdateState bool
	var state StreamState
	mset.store.FastState(&state)
	fseq := state.FirstSeq

	now := time.Now().UnixNano()
	next := int64(o.ackWait(0))
	// However, if there is backoff, initializes with the largest backoff.
	// It will be adjusted as needed.
	if l := len(o.cfg.BackOff); l > 0 {
		next = int64(o.cfg.BackOff[l-1])
	}

	// Since we can update timestamps, we have to review all pending.
	// We will now bail if we see an ack pending inbound to us via o.awl.
	var expired []uint64
	check := len(o.pending) > 1024
	for seq, p := range o.pending {
		if check && atomic.LoadInt64(&o.awl) > 0 {
			o.resetPtmr(100 * time.Millisecond)
			return
		}
		// Check if these are no longer valid.
		if seq < fseq || seq <= o.asflr {
			delete(o.pending, seq)
			delete(o.rdc, seq)
			o.removeFromRedeliverQueue(seq)
			shouldUpdateState = true
			// Check if we need to move ack floors.
			if seq > o.asflr {
				o.asflr = seq
			}
			if p.Sequence > o.adflr {
				o.adflr = p.Sequence
			}
			continue
		}
		elapsed, deadline := now-p.Timestamp, ttl
		if len(o.cfg.BackOff) > 0 {
			// This is ok even if o.rdc is nil, we would get dc == 0, which is what we want.
			dc := int(o.rdc[seq])
			if dc < 0 {
				// Prevent consumer backoff from going backwards.
				dc = 0
			}
			// This will be the index for the next backoff, will set to last element if needed.
			nbi := dc + 1
			if dc+1 >= len(o.cfg.BackOff) {
				dc = len(o.cfg.BackOff) - 1
				nbi = dc
			}
			deadline = int64(o.cfg.BackOff[dc])
			// Set `next` to the next backoff (if smaller than current `next` value).
			if nextBackoff := int64(o.cfg.BackOff[nbi]); nextBackoff < next {
				next = nextBackoff
			}
		}
		if elapsed >= deadline {
			// We will check if we have hit our max deliveries. Previously we would do this on getNextMsg() which
			// worked well for push consumers, but with pull based consumers would require a new pull request to be
			// present to process and redelivered could be reported incorrectly.
			if !o.onRedeliverQueue(seq) && !o.hasMaxDeliveries(seq) {
				expired = append(expired, seq)
			}
		} else if deadline-elapsed < next {
			// Update when we should fire next.
			next = deadline - elapsed
		}
	}

	if len(expired) > 0 {
		// We need to sort.
		slices.Sort(expired)
		o.addToRedeliverQueue(expired...)
		// Now we should update the timestamp here since we are redelivering.
		// We will use an incrementing time to preserve order for any other redelivery.
		off := now - o.pending[expired[0]].Timestamp
		for _, seq := range expired {
			if p, ok := o.pending[seq]; ok {
				p.Timestamp += off
			}
		}
		o.signalNewMessages()
	}

	if len(o.pending) > 0 {
		o.resetPtmr(time.Duration(next))
	} else {
		// Make sure to stop timer and clear out any re delivery queues
		o.stopAndClearPtmr()
		o.rdq = nil
		o.rdqi.Empty()
		o.pending = nil
		// Mimic behavior in processAckMsg when pending is empty.
		o.adflr, o.asflr = o.dseq-1, o.sseq-1
	}

	// Update our state if needed.
	if shouldUpdateState {
		if err := o.writeStoreStateUnlocked(); err != nil && o.srv != nil && o.mset != nil && !o.closed {
			s, acc, mset, name := o.srv, o.acc, o.mset, o.name
			s.Warnf("Consumer '%s > %s > %s' error on write store state from check pending: %v", acc, mset.getCfgName(), name, err)
		}
	}
}

// SeqFromReply will extract a sequence number from a reply subject.
func (o *consumer) seqFromReply(reply string) uint64 {
	_, dseq, _, _, _ := ackReplyInfo(reply)
	return dseq
}

// StreamSeqFromReply will extract the stream sequence from the reply subject.
func (o *consumer) streamSeqFromReply(reply string) uint64 {
	sseq, _, _, _, _ := ackReplyInfo(reply)
	return sseq
}

// Quick parser for positive numbers in ack reply encoding.
func parseAckReplyNum(d string) (n int64) {
	if len(d) == 0 {
		return -1
	}
	for _, dec := range d {
		if dec < asciiZero || dec > asciiNine {
			return -1
		}
		n = n*10 + (int64(dec) - asciiZero)
	}
	return n
}

const (
	expectedNumReplyTokensV1 = 9
	expectedNumReplyTokensV2 = 11
)

// Grab encoded information in the reply subject for a delivered message.
func ackReplyInfo(subject string) (sseq, dseq, dc uint64, ts int64, pending uint64) {
	tsa := [expectedNumReplyTokensV2]string{}
	start, tokens := 0, tsa[:0]
	for i := 0; i < len(subject); i++ {
		if subject[i] == btsep {
			tokens = append(tokens, subject[start:i])
			start = i + 1
		}
	}
	tokens = append(tokens, subject[start:])
	if (len(tokens) != expectedNumReplyTokensV1 && len(tokens) < expectedNumReplyTokensV2) || tokens[0] != "$JS" || tokens[1] != "ACK" {
		return 0, 0, 0, 0, 0
	}
	offset := 2
	if len(tokens) >= expectedNumReplyTokensV2 {
		offset = 4
	}
	// TODO(dlc) - Should we error if we do not match consumer name?
	// stream is tokens[offset], consumer is offset+1.
	dc = uint64(parseAckReplyNum(tokens[offset+2]))
	sseq, dseq = uint64(parseAckReplyNum(tokens[offset+3])), uint64(parseAckReplyNum(tokens[offset+4]))
	ts = parseAckReplyNum(tokens[offset+5])
	pending = uint64(parseAckReplyNum(tokens[offset+6]))

	return sseq, dseq, dc, ts, pending
}

// NextSeq returns the next delivered sequence number for this consumer.
func (o *consumer) nextSeq() uint64 {
	o.mu.RLock()
	dseq := o.dseq
	o.mu.RUnlock()
	return dseq
}

// Used to hold skip list when deliver policy is last per subject.
type lastSeqSkipList struct {
	resume uint64
	seqs   []uint64
}

// Let's us know we have a skip list, which is for deliver last per subject and we are just starting.
// Lock should be held.
func (o *consumer) hasSkipListPending() bool {
	return o.lss != nil && len(o.lss.seqs) > 0
}

// reconcileStateWithStream reconciles consumer state when the stream has reverted
// due to data loss (e.g., VM crash). This handles the case where consumer state
// is ahead of the stream's last sequence.
// Lock should be held.
func (o *consumer) reconcileStateWithStream(streamLastSeq uint64) {
	// If an ack floor is higher than stream last sequence,
	// reset back down but keep the highest known sequences.
	if o.asflr > streamLastSeq {
		o.asflr = streamLastSeq
		// Delivery floor is one below the delivered sequence,
		// but if it is zero somehow, ensure we don't underflow.
		o.adflr = o.dseq
		if o.adflr > 0 {
			o.adflr--
		}
		o.pending = nil
		o.rdc = nil
	}

	// Remove pending entries that are beyond the stream's last sequence
	if len(o.pending) > 0 {
		for seq := range o.pending {
			if seq > streamLastSeq {
				delete(o.pending, seq)
			}
		}
	}

	// Remove redelivered entries that are beyond the stream's last sequence
	if len(o.rdc) > 0 {
		for seq := range o.rdc {
			if seq > streamLastSeq {
				delete(o.rdc, seq)
			}
		}
	}

	// Update starting sequence and delivery sequence based on pending state
	if len(o.pending) == 0 {
		o.sseq = o.asflr + 1
		o.dseq = o.adflr + 1
	} else {
		// Find highest stream sequence in pending
		var maxStreamSeq uint64
		var maxConsumerSeq uint64

		for streamSeq, p := range o.pending {
			if streamSeq > maxStreamSeq {
				maxStreamSeq = streamSeq
			}
			if p.Sequence > maxConsumerSeq {
				maxConsumerSeq = p.Sequence
			}
		}

		// Set next sequences based on highest pending
		o.sseq = maxStreamSeq + 1
		o.dseq = maxConsumerSeq + 1
	}
}

// Will select the starting sequence.
func (o *consumer) selectStartingSeqNo() error {
	if o.mset == nil || o.mset.store == nil {
		o.sseq = 1
	} else {
		var state StreamState
		o.mset.store.FastState(&state)
		if o.cfg.OptStartSeq == 0 {
			if o.cfg.DeliverPolicy == DeliverAll {
				o.sseq = state.FirstSeq
			} else if o.cfg.DeliverPolicy == DeliverLast {
				if o.subjf == nil {
					o.sseq = state.LastSeq
				} else {
					// If we are partitioned here this will be properly set when we become leader.
					for _, filter := range o.subjf {
						ss, err := o.mset.store.FilteredState(1, filter.subject)
						if err != nil {
							return err
						}
						if ss.Last > o.sseq {
							o.sseq = ss.Last
						}
					}
				}
			} else if o.cfg.DeliverPolicy == DeliverLastPerSubject {
				// If our parent stream is set to max msgs per subject of 1 this is just
				// a normal consumer at this point. We can avoid any heavy lifting.
				o.mset.cfgMu.RLock()
				mmp := o.mset.cfg.MaxMsgsPer
				o.mset.cfgMu.RUnlock()
				if mmp == 1 {
					o.sseq = state.FirstSeq
				} else {
					filters := make([]string, 0, len(o.subjf))
					if o.subjf == nil {
						filters = append(filters, o.cfg.FilterSubject)
					} else {
						for _, filter := range o.subjf {
							filters = append(filters, filter.subject)
						}
					}

					lss := &lastSeqSkipList{resume: state.LastSeq}
					lss.seqs, _ = o.mset.store.MultiLastSeqs(filters, 0, 0)

					if len(lss.seqs) == 0 {
						o.sseq = state.LastSeq
					} else {
						o.sseq = lss.seqs[0]
					}
					// Assign skip list.
					o.lss = lss
				}
			} else if o.cfg.OptStartTime != nil {
				// If we are here we are time based.
				// TODO(dlc) - Once clustered can't rely on this.
				o.sseq = o.mset.store.GetSeqFromTime(*o.cfg.OptStartTime)
				// Here we want to see if we are filtered, and if so possibly close the gap
				// to the nearest first given our starting sequence from time. This is so we do
				// not force the system to do a linear walk between o.sseq and the real first.
				if len(o.subjf) > 0 {
					nseq := state.LastSeq
					for _, filter := range o.subjf {
						// Use first sequence since this is more optimized atm.
						ss, err := o.mset.store.FilteredState(state.FirstSeq, filter.subject)
						if err != nil {
							return err
						}
						if ss.First >= o.sseq && ss.First < nseq {
							nseq = ss.First
						}
					}
					// Skip ahead if possible.
					if nseq > o.sseq && nseq < state.LastSeq {
						o.sseq = nseq
					}
				}
			} else {
				// DeliverNew
				o.sseq = state.LastSeq + 1
			}
		} else {
			o.sseq = o.cfg.OptStartSeq
		}

		if state.FirstSeq == 0 && (o.cfg.Direct || o.cfg.OptStartSeq == 0) {
			// If the stream is empty, deliver only new.
			// But only if mirroring/sourcing, or start seq is unset, otherwise need to respect provided value.
			o.sseq = 1
		} else if o.sseq > state.LastSeq && (o.cfg.Direct || o.cfg.OptStartSeq == 0) {
			// If selected sequence is in the future, clamp back down.
			// But only if mirroring/sourcing, or start seq is unset, otherwise need to respect provided value.
			o.sseq = state.LastSeq + 1
		} else if o.sseq < state.FirstSeq {
			// If the first sequence is further ahead than the starting sequence,
			// there are no messages there anymore, so move the sequence up.
			o.sseq = state.FirstSeq
		}
	}

	// Always set delivery sequence to 1.
	o.dseq = 1
	// Set ack delivery floor to delivery-1
	o.adflr = o.dseq - 1
	// Set ack store floor to store-1
	o.asflr = o.sseq - 1
	// Set our starting sequence state.
	// But only if we're not clustered, if clustered we propose upon becoming leader.
	if o.store != nil && o.sseq > 0 && o.cfg.replicas(&o.mset.cfg) == 1 {
		if err := o.store.SetStarting(o.sseq - 1); err != nil {
			return err
		}
	}
	return nil
}

// Test whether a config represents a durable subscriber.
func isDurableConsumer(config *ConsumerConfig) bool {
	return config != nil && config.Durable != _EMPTY_
}

func (o *consumer) isDurable() bool {
	return o.cfg.Durable != _EMPTY_
}

// Are we in push mode, delivery subject, etc.
func (o *consumer) isPushMode() bool {
	return o.cfg.DeliverSubject != _EMPTY_
}

func (o *consumer) isPullMode() bool {
	return o.cfg.DeliverSubject == _EMPTY_
}

// Name returns the name of this consumer.
func (o *consumer) String() string {
	o.mu.RLock()
	n := o.name
	o.mu.RUnlock()
	return n
}

func createConsumerName() string {
	return getHash(nuid.Next())
}

// Lock should be held.
func (mset *stream) createStableConsumerHash() string {
	id := fmt.Sprintf("%s %s", mset.cfg.Name, mset.acc.Name)
	if domain := mset.srv.getOpts().JetStreamDomain; domain != _EMPTY_ {
		id = fmt.Sprintf("%s %s", id, domain)
	}
	return getHash(id)
}

// Lock should be held.
func (mset *stream) createSourcingConsumerHash(ssi *StreamSource, sources []*StreamSource) string {
	id := mset.createStableConsumerHash()

	// If the stream sources contain the same stream at least twice, we use a more strict hash of
	// an ID that also contains filter subjects etc. If the stream name is only used once, we can
	// support the stable identifier above.
	var once bool
	for _, src := range sources {
		if src.Name == ssi.Name {
			if once {
				if ssi.iname == _EMPTY_ {
					ssi.setIndexName()
				}
				// Append identifying information of the filter subjects, etc. to make it unique
				id = fmt.Sprintf("%s %s", id, ssi.iname)
				break
			} else {
				once = true
			}
		}
	}

	return getHash(id)
}

// deleteConsumer will delete the consumer from this stream.
func (mset *stream) deleteConsumer(o *consumer) error {
	return o.delete()
}

func (o *consumer) getStream() *stream {
	o.mu.RLock()
	mset := o.mset
	o.mu.RUnlock()
	return mset
}

func (o *consumer) streamName() string {
	o.mu.RLock()
	mset := o.mset
	o.mu.RUnlock()
	if mset != nil {
		return mset.name()
	}
	return _EMPTY_
}

// Active indicates if this consumer is still active.
func (o *consumer) isActive() bool {
	o.mu.RLock()
	active := o.active && o.mset != nil
	o.mu.RUnlock()
	return active
}

// hasNoLocalInterest return true if we have no local interest.
func (o *consumer) hasNoLocalInterest() bool {
	o.mu.RLock()
	interest := o.acc.sl.HasInterest(o.cfg.DeliverSubject)
	o.mu.RUnlock()
	return !interest
}

// This is when the underlying stream has been purged.
// sseq is the new first seq for the stream after purge.
// Consumer lock should NOT be held but the parent stream
// lock MUST be held.
func (o *consumer) purge(sseq uint64, slseq uint64, isWider bool) {
	// Do not update our state unless we know we are the leader.
	if !o.isLeader() {
		return
	}
	// Signals all have been purged for this consumer.
	if sseq == 0 && !isWider {
		sseq = slseq + 1
	}

	var store StreamStore
	if isWider {
		o.mu.RLock()
		if o.mset != nil {
			store = o.mset.store
		}
		o.mu.RUnlock()
	}

	o.mu.Lock()
	// Do not go backwards
	if o.sseq < sseq {
		o.sseq = sseq
	}

	if o.asflr < sseq {
		o.asflr = sseq - 1
		// We need to remove those no longer relevant from pending.
		for seq, p := range o.pending {
			if seq <= o.asflr {
				if p.Sequence > o.adflr {
					o.adflr = p.Sequence
					if o.adflr > o.dseq {
						o.dseq = o.adflr
					}
				}
				delete(o.pending, seq)
				delete(o.rdc, seq)
				// rdq handled below.
			}
			if isWider && store != nil {
				// Our filtered subject, which could be all, is wider than the underlying purge.
				// We need to check if the pending items left are still valid.
				var smv StoreMsg
				if _, err := store.LoadMsg(seq, &smv); err == errDeletedMsg || err == ErrStoreMsgNotFound {
					if p.Sequence > o.adflr {
						o.adflr = p.Sequence
						if o.adflr > o.dseq {
							o.dseq = o.adflr
						}
					}
					delete(o.pending, seq)
					delete(o.rdc, seq)
				}
			}
		}
	}

	// This means we can reset everything at this point.
	if len(o.pending) == 0 {
		o.pending, o.rdc = nil, nil
		o.adflr, o.asflr = o.dseq-1, o.sseq-1
	}

	// We need to remove all those being queued for redelivery under o.rdq
	if len(o.rdq) > 0 {
		rdq := o.rdq
		o.rdq = nil
		o.rdqi.Empty()
		for _, sseq := range rdq {
			if sseq >= o.sseq {
				o.addToRedeliverQueue(sseq)
			}
		}
	}
	// Grab some info in case of error below.
	s, acc, mset, name := o.srv, o.acc, o.mset, o.name
	o.mu.Unlock()

	if err := o.writeStoreState(); err != nil && s != nil && mset != nil {
		s.Warnf("Consumer '%s > %s > %s' error on write store state from purge: %v", acc, mset.nameLocked(false), name, err)
	}
}

func stopAndClearTimer(tp **time.Timer) {
	if *tp == nil {
		return
	}
	// Will get drained in normal course, do not try to
	// drain here.
	(*tp).Stop()
	*tp = nil
}

// Stop will shutdown  the consumer for the associated stream.
func (o *consumer) stop() error {
	return o.stopWithFlags(false, false, true, false)
}

func (o *consumer) deleteWithoutAdvisory() error {
	return o.stopWithFlags(true, false, true, false)
}

// Delete will delete the consumer for the associated stream and send advisories.
func (o *consumer) delete() error {
	return o.stopWithFlags(true, false, true, true)
}

// To test for closed state.
func (o *consumer) isClosed() bool {
	o.mu.RLock()
	defer o.mu.RUnlock()
	return o.closed
}

func (o *consumer) stopWithFlags(dflag, sdflag, doSignal, advisory bool) error {
	// If dflag is true determine if we are still assigned.
	var isAssigned bool
	if dflag {
		o.mu.RLock()
		acc, stream, consumer := o.acc, o.stream, o.name
		isClustered := o.js != nil && o.js.isClustered()
		o.mu.RUnlock()
		if isClustered {
			// Grab jsa to check assignment.
			var jsa *jsAccount
			if acc != nil {
				// Need lock here to avoid data race.
				acc.mu.RLock()
				jsa = acc.js
				acc.mu.RUnlock()
			}
			if jsa != nil {
				isAssigned = jsa.consumerAssigned(stream, consumer)
			}
		}
	}

	o.mu.Lock()
	if o.closed {
		o.mu.Unlock()
		return nil
	}
	o.closed = true

	// Signal to the monitor loop.
	// Can't use only qch here, since that's used when stepping down as a leader.
	if o.mqch != nil {
		close(o.mqch)
		o.mqch = nil
	}

	// Check if we are the leader and are being deleted (as a node).
	if dflag && o.isLeader() {
		// If we are clustered and node leader (probable from above), stepdown.
		if node := o.node; node != nil {
			node.StepDown()
		}

		// dflag does not necessarily mean that the consumer is being deleted,
		// just that the consumer node is being removed from this peer, so we
		// send delete advisories only if we are no longer assigned at the meta layer,
		// or we are not clustered.
		if !isAssigned && advisory {
			o.sendDeleteAdvisoryLocked()
		}
		if o.isPullMode() {
			// Release any pending.
			o.releaseAnyPendingRequests(isAssigned)
		}
	}

	if o.qch != nil {
		close(o.qch)
		o.qch = nil
	}

	a := o.acc
	store := o.store
	mset := o.mset
	o.mset = nil
	o.active = false
	o.unsubscribe(o.ackSubOld)
	o.unsubscribe(o.ackSub)
	o.unsubscribe(o.reqSub)
	o.unsubscribe(o.resetSub)
	o.unsubscribe(o.fcSubOld)
	o.unsubscribe(o.fcSub)
	o.ackSubOld = nil
	o.ackSub = nil
	o.reqSub = nil
	o.resetSub = nil
	o.fcSubOld = nil
	o.fcSub = nil
	if o.infoSub != nil {
		o.srv.sysUnsubscribe(o.infoSub)
		o.infoSub = nil
	}
	c := o.client
	o.client = nil
	sysc := o.sysc
	o.sysc = nil
	o.stopAndClearPtmr()
	stopAndClearTimer(&o.dtmr)
	stopAndClearTimer(&o.gwdtmr)
	delivery := o.cfg.DeliverSubject
	o.waiting = nil
	// Break us out of the readLoop.
	if doSignal {
		o.signalNewMessages()
	}
	n := o.node
	qgroup := o.cfg.DeliverGroup
	o.ackMsgs.unregister()
	if o.nextMsgReqs != nil {
		o.nextMsgReqs.unregister()
	}

	// For cleaning up the node assignment.
	var ca *consumerAssignment
	if dflag {
		ca = o.ca
	}
	js := o.js
	o.mu.Unlock()

	if c != nil {
		c.closeConnection(ClientClosed)
	}
	if sysc != nil {
		sysc.closeConnection(ClientClosed)
	}

	if delivery != _EMPTY_ {
		a.sl.clearNotification(delivery, qgroup, o.inch)
	}

	var rp RetentionPolicy
	if mset != nil {
		mset.mu.Lock()
		mset.removeConsumer(o)
		// No need for cfgMu's lock since mset.mu.Lock superseeds it.
		rp = mset.cfg.Retention
		mset.mu.Unlock()
	}

	// Cleanup messages that lost interest.
	if dflag && rp == InterestPolicy {
		o.cleanupNoInterestMessages(mset, true)
	}

	// Cluster cleanup.
	if n != nil {
		if dflag {
			n.Delete()
		} else {
			n.Stop()
		}
	}

	if ca != nil {
		js.mu.Lock()
		if ca.Group != nil {
			ca.Group.node = nil
		}
		js.mu.Unlock()
	}

	// Clean up our store.
	var err error
	if store != nil {
		if dflag {
			if sdflag {
				err = store.StreamDelete()
			} else {
				err = store.Delete()
			}
		} else {
			err = store.Stop()
		}
	} else if dflag {
		// If there's no store (for example, when it's offline), manually delete the directories.
		o.mu.RLock()
		stream, consumer := o.stream, o.name
		o.mu.RUnlock()
		accDir := filepath.Join(js.config.StoreDir, a.GetName())
		consumersDir := filepath.Join(accDir, streamsDir, stream, consumerDir)
		os.RemoveAll(filepath.Join(consumersDir, consumer))
	}

	return err
}

// We need to optionally remove all messages since we are interest based retention.
// We will do this consistently on all replicas. Note that if in clustered mode the non-leader
// consumers will need to restore state first.
// ignoreInterest marks whether the consumer should be ignored when determining interest.
// No lock held on entry.
func (o *consumer) cleanupNoInterestMessages(mset *stream, ignoreInterest bool) {
	o.mu.Lock()
	if !o.isLeader() {
		o.readStoredState()
	}
	start := o.asflr
	o.mu.Unlock()

	// Make sure we start at worst with first sequence in the stream.
	state := mset.state()
	if start < state.FirstSeq {
		start = state.FirstSeq
	}
	stop := state.LastSeq

	// Consumer's interests are ignored by default. If we should not ignore interest, unset.
	co := o
	if !ignoreInterest {
		co = nil
	}

	var rmseqs []uint64
	mset.mu.RLock()

	// If over this amount of messages to check, optimistically call to checkInterestState().
	// It will not always do the right thing in removing messages that lost interest, but ensures
	// we don't degrade performance by doing a linear scan through the whole stream.
	// Messages might need to expire based on limits to be cleaned up.
	// TODO(dlc) - Better way?
	const bailThresh = 100_000

	// Check if we would be spending too much time here and defer to separate go routine.
	if len(mset.consumers) == 0 {
		mset.mu.RUnlock()
		mset.mu.Lock()
		defer mset.mu.Unlock()
		mset.store.Purge()
		var state StreamState
		mset.store.FastState(&state)
		mset.lseq = state.LastSeq
		// Also make sure we clear any pending acks.
		mset.clearAllPreAcksBelowFloor(state.FirstSeq)
		return
	} else if stop-start > bailThresh {
		mset.mu.RUnlock()
		go mset.checkInterestState()
		return
	}

	mset.mu.RUnlock()
	mset.mu.Lock()
	for seq := start; seq <= stop; seq++ {
		if mset.noInterest(seq, co) {
			rmseqs = append(rmseqs, seq)
		}
	}
	mset.mu.Unlock()

	// These can be removed.
	for _, seq := range rmseqs {
		mset.store.RemoveMsg(seq)
	}
}

// Check that we do not form a cycle by delivering to a delivery subject
// that is part of the interest group.
func deliveryFormsCycle(cfg *StreamConfig, deliverySubject string) bool {
	for _, subject := range cfg.Subjects {
		if subjectIsSubsetMatch(deliverySubject, subject) {
			return true
		}
	}
	return false
}

// switchToEphemeral is called on startup when recovering ephemerals.
func (o *consumer) switchToEphemeral() {
	o.mu.Lock()
	o.cfg.Durable = _EMPTY_
	store, ok := o.store.(*consumerFileStore)
	interest := o.acc.sl.HasInterest(o.cfg.DeliverSubject)
	// Setup dthresh.
	o.updateInactiveThreshold(&o.cfg)
	o.updatePauseState(&o.cfg)
	o.mu.Unlock()

	// Update interest
	o.updateDeliveryInterest(interest)
	// Write out new config
	if ok {
		store.updateConfig(o.cfg)
	}
}

// RequestNextMsgSubject returns the subject to request the next message when in pull or worker mode.
// Returns empty otherwise.
func (o *consumer) requestNextMsgSubject() string {
	return o.nextMsgSubj
}

func (o *consumer) decStreamPending(sseq uint64, subj string) {
	o.mu.Lock()

	// Update our cached num pending only if we think deliverMsg has not done so.
	if sseq >= o.sseq && o.isFilteredMatch(subj) {
		o.npc--
	}

	// Check if this message was pending.
	p, wasPending := o.pending[sseq]
	var rdc uint64
	if wasPending {
		rdc = o.deliveryCount(sseq)
	}

	o.mu.Unlock()

	// If it was pending process it like an ack.
	if wasPending {
		// We could have the lock for the stream so do this in a go routine.
		// TODO(dlc) - We should do this with ipq vs naked go routines.
		go o.processTerm(sseq, p.Sequence, rdc, ackTermUnackedLimitsReason, _EMPTY_)
	}
}

func (o *consumer) account() *Account {
	o.mu.RLock()
	a := o.acc
	o.mu.RUnlock()
	return a
}

// Creates a sublist for consumer.
// All subjects share the same callback.
func (o *consumer) signalSubs() []string {
	o.mu.Lock()
	defer o.mu.Unlock()

	if o.sigSubs != nil {
		return o.sigSubs
	}

	if len(o.subjf) == 0 {
		subs := []string{fwcs}
		o.sigSubs = subs
		return subs
	}

	subs := make([]string, 0, len(o.subjf))
	for _, filter := range o.subjf {
		subs = append(subs, filter.subject)
	}
	o.sigSubs = subs
	return subs
}

// This is what will be called when our parent stream wants to kick us regarding a new message.
// We know that this subject matches us by how the parent handles registering us with the signaling sublist,
// but we must check if we are leader.
// We do need the sequence of the message however and we use the msg as the encoded seq.
func (o *consumer) processStreamSignal(seq uint64) {
	// We can get called here now when not leader, so bail fast
	// and without acquiring any locks.
	if !o.leader.Load() {
		return
	}
	o.mu.Lock()
	defer o.mu.Unlock()
	if o.mset == nil {
		return
	}
	if seq > o.npf {
		o.npc++
	}
	if seq < o.sseq {
		return
	}
	if o.isPushMode() && o.active || o.isPullMode() && !o.waiting.isEmpty() {
		o.signalNewMessages()
	}
}

// Used to compare if two multiple filtered subject lists are equal.
func subjectSliceEqual(slice1 []string, slice2 []string) bool {
	if len(slice1) != len(slice2) {
		return false
	}
	set2 := make(map[string]struct{}, len(slice2))
	for _, val := range slice2 {
		set2[val] = struct{}{}
	}
	for _, val := range slice1 {
		if _, ok := set2[val]; !ok {
			return false
		}
	}
	return true
}

// Utility for simpler if conditions in Consumer config checks.
// In future iteration, we can immediately create `o.subjf` and
// use it to validate things.
func gatherSubjectFilters(filter string, filters []string) []string {
	if filter != _EMPTY_ {
		filters = append(filters, filter)
	}
	// list of filters should never contain non-empty filter.
	return filters
}

// shouldStartMonitor will return true if we should start a monitor
// goroutine or will return false if one is already running.
func (o *consumer) shouldStartMonitor() bool {
	o.mu.Lock()
	defer o.mu.Unlock()

	if o.inMonitor {
		return false
	}
	o.monitorWg.Add(1)
	o.inMonitor = true
	return true
}

// Clear the monitor running state. The monitor goroutine should
// call this in a defer to clean up on exit.
func (o *consumer) clearMonitorRunning() {
	o.mu.Lock()
	defer o.mu.Unlock()

	if o.inMonitor {
		o.monitorWg.Done()
		o.inMonitor = false
	}
}

// Test whether we are in the monitor routine.
func (o *consumer) isMonitorRunning() bool {
	o.mu.RLock()
	defer o.mu.RUnlock()
	return o.inMonitor
}

// If we detect that our ackfloor is higher than the stream's last sequence, return this error.
var errAckFloorHigherThanLastSeq = errors.New("consumer ack floor is higher than streams last sequence")
var errAckFloorInvalid = errors.New("consumer ack floor is invalid")

// If we are a consumer of an interest or workqueue policy stream, process that state and make sure consistent.
func (o *consumer) checkStateForInterestStream(ss *StreamState) error {
	o.mu.RLock()
	// See if we need to process this update if our parent stream is not a limits policy stream.
	mset := o.mset
	shouldProcessState := mset != nil && o.retention != LimitsPolicy
	if o.closed || !shouldProcessState || o.store == nil || ss == nil {
		o.mu.RUnlock()
		return nil
	}
	store := mset.store
	state, err := o.store.State()

	filters, subjf, filter := o.filters, o.subjf, _EMPTY_
	var wc bool
	if filters == nil && subjf != nil {
		filter, wc = subjf[0].subject, subjf[0].hasWildcard
	}
	chkfloor := o.chkflr
	o.mu.RUnlock()

	if err != nil {
		return err
	}

	asflr := state.AckFloor.Stream
	// Protect ourselves against rolling backwards.
	if asflr&(1<<63) != 0 {
		return errAckFloorInvalid
	}
	dflr := asflr
	if len(state.Pending) > 0 && state.Delivered.Stream > dflr {
		dflr = state.Delivered.Stream
	}

	// Check if the underlying stream's last sequence is less than our floor.
	// This can happen if the stream has been reset and has not caught up yet.
	if asflr > ss.LastSeq {
		return errAckFloorHigherThanLastSeq
	}

	var smv StoreMsg
	var seq, nseq uint64
	// Start at first stream seq or a previous check floor, whichever is higher.
	// Note this will really help for interest retention, with WQ the loadNextMsg
	// gets us a long way already since it will skip deleted msgs not for our filter.
	fseq := ss.FirstSeq
	if chkfloor > fseq {
		fseq = chkfloor
	}

	var retryAsflr uint64
	for seq = fseq; dflr > 0 && seq <= dflr; seq++ {
		if filters != nil {
			_, nseq, err = store.LoadNextMsgMulti(filters, seq, &smv)
		} else {
			_, nseq, err = store.LoadNextMsg(filter, wc, seq, &smv)
		}
		// if we advanced sequence update our seq. This can be on no error and EOF.
		if nseq > seq {
			seq = nseq
		}
		if err == nil {
			// Only ack though if no error and seq <= ack floor.
			if seq <= asflr {
				didRemove := mset.ackMsg(o, seq)
				// Removing the message could fail. For example if clustered since we need to propose it.
				// Overwrite retry floor (only the first time) to allow us to check next time if the removal was successful.
				if didRemove && retryAsflr == 0 {
					retryAsflr = seq
				}
			} else if seq <= dflr {
				// Store the first entry above our ack floor, so we don't need to look it up again on retryAsflr=0.
				if retryAsflr == 0 {
					retryAsflr = seq
				}
				// If we have pending, we will need to walk through to delivered in case we missed any of those acks as well.
				if _, ok := state.Pending[seq]; !ok {
					// The filters are already taken into account,
					mset.ackMsg(o, seq)
				}
			}
		} else if err == ErrStoreEOF {
			break
		}
	}
	// If retry floor was not overwritten, set to ack floor+1, we don't need to account for any retries below it.
	// However, our ack floor may be lower than the next message we can receive, so we correct it upward if needed.
	if retryAsflr == 0 {
		if filters != nil {
			_, nseq, err = store.LoadNextMsgMulti(filters, asflr+1, &smv)
		} else {
			_, nseq, err = store.LoadNextMsg(filter, wc, asflr+1, &smv)
		}
		if err == nil {
			retryAsflr = max(asflr+1, nseq)
		} else if err == ErrStoreEOF {
			retryAsflr = ss.LastSeq + 1
		}
	}

	o.mu.Lock()
	// Update our check floor.
	// Check floor must never be greater than ack floor+1, otherwise subsequent calls to this function would skip work.
	if retryAsflr > o.chkflr {
		o.chkflr = retryAsflr
	}
	o.mu.Unlock()
	return nil
}

func (o *consumer) resetPtmr(delay time.Duration) {
	// A delay of zero means it should be stopped.
	if delay == 0 {
		o.stopAndClearPtmr()
		return
	}

	if o.ptmr == nil {
		o.ptmr = time.AfterFunc(delay, o.checkPending)
	} else {
		o.ptmr.Reset(delay)
	}
	o.ptmrEnd = time.Now().Add(delay)
}

func (o *consumer) stopAndClearPtmr() {
	// If the end time is unset, short-circuit since the timer will already be stopped.
	if o.ptmrEnd.IsZero() {
		return
	}
	stopAndClearTimer(&o.ptmr)
	o.ptmrEnd = time.Time{}
}

func (o *consumer) resetPendingDeliveries() {
	for _, pmsg := range o.pendingDeliveries {
		pmsg.returnToPool()
	}
	o.pendingDeliveries = nil
	for _, wd := range o.waitingDeliveries {
		wd.recycle()
	}
	o.waitingDeliveries = nil
}
