package upstream import ( "time" proxyDomain "proxy-pool/internal/domain/proxy" ) type ProxyCapacity struct { State proxyDomain.State ExpiresAt time.Time Max int64 Active int64 Reserved int64 } type Inventory struct { Proxies []ProxyCapacity PendingExpected int FetchedTotal int64 MaxSize int MaxTotal int64 } func (i Inventory) AvailableSlots(now time.Time, safetyMargin time.Duration) int64 { var available int64 deadline := now.Add(safetyMargin) for _, candidate := range i.Proxies { if candidate.State != proxyDomain.StateAvailable { continue } if !candidate.ExpiresAt.IsZero() && !candidate.ExpiresAt.After(deadline) { continue } slots := candidate.Max - candidate.Active - candidate.Reserved if slots > 0 { available += slots } } return available } func (i Inventory) ManagedCount() int { count := max(i.PendingExpected, 0) for _, candidate := range i.Proxies { if managedState(candidate.State) { count++ } } return count } func (i Inventory) FetchAllowance(requested int) int { if requested <= 0 || i.MaxSize <= 0 { return 0 } allowed := min(requested, max(i.MaxSize-i.ManagedCount(), 0)) if i.MaxTotal > 0 { remaining := i.MaxTotal - i.FetchedTotal if remaining <= 0 { return 0 } if int64(allowed) > remaining { allowed = int(remaining) } } return allowed } func managedState(state proxyDomain.State) bool { switch state { case proxyDomain.StateFetched, proxyDomain.StateChecking, proxyDomain.StateAvailable, proxyDomain.StateSuspect, proxyDomain.StateDraining: return true default: return false } }