import { PACKAGE_VERSION, init_package_version } from "../../../package-version.js";
import { createLogger } from "../../../utils/logger/index.js";
import { init_logger } from "../../../utils/logger.js";
import { loadConfig } from "../../../config/loader.js";
import "../../../config/index.js";
import { acquireUpdateLock } from "../../../infra/update-lock.js";
import { normalizeUpdateChannel } from "../../../infra/update-channels.js";
import { detectInstallKind, resolvePackageRoot } from "../../../infra/update-check.js";
import { getUpdateAvailable, runGatewayUpdateCheck } from "../../../infra/update-startup.js";
import { runAutoUpdateCommand, runAutoUpdateCommandWithProgress } from "../../../infra/update-runner.js";
import { streamSSE } from "hono/streaming";
//#region src/gateway/hono/routes/update.ts
init_package_version();
init_logger();
const log = createLogger("GatewayUpdate");
function parseUpdateCliJson(stdout) {
	const t = stdout.trim();
	if (!t) return null;
	try {
		const parsed = JSON.parse(t);
		return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
	} catch {
		const lines = t.split("\n").filter(Boolean);
		for (let i = lines.length - 1; i >= 0; i--) {
			const line = lines[i].trim();
			if (!line.startsWith("{")) continue;
			try {
				const parsed = JSON.parse(line);
				if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
			} catch {}
		}
	}
	return null;
}
function isPreconditionFail(x) {
	return !x.ok;
}
async function npmUpdatePreconditions(service) {
	const channel = normalizeUpdateChannel(loadConfig(service.getHealth().configPath).update?.channel) ?? "stable";
	const root = await resolvePackageRoot();
	if (root) {
		if (await detectInstallKind(root) === "git") return {
			ok: false,
			status: 400,
			body: {
				ok: false,
				error: "git-checkout",
				message: "Running from a git checkout. Use `git pull` in the repo, or install from npm to use one-click update."
			}
		};
	}
	return {
		ok: true,
		channel,
		root
	};
}
function registerUpdateRoutes(authenticated, deps) {
	const { strictRateLimitMiddleware, service } = deps;
	/**
	* GET /api/update/status
	*/
	authenticated.get("/api/update/status", (c) => {
		const update = getUpdateAvailable();
		return c.json({
			ok: true,
			payload: {
				currentVersion: PACKAGE_VERSION,
				updateAvailable: update !== null,
				latestVersion: update?.latestVersion ?? null,
				channel: update?.channel ?? null
			}
		});
	});
	/**
	* POST /api/update/check
	*/
	authenticated.post("/api/update/check", strictRateLimitMiddleware, async (c) => {
		await runGatewayUpdateCheck({
			config: loadConfig(service.getHealth().configPath),
			force: true,
			onUpdateAvailableChange: (update) => {
				service.emit("update.available", update);
			}
		});
		const result = getUpdateAvailable();
		return c.json({
			ok: true,
			payload: {
				currentVersion: PACKAGE_VERSION,
				updateAvailable: result !== null,
				latestVersion: result?.latestVersion ?? null,
				channel: result?.channel ?? null
			}
		});
	});
	/**
	* POST /api/update/run — one-click npm install (OpenClaw-style). Rejects git checkouts.
	*/
	authenticated.post("/api/update/run", strictRateLimitMiddleware, async (c) => {
		const pre = await npmUpdatePreconditions(service);
		if (isPreconditionFail(pre)) return c.json(pre.body, pre.status);
		const lock = await acquireUpdateLock("gateway");
		if (!lock) return c.json({
			ok: false,
			error: "busy",
			message: "Another update is already in progress."
		}, 409);
		const { channel, root } = pre;
		try {
			log.info({ channel }, "Gateway: starting one-click npm update");
			const result = await runAutoUpdateCommand({
				channel,
				root
			});
			const parsed = parseUpdateCliJson(result.stdout ?? "");
			if (result.ok && parsed?.status === "skipped" && parsed?.reason === "git-checkout") return c.json({
				ok: false,
				error: "git-checkout",
				message: String(parsed.message ?? "Git checkout — use git pull instead.")
			}, 400);
			if (!result.ok) {
				const installMessage = typeof parsed?.message === "string" ? parsed.message : typeof parsed?.stderrTail === "string" ? parsed.stderrTail : void 0;
				log.warn({
					channel,
					exitCode: result.exitCode,
					reason: result.reason
				}, "Gateway: one-click npm update failed");
				return c.json({
					ok: false,
					error: "update-failed",
					message: installMessage || result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? "unknown"}`,
					result: parsed
				});
			}
			log.info({ channel }, "Gateway: one-click npm update finished");
			return c.json({
				ok: true,
				result: parsed
			});
		} catch (err) {
			log.error({
				err,
				channel
			}, "Gateway: one-click npm update threw");
			return c.json({
				ok: false,
				error: "internal",
				message: err instanceof Error ? err.message : String(err)
			}, 500);
		} finally {
			await lock.release();
		}
	});
	/**
	* POST /api/update/run/stream — SSE-streamed npm update with progress lines.
	*/
	authenticated.post("/api/update/run/stream", strictRateLimitMiddleware, async (c) => {
		const pre = await npmUpdatePreconditions(service);
		if (isPreconditionFail(pre)) return c.json(pre.body, pre.status);
		const { channel, root } = pre;
		return streamSSE(c, async (stream) => {
			const lock = await acquireUpdateLock("gateway");
			if (!lock) {
				await stream.writeSSE({
					event: "result",
					data: JSON.stringify({
						ok: false,
						error: "busy",
						message: "Another update is already in progress."
					})
				});
				return;
			}
			try {
				log.info({ channel }, "Gateway: starting streamed one-click npm update");
				const result = await runAutoUpdateCommandWithProgress({
					channel,
					root,
					onProgress: async (line, source) => {
						await stream.writeSSE({
							event: "progress",
							data: JSON.stringify({
								line,
								source
							})
						});
					}
				});
				const parsed = parseUpdateCliJson(result.stdout ?? "");
				if (result.ok && parsed?.status === "skipped" && parsed?.reason === "git-checkout") {
					await stream.writeSSE({
						event: "result",
						data: JSON.stringify({
							ok: false,
							error: "git-checkout",
							message: String(parsed.message ?? "Git checkout — use git pull instead.")
						})
					});
					return;
				}
				if (!result.ok) {
					const installMessage = typeof parsed?.message === "string" ? parsed.message : typeof parsed?.stderrTail === "string" ? parsed.stderrTail : void 0;
					await stream.writeSSE({
						event: "result",
						data: JSON.stringify({
							ok: false,
							error: "update-failed",
							message: installMessage || result.stderr?.trim() || result.reason || `Update exited with code ${result.exitCode ?? "unknown"}`,
							result: parsed,
							exitCode: result.exitCode,
							reason: result.reason
						})
					});
					return;
				}
				await stream.writeSSE({
					event: "result",
					data: JSON.stringify({
						ok: true,
						result: parsed
					})
				});
			} catch (err) {
				log.error({
					err,
					channel
				}, "Gateway: streamed npm update threw");
				await stream.writeSSE({
					event: "result",
					data: JSON.stringify({
						ok: false,
						error: "internal",
						message: err instanceof Error ? err.message : String(err)
					})
				});
			} finally {
				await lock.release();
			}
		});
	});
}
//#endregion
export { registerUpdateRoutes };

//# sourceMappingURL=update.js.map