import { isFlyControlAvailable, restartAllMachines } from "./fly-control-service.mjs";
import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
//#region extensions/crypto/src/services/update-service.ts
/**
* Update Service — in-place update from GitHub + Fly restart.
*
* Flow:
*   1. Clone latest code from GitHub (shallow, depth 1)
*   2. Install deps + build + pack tarball
*   3. Extract tarball over the installed extension
*   4. Restart the Fly machine via Machines API
*
* All steps report progress via a callback so the user sees
* live updates in Telegram.
*
* Prerequisites:
*   - FLY_API_TOKEN (for restart) — already required for /flykeys etc.
*   - git, npm, node in the container — present in Dockerfile layer 1
*/
const execFile$1 = promisify(execFile);
const REPO_OWNER = "clawnchdev";
const REPO_NAME = "openclawnch";
const BRANCH = "master";
/** Build the clone URL, injecting GITHUB_TOKEN for private repos. */
function getRepoUrl() {
	const token = process.env.GITHUB_TOKEN;
	if (token) return `https://${token}@github.com/${REPO_OWNER}/${REPO_NAME}.git`;
	return `https://github.com/${REPO_OWNER}/${REPO_NAME}.git`;
}
const WORK_DIR = "/tmp/openclawnch-update";
const EXTENSION_DEST = "/usr/local/lib/node_modules/@clawnch/openclawnch";
/** Max time per shell command (3 min). */
const CMD_TIMEOUT_MS = 3 * 6e4;
async function run(cmd, args, opts = {}) {
	const { stdout } = await execFile$1(cmd, args, {
		cwd: opts.cwd,
		timeout: opts.timeout ?? CMD_TIMEOUT_MS,
		maxBuffer: 10 * 1024 * 1024,
		env: {
			...process.env,
			NODE_ENV: "development"
		}
	});
	return stdout.trim();
}
/** Get the currently installed commit hash (if available). */
function getCurrentCommit() {
	try {
		const commitFile = join(EXTENSION_DEST, ".commit");
		if (existsSync(commitFile)) return readFileSync(commitFile, "utf8").trim();
		const pkg = join(EXTENSION_DEST, "package.json");
		if (existsSync(pkg)) return JSON.parse(readFileSync(pkg, "utf8")).version ?? null;
	} catch {}
	return null;
}
async function checkForUpdates() {
	const current = getCurrentCommit();
	try {
		const remoteRef = (await run("git", [
			"ls-remote",
			getRepoUrl(),
			`refs/heads/${BRANCH}`
		])).split(/\s/)[0] ?? null;
		if (!remoteRef) return {
			available: false,
			currentRef: current,
			remoteRef: null,
			newCommits: []
		};
		if (!current || current !== remoteRef?.slice(0, current.length)) {
			let newCommits = [];
			try {
				await run("git", [
					"ls-remote",
					"--refs",
					getRepoUrl()
				], { timeout: 1e4 });
				newCommits = ["(clone required to see details)"];
			} catch {}
			return {
				available: true,
				currentRef: current,
				remoteRef: remoteRef.slice(0, 12),
				newCommits
			};
		}
		return {
			available: false,
			currentRef: current,
			remoteRef: remoteRef.slice(0, 12),
			newCommits: []
		};
	} catch {
		return {
			available: false,
			currentRef: current,
			remoteRef: null,
			newCommits: []
		};
	}
}
async function performUpdate(progress) {
	const start = Date.now();
	if (!isFlyControlAvailable()) return {
		success: false,
		message: "Fly.io not configured. Set FLY_API_TOKEN to enable /update.\nRun: `fly secrets set FLY_API_TOKEN=\"$(fly tokens create deploy -a <app>)\" -a <app>`"
	};
	try {
		await progress("1/6 Cloning latest code...");
		if (existsSync(WORK_DIR)) rmSync(WORK_DIR, {
			recursive: true,
			force: true
		});
		mkdirSync(WORK_DIR, { recursive: true });
		await run("git", [
			"clone",
			"--depth",
			"1",
			"--branch",
			BRANCH,
			getRepoUrl(),
			WORK_DIR
		]);
		const newCommit = await run("git", [
			"rev-parse",
			"--short",
			"HEAD"
		], { cwd: WORK_DIR });
		await progress(`1/6 Cloned: ${newCommit} — ${await run("git", [
			"log",
			"-1",
			"--format=%s"
		], { cwd: WORK_DIR })}`);
		await progress("2/6 Installing dependencies...");
		let pm = "npm";
		try {
			await run("pnpm", ["--version"]);
			pm = "pnpm";
		} catch {}
		if (pm === "pnpm") await run("pnpm", ["install", "--frozen-lockfile"], { cwd: WORK_DIR });
		else await run("npm", ["install"], { cwd: WORK_DIR });
		await progress("2/6 Dependencies installed");
		await progress("3/6 Building...");
		await run(pm, ["run", "build"], { cwd: WORK_DIR });
		await progress("3/6 Build complete");
		await progress("4/6 Packing extension...");
		const tgzPath = join(WORK_DIR, (await run(pm, ["pack"], { cwd: WORK_DIR })).split("\n").pop()?.trim() ?? "");
		if (!existsSync(tgzPath)) return {
			success: false,
			message: `Pack failed — tarball not found at ${tgzPath}`
		};
		await progress("4/6 Tarball packed");
		await progress("5/6 Installing update...");
		if (existsSync(EXTENSION_DEST)) {
			existsSync(join(EXTENSION_DEST, "node_modules"));
			await run("tar", [
				"xzf",
				tgzPath,
				"-C",
				EXTENSION_DEST,
				"--strip-components=1"
			]);
			const { writeFileSync } = await import("node:fs");
			writeFileSync(join(EXTENSION_DEST, ".commit"), newCommit, "utf8");
		}
		await progress("5/6 Update installed");
		await progress("6/6 Restarting machine...");
		const restarted = await restartAllMachines();
		try {
			rmSync(WORK_DIR, {
				recursive: true,
				force: true
			});
		} catch {}
		const duration = Date.now() - start;
		return {
			success: true,
			message: `Updated to ${newCommit}. ${restarted.length} machine(s) restarting.\nDuration: ${(duration / 1e3).toFixed(1)}s`,
			newCommit,
			durationMs: duration
		};
	} catch (err) {
		try {
			rmSync(WORK_DIR, {
				recursive: true,
				force: true
			});
		} catch {}
		return {
			success: false,
			message: `Update failed: ${err instanceof Error ? err.message : String(err)}`,
			durationMs: Date.now() - start
		};
	}
}
//#endregion
export { checkForUpdates, getCurrentCommit, performUpdate };

//# sourceMappingURL=update-service.mjs.map