package api

import (
	"net/http"
	"sync/atomic"
)

// maxConcurrentSSEStreams caps the global number of in-flight SSE
// connections. Each stream holds a goroutine, an event-bus subscription, and
// (for log streams) a Docker daemon TCP socket; a single tab opening
// thousands of EventSources would otherwise exhaust file descriptors.
const maxConcurrentSSEStreams = 256

// sseGate is a counting gate that limits concurrent SSE streams.
type sseGate struct {
	cap int64
	cur atomic.Int64
}

func newSSEGate(cap int) *sseGate { return &sseGate{cap: int64(cap)} }

// enter reserves a slot and returns a release func, or nil if the gate is full.
func (g *sseGate) enter() func() {
	if g.cur.Add(1) > g.cap {
		g.cur.Add(-1)
		return nil
	}
	return func() { g.cur.Add(-1) }
}

// acquireSSESlot is a small helper used by every SSE handler to honour the
// global cap. Returns false (and writes a 503) if the cap is reached.
func acquireSSESlot(w http.ResponseWriter, gate *sseGate) (release func(), ok bool) {
	release = gate.enter()
	if release == nil {
		respondError(w, http.StatusServiceUnavailable, "stream limit reached")
		return nil, false
	}
	return release, true
}
