#!/usr/bin/env node
const { spawnSync } = require("node:child_process");
const { existsSync, readdirSync, statSync } = require("node:fs");
const { join, resolve } = require("node:path");

const root = resolve(__dirname, "..");
const srcResources = join(root, "src", "resources");
const distResources = join(root, "dist", "resources");
const stampPath = join(distResources, ".sf-resource-build-stamp");
const copyResourcesScript = join(root, "scripts", "copy-resources.cjs");

function latestMtimeMs(path) {
	let latest = 0;
	const stack = [path];

	while (stack.length > 0) {
		const current = stack.pop();
		if (!current) continue;

		let entries;
		try {
			entries = readdirSync(current, { withFileTypes: true });
		} catch {
			continue;
		}

		for (const entry of entries) {
			const entryPath = join(current, entry.name);
			let stat;
			try {
				stat = statSync(entryPath);
			} catch {
				continue;
			}
			latest = Math.max(latest, stat.mtimeMs);
			if (entry.isDirectory()) {
				stack.push(entryPath);
			}
		}
	}

	return latest;
}

function sourceInputsMtimeMs() {
	return Math.max(
		latestMtimeMs(srcResources),
		existsSync(copyResourcesScript) ? statSync(copyResourcesScript).mtimeMs : 0,
		existsSync(join(root, "tsconfig.resources.json"))
			? statSync(join(root, "tsconfig.resources.json")).mtimeMs
			: 0,
	);
}

function hasCompleteResourceBuild() {
	return (
		existsSync(stampPath) &&
		existsSync(join(distResources, "SF-WORKFLOW.md")) &&
		existsSync(join(distResources, "agents")) &&
		existsSync(join(distResources, "extensions"))
	);
}

function shouldRebuild() {
	if (process.env.SF_DEV_CLI_SKIP_RESOURCE_BUILD === "1") return false;
	if (process.env.SF_SKIP_SOURCE_RESOURCE_BUILD === "1") return false;
	if (!hasCompleteResourceBuild()) return true;

	let stampMtime = 0;
	try {
		stampMtime = statSync(stampPath).mtimeMs;
	} catch {
		return true;
	}

	return sourceInputsMtimeMs() > stampMtime;
}

if (shouldRebuild()) {
	console.error(
		"[forge] Source resources changed; rebuilding dist/resources before launch...",
	);
	const result = spawnSync(process.execPath, [copyResourcesScript], {
		cwd: root,
		stdio: "inherit",
		env: process.env,
	});

	if (result.status !== 0) {
		process.exit(result.status ?? 1);
	}
}

/**
 * Auto-rebuild workspace packages whose src/ is newer than dist/.
 *
 * Without this, edits to packages/coding-agent/src/* (or any other workspace
 * package src) silently land while the dist stays stale — agents continue
 * loading the old compiled JS and operators see "why didn't my edit take
 * effect?" symptoms (observed 2026-05-17 with the AST tools wire-in).
 *
 * Skipped:
 *   - native: Rust build takes 5–10 min; trigger manually via
 *     `node rust-engine/scripts/build.js --dev` instead.
 *   - any package set in SF_SKIP_WORKSPACE_AUTOBUILD (comma-separated names).
 *
 * Override the whole step with SF_SKIP_WORKSPACE_AUTOBUILD=all.
 */
const WORKSPACE_AUTOBUILD_PACKAGES = [
	"agent-core",
	"ai",
	"coding-agent",
	"daemon",
	"google-gemini-cli-provider",
	"openai-codex-provider",
	"rpc-client",
	"tui",
];

function packageNeedsRebuild(pkgName) {
	const pkgDir = join(root, "packages", pkgName);
	const pkgSrc = join(pkgDir, "src");
	const pkgDist = join(pkgDir, "dist");
	if (!existsSync(pkgSrc) || !existsSync(pkgDist)) return false;
	const srcMtime = latestMtimeMs(pkgSrc);
	const distMtime = latestMtimeMs(pkgDist);
	// Add a small grace window so files written within the same second as a
	// dist sync don't trigger redundant rebuilds.
	return srcMtime > distMtime + 100;
}

function rebuildWorkspacePackagesIfStale() {
	const skip = (process.env.SF_SKIP_WORKSPACE_AUTOBUILD || "")
		.split(",")
		.map((s) => s.trim())
		.filter(Boolean);
	if (skip.includes("all")) return;

	const stale = WORKSPACE_AUTOBUILD_PACKAGES.filter(
		(pkg) => !skip.includes(pkg) && packageNeedsRebuild(pkg),
	);
	if (stale.length === 0) return;

	console.error(
		`[forge] Workspace src newer than dist for: ${stale.join(", ")} — rebuilding...`,
	);
	for (const pkg of stale) {
		const result = spawnSync(
			"npm",
			["run", "build", "-w", `@singularity-forge/${pkg}`],
			{ cwd: root, stdio: "inherit", env: process.env },
		);
		if (result.status !== 0) {
			console.error(`[forge] FAILED to rebuild packages/${pkg}`);
			process.exit(result.status ?? 1);
		}
	}
}

rebuildWorkspacePackagesIfStale();
