package h2

import "errors"

// DefaultInitialWindow is SETTINGS_INITIAL_WINDOW_SIZE's default (RFC
// 9113 §6.5.2): 65,535 bytes per stream.
const DefaultInitialWindow = 65535

// FlowController models HTTP/2's two-level flow control: a sender may
// transmit DATA only while BOTH the connection window and the per-stream
// window are positive. Each DATA byte debits both; WINDOW_UPDATE credits
// one of them. This is application-level backpressure layered on top of
// TCP's own receive window.
type FlowController struct {
	conn    int64
	streams map[uint32]int64
	initial int64
}

func NewFlowController() *FlowController {
	return &FlowController{
		conn:    DefaultInitialWindow,
		streams: map[uint32]int64{},
		initial: DefaultInitialWindow,
	}
}

func (fc *FlowController) ensure(stream uint32) {
	if _, ok := fc.streams[stream]; !ok {
		fc.streams[stream] = fc.initial
	}
}

// ConnWindow / StreamWindow expose current windows (for tests/observability).
func (fc *FlowController) ConnWindow() int64 { return fc.conn }
func (fc *FlowController) StreamWindow(stream uint32) int64 {
	fc.ensure(stream)
	return fc.streams[stream]
}

// CanSend reports whether n DATA bytes are permitted on stream right now.
func (fc *FlowController) CanSend(stream uint32, n int64) bool {
	fc.ensure(stream)
	return fc.conn >= n && fc.streams[stream] >= n
}

// Sent debits both windows after sending n DATA bytes on stream.
func (fc *FlowController) Sent(stream uint32, n int64) error {
	if !fc.CanSend(stream, n) {
		return errors.New("h2: flow-control violation: window exhausted")
	}
	fc.conn -= n
	fc.streams[stream] -= n
	return nil
}

// WindowUpdate credits a window (stream == 0 means the connection window).
func (fc *FlowController) WindowUpdate(stream uint32, inc int64) {
	if stream == 0 {
		fc.conn += inc
		return
	}
	fc.ensure(stream)
	fc.streams[stream] += inc
}
