import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { homedir } from "node:os";
import { delimiter, join } from "node:path";

const SF_RTK_PATH_ENV = "SF_RTK_PATH";
const SF_RTK_DISABLED_ENV = "SF_RTK_DISABLED";
const SF_RTK_REWRITE_TIMEOUT_MS_ENV = "SF_RTK_REWRITE_TIMEOUT_MS";
const RTK_TELEMETRY_DISABLED_ENV = "RTK_TELEMETRY_DISABLED";
const RTK_REWRITE_TIMEOUT_MS = 5_000;
function isTruthy(value) {
	if (!value) return false;
	const normalized = value.trim().toLowerCase();
	return normalized === "1" || normalized === "true" || normalized === "yes";
}
function getRewriteTimeoutMs(env = process.env) {
	const configured = Number.parseInt(
		env[SF_RTK_REWRITE_TIMEOUT_MS_ENV] ?? "",
		10,
	);
	if (Number.isFinite(configured) && configured > 0) {
		return configured;
	}
	return RTK_REWRITE_TIMEOUT_MS;
}
export function isRtkEnabled(env = process.env) {
	return !isTruthy(env[SF_RTK_DISABLED_ENV]);
}
export function buildRtkEnv(env = process.env) {
	return {
		...env,
		[RTK_TELEMETRY_DISABLED_ENV]: "1",
	};
}
function getManagedRtkDir(env = process.env) {
	return join(env.SF_HOME || join(homedir(), ".sf"), "agent", "bin");
}
function getRtkBinaryName(platform = process.platform) {
	return platform === "win32" ? "rtk.exe" : "rtk";
}
function getPathValue(env) {
	const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path");
	return pathKey ? env[pathKey] : env.PATH;
}
function resolvePathCandidates(pathValue) {
	if (!pathValue) return [];
	return pathValue
		.split(delimiter)
		.map((part) => part.trim())
		.filter(Boolean);
}
function resolveSystemRtkPath(pathValue, platform = process.platform) {
	const candidates =
		platform === "win32" ? ["rtk.exe", "rtk.cmd", "rtk.bat", "rtk"] : ["rtk"];
	for (const dir of resolvePathCandidates(pathValue)) {
		for (const candidate of candidates) {
			const fullPath = join(dir, candidate);
			if (existsSync(fullPath)) {
				return fullPath;
			}
		}
	}
	return null;
}
export function resolveRtkBinaryPath(options = {}) {
	const env = options.env ?? process.env;
	const platform = options.platform ?? process.platform;
	const explicitPath = options.binaryPath ?? env[SF_RTK_PATH_ENV];
	if (explicitPath && existsSync(explicitPath)) {
		return explicitPath;
	}
	const managedDir = getManagedRtkDir(env);
	const managedPath = join(managedDir, getRtkBinaryName(platform));
	if (existsSync(managedPath)) {
		return managedPath;
	}
	// On Windows, also check for rtk.cmd in the managed dir (used by test fake RTK
	// and any wrapper-style installs where a .cmd launcher accompanies the binary).
	if (platform === "win32") {
		const managedCmd = join(managedDir, "rtk.cmd");
		if (existsSync(managedCmd)) {
			return managedCmd;
		}
	}
	return resolveSystemRtkPath(options.pathValue ?? getPathValue(env), platform);
}
export function rewriteCommandWithRtk(command, options = {}) {
	const env = options.env ?? process.env;
	if (!command.trim()) return command;
	if (!isRtkEnabled(env)) return command;
	const binaryPath = options.binaryPath ?? resolveRtkBinaryPath({ env });
	if (!binaryPath) return command;
	const run = options.spawnSyncImpl ?? spawnSync;
	const result = run(binaryPath, ["rewrite", command], {
		encoding: "utf-8",
		env: buildRtkEnv(env),
		stdio: ["ignore", "pipe", "ignore"],
		timeout: getRewriteTimeoutMs(env),
		// .cmd/.bat wrappers (used by fake-rtk in tests) require shell:true on Windows
		shell: /\.(cmd|bat)$/i.test(binaryPath),
	});
	if (result.error) return command;
	if (result.status !== 0 && result.status !== 3) return command;
	const rewritten = (result.stdout ?? "").trimEnd();
	return rewritten || command;
}
