package telegram

import (
	
	
	
	

	

	
)

type cachedCDNPool struct {
	conn CloseInvoker
	// max is normalized bucket size used for reuse matching.
	max int64
}

var (
	errCDNPoolHandleClosed = errors.New("CDN pool handle is closed")
	errCDNPoolHandleDouble = errors.New("CDN pool handle already closed")
)

// cdnPoolHandle is a per-call wrapper around shared cached CDN pool.
// Close() releases only this borrowed handle; underlying pool is managed by
// client cache lifecycle (fingerprint invalidation or client shutdown).
type cdnPoolHandle struct {
	manager *cdnPoolManager
	conn    CloseInvoker
	closed  atomic.Bool
}

var _ CloseInvoker = (*cdnPoolHandle)(nil)

func ( *cdnPoolHandle) ( context.Context,  bin.Encoder,  bin.Decoder) error {
	if .closed.Load() {
		return errCDNPoolHandleClosed
	}
	return .conn.Invoke(, , )
}

func ( *cdnPoolHandle) () error {
	if .closed.Swap(true) {
		return errCDNPoolHandleDouble
	}
	if !.manager.releaseCachedHandle(.conn) {
		return nil
	}
	return .conn.Close()
}

type cdnPoolManager struct {
	mux sync.Mutex

	conns map[int][]cachedCDNPool
	// refs tracks active handle references for shared CDN pools.
	refs map[CloseInvoker]int
	// closing tracks pools already known to close pipeline to avoid duplicate
	// queue entries and duplicate Close() calls.
	//
	// Value denotes whether conn is currently queued for worker processing.
	closing map[CloseInvoker]bool

	// closeQueue contains stale CDN pools waiting for async close.
	// Close() may block on unstable network/proxy, so queue is processed by
	// bounded worker count.
	closeQueue   []CloseInvoker
	closePending []CloseInvoker
	closeWorkers int
	closeBusy    int
}

const (
	maxCDNCloseWorkers = 4
	// Historical sizing hint for close backlog in tests and heuristics.
	// Queue growth is controlled by stale-pool production rate and de-dup via
	// closing map, while close fan-out remains bounded by workers.
	maxCDNCloseQueue = 256
)

func () cdnPoolManager {
	return cdnPoolManager{
		conns:   map[int][]cachedCDNPool{},
		refs:    map[CloseInvoker]int{},
		closing: map[CloseInvoker]bool{},
	}
}

func ( cachedCDNPool) ( int64) bool {
	// pool max < 1 means unlimited in pool package.
	if .max < 1 {
		return true
	}
	// Requested max < 1 means unlimited, finite pool does not satisfy it.
	if  < 1 {
		return false
	}
	return .max >= 
}

func ( []cachedCDNPool,  int64) (CloseInvoker, bool) {
	// Pick the smallest pool that still covers requested capacity.
	 := -1
	for ,  := range  {
		if !.covers() {
			continue
		}
		if  == -1 {
			 = 
			continue
		}
		// Prefer tighter finite limit to avoid over-allocating.
		if [].max < 1 {
			 = 
			continue
		}
		if .max > 0 && .max < [].max {
			 = 
		}
	}
	if  == -1 {
		return nil, false
	}
	return [].conn, true
}

func ( *cdnPoolManager) ( CloseInvoker) CloseInvoker {
	,  := .refs[]
	if ! {
		// Keep one cache-owner reference so pool can be reused between
		// sequential downloads.
		 = 1
	}
	.refs[] =  + 1

	return &cdnPoolHandle{
		manager: ,
		conn:    ,
	}
}

func ( *cdnPoolManager) ( CloseInvoker) bool {
	.mux.Lock()
	defer .mux.Unlock()

	,  := .refs[]
	if ! ||  < 1 {
		// Connection is already evicted/closed by another path.
		return false
	}
	--
	if  == 0 {
		delete(.refs, )
		return true
	}
	.refs[] = 
	return false
}

func ( *cdnPoolManager) ( int,  int64) (CloseInvoker, bool) {
	.mux.Lock()
	defer .mux.Unlock()

	,  := pickCDNPool(.conns[], )
	if ! {
		return nil, false
	}
	return .cachedHandleLocked(), true
}

