package downloader

import (
	
	
	
	

	
	

	
)

// Builder is a download builder.
type Builder struct {
	downloader *Downloader

	schema schema
	hashes []tg.FileHash
	// verify controls legacy outer verifier (reader + verifier queue).
	// CDN redirect path has mandatory protocol-level verification in cdn schema,
	// independent from this flag.
	verify  bool
	threads int
}

func ( *Downloader,  schema) *Builder {
	return &Builder{
		schema:     ,
		threads:    1,
		downloader: ,
	}
}

// WithThreads sets downloading goroutines limit.
func ( *Builder) ( int) *Builder {
	if  > 0 {
		.threads = 
	}
	return 
}

// WithRetryHandler sets callback for transient download errors that are retried
// internally.
//
// Handler can be called concurrently from download workers.
func ( *Builder) ( RetryHandler) *Builder {
	switch s := .schema.(type) {
	case master:
		.retryHandler = 
		.schema = 
	case web:
		.retryHandler = 
		.schema = 
	case *cdn:
		.retryHandler = 
	}

	return 
}

// WithVerify controls global hash verification behavior.
//
// `true` enables classic verifier reader (preloads hash queue and validates all
// chunks, both legacy and CDN).
// `false` disables classic verifier reader.
//
// If not called explicitly:
// - non-CDN path preserves old behavior (no upfront hash requests);
// - CDN path enables strict inline CDN verification after redirect.
//
// Use WithVerify(true) to force verifier-queue mode on all paths.
func ( *Builder) ( bool) *Builder {
	.verify = 
	return 
}

func ( *Builder) ( master,  bool) *Builder {
	 := *
	 := 
	// Keep explicit switch in schema to guarantee old request path when
	// CDN is disabled or unavailable.
	.allowCDN = 
	.schema = 
	.hashes = nil
	return &
}

func ( *Builder) ( master,  CDNProvider) *Builder {
	// Enable redirect errors on master schema (`upload.fileCdnRedirect`) while
	// still serving regular files from master when redirect is not required.
	.allowCDN = true

	 := *
	// Avoid outer verifier on default path to keep non-redirect requests
	// equivalent to legacy master flow; CDN schema still verifies redirected
	// chunks inline according to Telegram CDN protocol.
	 := !.verify
	.hashes = nil
	.schema = newCDNSchema(
		,
		,
		.downloader.pool,
		int64(.threads),
		,
		.retryHandler,
	)
	return &
}

func ( *Builder) () bool {
	// CDN redirect flow is explicit-only to avoid hidden behavior changes and
	// preserve backwards compatibility for callers that never opted in.
	if .downloader.allowCDN == nil {
		return false
	}
	return *.downloader.allowCDN
}

func ( schema) func() error {
	if ,  := .(interface{ () error });  {
		return .
	}
	return nil
}

func ( *Builder) () ( *Builder,  func() error,  error) {
	,  := .schema.(master)
	if ! {
		return , closeSchema(.schema), nil
	}

	// Fast path compatibility guarantee:
	// if CDN is not explicitly allowed we keep legacy master flow exactly as is,
	// without CDN pool creation and without extra hash requests.
	if !.shouldAllowCDN() {
		 := .prepareMaster(, false)
		return , closeSchema(.schema), nil
	}

	// Even with AllowCDN=true, fallback to legacy master flow if client does not
	// provide CDN transport factory.
	,  := .client.(CDNProvider)
	if ! {
		 := .prepareMaster(, false)
		return , closeSchema(.schema), nil
	}

	 := .prepareCDNPath(, )
	return , closeSchema(.schema), nil
}

func ( *Builder) () *reader {
	if .verify {
		return verifiedReader(.schema, newVerifier(.schema, .hashes...))
	}

	return plainReader(.schema, .downloader.partSize)
}

// Stream downloads file to given io.Writer.
// NB: in this mode download can't be parallel.
func ( *Builder) ( context.Context,  io.Writer) ( tg.StorageFileTypeClass,  error) {
	, ,  := .prepare()
	if  != nil {
		return nil, 
	}
	defer func() {
		if  != nil {
			multierr.AppendInto(&, ())
		}
	}()
	,  := .downloader.stream(, .reader(), )
	return , 
}

// Parallel downloads file to given io.WriterAt.
func ( *Builder) ( context.Context,  io.WriterAt) ( tg.StorageFileTypeClass,  error) {
	, ,  := .prepare()
	if  != nil {
		return nil, 
	}
	defer func() {
		if  != nil {
			multierr.AppendInto(&, ())
		}
	}()

	,  := .downloader.parallel(, .reader(), .threads, )
	return , 
}

// ToPath downloads file to given path.
func ( *Builder) ( context.Context,  string) ( tg.StorageFileTypeClass,  error) {
	,  := os.Create(filepath.Clean())
	if  != nil {
		return nil, errors.Wrap(, "create output file")
	}
	defer func() {
		multierr.AppendInto(&, .Close())
	}()

	,  := .Parallel(, )
	return , 
}