package downloader

import (
	
	

	

	
	
)

// cdn is a download schema that starts on the master DC and switches to CDN on
// upload.fileCdnRedirect without losing the original request.
type cdn struct {
	provider CDNProvider
	client   Client
	pool     *bin.Pool
	// retryHandler observes retried transient downloader errors.
	retryHandler RetryHandler

	// master preserves regular path that may return redirect errors when
	// allowCDN=true.
	master master
	// max is forwarded to provider pool creation, usually mapped from number of
	// download threads.
	max int64
	// verify enables inline verification for decrypted CDN chunks.
	verify bool

	// stateMux guards mode/redirect/client pointer/revision.
	stateMux sync.RWMutex
	// refreshMux serializes redirect refreshes so only one goroutine asks master
	// for new token when CDN reports token invalid.
	refreshMux sync.Mutex
	// clientMux serializes CDN client (re)creation per schema instance.
	clientMux sync.Mutex
	// hashesMux guards in-memory cache of CDN hashes by offset.
	hashesMux sync.RWMutex
	// windowsMux guards bounded cache of verified CDN hash windows used to
	// handle custom part sizes that split hash windows.
	windowsMux sync.Mutex
	// windowsLoad deduplicates concurrent fetches of the same full hash window.
	windowsLoad singleflight.Group

	mode        cdnMode
	redirect    *tg.UploadFileCDNRedirect
	cdn         CDN
	closer      io.Closer
	clientDC    int
	rev         uint64
	hashes      map[int64]tg.FileHash
	hashOffsets []int64
	windows     map[int64][]byte
	windowsFIFO []int64
}

var _ schema = (*cdn)(nil)

type cdnMode uint8

const (
	// modeMaster means request master first and switch only on redirect.
	modeMaster cdnMode = iota
	// modeCDN means active redirect exists and chunks should be fetched from CDN.
	modeCDN
)

// maxVerifiedWindowCache bounds memory used by split-window verification.
//
// Split windows are needed only when downloader part size does not align with
// Telegram CDN hash window size (typically 128KB). In that case we may fetch a
// full hash window once, verify it, and reuse verified bytes for neighboring
// chunks. A small bounded cache is enough because sequential/parallel readers
// usually work on nearby offsets.
const maxVerifiedWindowCache = 16

func (
	 master,
	 CDNProvider,
	 *bin.Pool,
	 int64,
	 bool,
	 RetryHandler,
) *cdn {
	if  < 1 {
		 = 1
	}

	return &cdn{
		provider:     ,
		client:       .client,
		pool:         ,
		retryHandler: ,
		master:       ,
		max:          ,
		verify:       ,
		mode:         modeMaster,
	}
}

func ( *cdn) ( string,  int,  error) {
	if  < 1 ||  == nil || .retryHandler == nil {
		return
	}
	.retryHandler(RetryEvent{
		Operation: ,
		Attempt:   ,
		Err:       ,
	})
}

func ( *cdn) () error {
	// Close is called by Builder defer path and should release only schema-local
	// CDN resources. Shared client-level pools are managed in telegram package.
	.stateMux.Lock()
	 := .closer
	.cdn = nil
	.closer = nil
	.clientDC = 0
	.stateMux.Unlock()

	if  != nil {
		return .Close()
	}
	return nil
}

func ( *cdn) () {
	// Internal best-effort close used on fingerprint/token recovery loops.
	.stateMux.Lock()
	 := .closer
	.cdn = nil
	.closer = nil
	.clientDC = 0
	.stateMux.Unlock()

	if  != nil {
		_ = .Close()
	}
}

func ( *cdn) () ( cdnMode,  *tg.UploadFileCDNRedirect,  uint64) {
	.stateMux.RLock()
	defer .stateMux.RUnlock()

	return .mode, .redirect, .rev
}

func ( *cdn) ( *tg.UploadFileCDNRedirect) {
	.stateMux.Lock()
	.mode = modeCDN
	.redirect = 
	.rev++
	.stateMux.Unlock()

	// Redirect update invalidates hash cache scope (file token / offset range
	// may change). Seed with hashes returned in redirect when available.
	.resetHashes()
	.resetWindows()
	if  != nil {
		.cacheHashes(.FileHashes)
	}
}

func ( *cdn) () {
	.stateMux.Lock()
	.mode = modeMaster
	.redirect = nil
	.rev++
	.stateMux.Unlock()

	// Leaving CDN mode invalidates CDN hash cache.
	.resetHashes()
	.resetWindows()
}