func ( *cdnPoolManager) ( int,  int64,  CloseInvoker) (CloseInvoker, bool) {
	.mux.Lock()
	defer .mux.Unlock()

	if ,  := pickCDNPool(.conns[], );  {
		return .cachedHandleLocked(), true
	}
	.conns[] = append(.conns[], cachedCDNPool{
		conn: ,
		max:  ,
	})
	return .cachedHandleLocked(), false
}

func ( *cdnPoolManager) () []CloseInvoker {
	.mux.Lock()
	defer .mux.Unlock()

	 := map[CloseInvoker]struct{}{}
	 := make([]CloseInvoker, 0, len(.conns)+len(.closeQueue)+len(.closePending))
	 := func( CloseInvoker) {
		if  == nil {
			return
		}
		if ,  := [];  {
			return
		}
		[] = struct{}{}
		 = append(, )
	}
	for ,  := range .conns {
		for ,  := range  {
			(.conn)
		}
	}
	for ,  := range .closeQueue {
		()
	}
	for ,  := range .closePending {
		()
	}
	.conns = map[int][]cachedCDNPool{}
	.refs = map[CloseInvoker]int{}
	.closing = map[CloseInvoker]bool{}
	.closeQueue = nil
	.closePending = nil
	return 
}

func ( *cdnPoolManager) () {
	for len(.closeQueue) < maxCDNCloseQueue && len(.closePending) > 0 {
		 := .closePending[0]
		.closePending[0] = nil
		.closePending = .closePending[1:]
		if  == nil {
			continue
		}

		,  := .closing[]
		if ! ||  {
			// Already closed/queued by another path.
			continue
		}
		.closing[] = true
		.closeQueue = append(.closeQueue, )
	}
}

func ( *cdnPoolManager) ( []CloseInvoker) {
	if len() == 0 {
		return
	}

	for ,  := range  {
		if  == nil {
			continue
		}
		if ,  := .closing[];  {
			continue
		}
		if len(.closeQueue) < maxCDNCloseQueue {
			.closing[] = true
			.closeQueue = append(.closeQueue, )
			continue
		}

		// Queue is saturated, keep pending task deduplicated and promote when
		// worker frees queue slots.
		.closing[] = false
		.closePending = append(.closePending, )
	}

	.refillCloseQueueLocked()

	// Start enough workers to avoid head-of-line blocking on slow Close(),
	// but keep fan-out bounded.
	for .closeWorkers < maxCDNCloseWorkers {
		 := .closeWorkers - .closeBusy
		if  >= len(.closeQueue) {
			break
		}
		.closeWorkers++
		go .runCloseWorker()
	}
}

func ( *cdnPoolManager) () {
	for {
		.mux.Lock()
		if len(.closeQueue) == 0 {
			.closeWorkers--
			.mux.Unlock()
			return
		}
		 := .closeQueue[0]
		.closeQueue[0] = nil
		.closeQueue = .closeQueue[1:]
		.closeBusy++
		.mux.Unlock()

		_ = .Close()

		.mux.Lock()
		delete(.closing, )
		.closeBusy--
		.refillCloseQueueLocked()
		.mux.Unlock()
	}
}

func ( *cdnPoolManager) ( int) {
	.mux.Lock()
	 := append([]cachedCDNPool(nil), .conns[]...)
	for ,  := range  {
		delete(.refs, .conn)
	}
	delete(.conns, )

	 := make([]CloseInvoker, 0, len())
	for ,  := range  {
		 = append(, .conn)
	}
	.enqueueCloseLocked()
	.mux.Unlock()
}

func ( int64) int64 {
	// Keep unlimited pools as-is.
	if  < 1 {
		return 
	}
	// Collapse close finite values into power-of-two buckets to cap the number
	// of cached CDN pools per DC.
	if  < 2 {
		return 
	}
	 := bits.Len64(uint64( - 1))
	// Guard signed overflow for extremely large values.
	if  >= 63 {
		return 
	}
	return int64(1) << 
}