import { createLogger } from "../utils/logger/index.js";
import { init_logger } from "../utils/logger.js";
import { init_paths, resolveBinDir } from "../config/paths.js";
import { extractFrpcFromReleaseArchive, frpcReleaseArchiveExtension, nodePlatformForFrpTarget } from "./frpc-extract.js";
import { randomBytes } from "node:crypto";
import { chmodSync, createWriteStream, existsSync, mkdirSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";
//#region src/tunnel/frpc-binary.ts
init_paths();
init_logger();
const log = createLogger("TunnelFrpc");
const FRPC_VERSION = "0.62.1";
const PLATFORM_MAP = {
	darwin: "darwin",
	linux: "linux",
	win32: "windows"
};
const ARCH_MAP = {
	x64: "amd64",
	arm64: "arm64",
	ia32: "386"
};
function buildFrpcReleaseBasename(frpPlatform, arch, version = FRPC_VERSION) {
	return `frp_${version}_${frpPlatform}_${arch}`;
}
function frpcDownloadUrlsForTarget(frpPlatform, arch, version = FRPC_VERSION) {
	const folder = buildFrpcReleaseBasename(frpPlatform, arch, version);
	const ext = frpcReleaseArchiveExtension(frpPlatform);
	return [
		`https://frp.xopc.ai/bin/${folder}${ext}`,
		`https://github.com/fatedier/frp/releases/download/v${version}/${folder}${ext}`,
		`https://ghfast.top/https://github.com/fatedier/frp/releases/download/v${version}/${folder}${ext}`
	];
}
function frpcPlatformArch() {
	const platform = PLATFORM_MAP[process.platform] ?? process.platform;
	const arch = ARCH_MAP[process.arch] ?? process.arch;
	return {
		platform,
		arch,
		folder: buildFrpcReleaseBasename(platform, arch)
	};
}
function frpcDownloadUrls() {
	const { platform, arch } = frpcPlatformArch();
	return frpcDownloadUrlsForTarget(platform, arch);
}
async function downloadToFile(url, destPath, onProgress) {
	const res = await fetch(url);
	if (!res.ok || !res.body) throw new Error(`Download failed: ${res.status} ${url}`);
	const contentLength = res.headers.get("content-length");
	const totalBytes = contentLength && Number.isFinite(Number(contentLength)) ? Number(contentLength) : null;
	let bytesReceived = 0;
	const report = () => {
		onProgress?.({
			phase: "downloading",
			url,
			bytesReceived,
			totalBytes,
			percent: totalBytes && totalBytes > 0 ? Math.min(100, Math.round(bytesReceived / totalBytes * 100)) : null
		});
	};
	report();
	const nodeStream = Readable.fromWeb(res.body);
	nodeStream.on("data", (chunk) => {
		bytesReceived += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
		report();
	});
	await pipeline(nodeStream, createWriteStream(destPath));
	report();
}
/** Set after a successful tunnel start so subprocesses can resolve the same binary. */
function publishFrpcPathForProcess(binPath) {
	process.env.XOPC_FRPC_PATH = binPath;
}
/** Clear runtime frpc path when the tunnel stops (no bundled Electron binary). */
function clearFrpcPathForProcess() {
	delete process.env.XOPC_FRPC_PATH;
}
async function ensureFrpcBinary(opts) {
	const onProgress = opts?.onProgress;
	const binName = `frpc${process.platform === "win32" ? ".exe" : ""}`;
	const fromEnv = process.env.XOPC_FRPC_PATH?.trim();
	if (fromEnv && existsSync(fromEnv)) return fromEnv;
	const cacheDir = resolveBinDir();
	mkdirSync(cacheDir, { recursive: true });
	const cachePath = join(cacheDir, binName);
	if (existsSync(cachePath)) return cachePath;
	const tmpBase = join(tmpdir(), `xopc-frpc-${randomBytes(6).toString("hex")}`);
	mkdirSync(tmpBase, { recursive: true });
	const { platform, arch, folder } = frpcPlatformArch();
	const archivePath = join(tmpBase, `frpc${frpcReleaseArchiveExtension(platform)}`);
	const urls = frpcDownloadUrls();
	let lastErr;
	try {
		for (const url of urls) try {
			log.info({ url }, "Downloading frpc");
			await downloadToFile(url, archivePath, onProgress);
			onProgress?.({
				phase: "extracting",
				url,
				bytesReceived: 0,
				totalBytes: null,
				percent: null
			});
			await extractFrpcFromReleaseArchive(archivePath, cachePath, folder, nodePlatformForFrpTarget(platform));
			if (process.platform !== "win32") chmodSync(cachePath, 493);
			return cachePath;
		} catch (err) {
			lastErr = err;
			log.warn({
				url,
				err
			}, "frpc download attempt failed");
		}
	} finally {
		rmSync(tmpBase, {
			recursive: true,
			force: true
		});
	}
	throw new Error(`Failed to download frpc v${FRPC_VERSION} for ${platform}/${arch}: ${lastErr instanceof Error ? lastErr.message : String(lastErr)}`);
}
//#endregion
export { FRPC_VERSION, buildFrpcReleaseBasename, clearFrpcPathForProcess, ensureFrpcBinary, frpcDownloadUrlsForTarget, publishFrpcPathForProcess };

//# sourceMappingURL=frpc-binary.js.map