package reliability

// Bulkhead isolates concurrency per dependency (named after a ship's
// watertight compartments): a fixed number of concurrent slots, so a
// saturated or slow dependency can only exhaust ITS OWN slots, not the
// service's shared capacity. Calls beyond the limit are rejected
// immediately (fail fast) rather than piling up. Pairs with circuit
// breakers and adaptive concurrency (gw-06).
type Bulkhead struct {
	sem chan struct{}
}

// NewBulkhead creates a bulkhead allowing `max` concurrent in-flight
// calls.
func NewBulkhead(max int) *Bulkhead {
	if max < 1 {
		max = 1
	}
	return &Bulkhead{sem: make(chan struct{}, max)}
}

// TryAcquire takes a slot if one is free; returns false (reject) if the
// bulkhead is full. Non-blocking — the caller sheds rather than queues.
func (b *Bulkhead) TryAcquire() bool {
	select {
	case b.sem <- struct{}{}:
		return true
	default:
		return false
	}
}

// Release returns a slot.
func (b *Bulkhead) Release() { <-b.sem }

// InUse reports the current number of occupied slots.
func (b *Bulkhead) InUse() int { return len(b.sem) }

// Cap reports the maximum concurrency.
func (b *Bulkhead) Cap() int { return cap(b.sem) }
