"use strict";
/* Socket Lib - Built with rolldown */
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_node_crypto = require('../node/crypto.js');
const require_primordials_buffer = require('../primordials/buffer.js');
const require_primordials_error = require('../primordials/error.js');
const require_primordials_math = require('../primordials/math.js');
const require_primordials_number = require('../primordials/number.js');
const require_node_fs = require('../node/fs.js');
const require_primordials_promise = require('../primordials/promise.js');
const require_fs_safe = require('../fs/safe.js');
const require_http_request_request_attempt = require('./request-attempt.js');
const require_http_request_response_types = require('./response-types.js');
let node_timers_promises = require("node:timers/promises");

//#region src/http-request/download.ts
/**
* @file Streaming file downloads with retries, progress callbacks, and SHA-256
*   verification. `httpDownload` is the public entry — it writes to a
*   randomly-named sibling temp file first, then atomic-renames into place on
*   success so a failed run never leaves a half-written file at `destPath`.
*   `httpDownloadAttempt` is one streaming pass; the retry / hash-check loop
*   sits in `httpDownload`. `httpDownloadAttempt` is exported (not private) per
*   `export-top-level-functions` — it lives here next to the only caller, and
*   it uses `httpRequestAttempt(... { stream: true })` from `request.ts` to
*   obtain the unconsumed `rawResponse` it pipes to disk.
*/
/**
* Download a file from a URL to a local path with redirect support, retry
* logic, and progress callbacks. Uses streaming to avoid loading entire file in
* memory.
*
* The download is streamed directly to disk, making it memory-efficient even
* for large files. Progress callbacks allow for real-time download status
* updates.
*
* Automatically follows HTTP redirects (3xx status codes) by default, making it
* suitable for downloading from services like GitHub releases that redirect to
* CDN URLs.
*
* @example
*   ```ts
*   // Simple download
*   const result = await httpDownload(
*   'https://example.com/file.zip',
*   '/tmp/file.zip',
*   )
*   console.log(`Downloaded ${result.size} bytes to ${result.path}`)
*
*   // Download from GitHub releases (handles 302 redirect automatically)
*   await httpDownload(
*   'https://github.com/org/repo/releases/download/v1.0.0/binary.tar.gz',
*   '/tmp/binary.tar.gz',
*   )
*
*   // With progress tracking
*   await httpDownload('https://example.com/large-file.zip', '/tmp/file.zip', {
*   onProgress: (downloaded, total) => {
*   const percent = ((downloaded / total) * 100).toFixed(1)
*   console.log(`Progress: ${percent}% (${downloaded}/${total} bytes)`)
*   },
*   })
*
*   // With retries and custom timeout
*   await httpDownload('https://example.com/file.zip', '/tmp/file.zip', {
*   retries: 3,
*   retryDelay: 2000,
*   timeout: 300000, // 5 minutes
*   headers: { Authorization: 'Bearer token123' },
*   })
*   ```
*
* @param url - The URL to download from (must start with http:// or https://)
* @param destPath - Absolute path where the file should be saved.
* @param options - Download configuration options.
*
* @returns Promise resolving to download result with path and size
*
* @throws {Error} When all retries are exhausted, download fails, or file
*   cannot be written.
*/
async function httpDownload(url, destPath, options) {
	const { ca, followRedirects = true, headers = {}, logger, maxRedirects = 5, onProgress, progressInterval = 10, retries = 0, retryDelay = 1e3, sha256, timeout = 12e4 } = {
		__proto__: null,
		...options
	};
	/* c8 ignore start */
	let progressCallback;
	if (onProgress) progressCallback = onProgress;
	else if (logger) {
		let lastPercent = 0;
		progressCallback = (downloaded, total) => {
			const percent = total === 0 ? 0 : require_primordials_math.MathFloor(downloaded / total * 100);
			if (percent >= lastPercent + progressInterval) {
				logger.log(`  Progress: ${percent}% (${(downloaded / 1024 / 1024).toFixed(1)} MB / ${(total / 1024 / 1024).toFixed(1)} MB)`);
				lastPercent = percent;
			}
		};
	}
	/* c8 ignore stop */
	const crypto = /* @__PURE__ */ require_node_crypto.getNodeCrypto();
	const fs = /* @__PURE__ */ require_node_fs.getNodeFs();
	const tempPath = `${destPath}.${crypto.randomBytes(6).toString("hex")}.download`;
	if (fs.existsSync(tempPath)) await require_fs_safe.safeDelete(tempPath);
	let lastError;
	for (let attempt = 0; attempt <= retries; attempt++) try {
		const result = await httpDownloadAttempt(url, tempPath, {
			ca,
			followRedirects,
			headers,
			maxRedirects,
			onProgress: progressCallback,
			timeout
		});
		if (sha256) {
			const fileContent = await fs.promises.readFile(tempPath);
			const computedHash = crypto.createHash("sha256").update(fileContent).digest("hex");
			const expectedHash = sha256.toLowerCase();
			if (computedHash.length !== expectedHash.length || !crypto.timingSafeEqual(require_primordials_buffer.BufferFrom(computedHash), Buffer.from(expectedHash))) {
				await require_fs_safe.safeDelete(tempPath);
				throw new require_primordials_error.ErrorCtor(`Checksum verification failed for ${url}\nExpected: ${expectedHash}\nComputed: ${computedHash}`);
			}
		}
		await fs.promises.rename(tempPath, destPath);
		return {
			...result,
			path: destPath
		};
	} catch (e) {
		lastError = e;
		if (fs.existsSync(tempPath)) await require_fs_safe.safeDelete(tempPath);
		if (attempt === retries) break;
		await (0, node_timers_promises.setTimeout)(retryDelay * 2 ** attempt);
	}
	throw lastError || new require_primordials_error.ErrorCtor("Download failed after retries");
}
/**
* Single download attempt using httpRequestAttempt with stream: true.
*/
async function httpDownloadAttempt(url, destPath, options) {
	const { ca, followRedirects = true, headers = {}, maxRedirects = 5, onProgress, timeout = 12e4 } = {
		__proto__: null,
		...options
	};
	const response = await require_http_request_request_attempt.httpRequestAttempt(url, {
		ca,
		followRedirects,
		headers,
		maxRedirects,
		method: "GET",
		stream: true,
		timeout
	});
	if (!response.ok) throw new require_http_request_response_types.HttpResponseError(response, `Download failed: HTTP ${response.status} ${response.statusText}`);
	const res = response.rawResponse;
	/* c8 ignore next 3 */
	if (!res) throw new require_primordials_error.ErrorCtor("Stream response missing rawResponse");
	const { createWriteStream } = /* @__PURE__ */ require_node_fs.getNodeFs();
	const totalSize = require_primordials_number.NumberParseInt(response.headers["content-length"] || "0", 10);
	return await new require_primordials_promise.PromiseCtor((resolve, reject) => {
		let downloadedSize = 0;
		const fileStream = createWriteStream(destPath);
		const cleanupPartial = () => {
			(async () => {
				try {
					await require_fs_safe.safeDelete(destPath);
				} catch {}
			})();
		};
		fileStream.on("error", (error) => {
			fileStream.destroy();
			cleanupPartial();
			reject(new require_primordials_error.ErrorCtor(`Failed to write file: ${error.message}`, { cause: error }));
		});
		res.on("data", (chunk) => {
			downloadedSize += chunk.length;
			if (onProgress && totalSize > 0) onProgress(downloadedSize, totalSize);
		});
		fileStream.on("finish", () => {
			resolve({
				headers: response.headers,
				ok: true,
				path: destPath,
				size: downloadedSize,
				status: response.status,
				statusText: response.statusText
			});
		});
		res.on("error", (error) => {
			fileStream.destroy();
			cleanupPartial();
			reject(error);
		});
		res.pipe(fileStream);
	});
}

//#endregion
exports.httpDownload = httpDownload;
exports.httpDownloadAttempt = httpDownloadAttempt;