#!/usr/bin/env node

/**
 * Build script for the SF native Rust addon.
 *
 * Usage:
 *   node native/scripts/build.js          # release build
 *   node native/scripts/build.js --dev    # debug build
 *
 * Runs `cargo build` in the engine crate directory and copies the resulting
 * shared library to `native/addon/` with a `.node` extension so Node.js
 * can load it via `require()`.
 */

import { execSync } from "node:child_process";
import * as fs from "node:fs";
import * as path from "node:path";

const nativeRoot = path.resolve(import.meta.dirname, "..");
const engineDir = path.join(nativeRoot, "crates", "engine");
const addonDir = path.join(nativeRoot, "addon");

const isDev = process.argv.includes("--dev");
const profile = isDev ? "debug" : "release";
const cargoArgs = ["build"];
if (!isDev) cargoArgs.push("--release");

function shellValue(command) {
	try {
		return (
			execSync(command, {
				stdio: ["ignore", "pipe", "ignore"],
				env: process.env,
			})
				.toString()
				.trim() || undefined
		);
	} catch {
		return undefined;
	}
}

function getZlibLibDir() {
	return (
		process.env.ZLIB_LIB_DIR || shellValue("pkg-config --variable=libdir zlib")
	);
}

function getCargoEnvironment() {
	const zlibLibDir = getZlibLibDir();
	const defaultRustFlags = process.env.RUSTFLAGS || "-C target-cpu=native";
	const rustFlags =
		zlibLibDir && !defaultRustFlags.includes(zlibLibDir)
			? `${defaultRustFlags} -L native=${zlibLibDir}`
			: defaultRustFlags;
	const libraryPath = [zlibLibDir, process.env.LIBRARY_PATH]
		.filter(Boolean)
		.join(path.delimiter);

	return {
		...process.env,
		// Optimize for native CPU when building locally
		RUSTFLAGS: rustFlags,
		...(libraryPath ? { LIBRARY_PATH: libraryPath } : {}),
	};
}

function getCargoTargetDirectory() {
	if (process.env.CARGO_TARGET_DIR) {
		return path.resolve(process.env.CARGO_TARGET_DIR);
	}

	const metadataRaw = execSync("cargo metadata --format-version 1 --no-deps", {
		cwd: engineDir,
		stdio: ["ignore", "pipe", "inherit"],
		env: getCargoEnvironment(),
	}).toString();
	const metadata = JSON.parse(metadataRaw);
	if (
		typeof metadata.target_directory !== "string" ||
		metadata.target_directory.length === 0
	) {
		throw new Error("cargo metadata did not return a target_directory");
	}
	return path.resolve(metadata.target_directory);
}

console.log(`Building forge-engine (${profile})...`);

try {
	execSync(`cargo ${cargoArgs.join(" ")}`, {
		cwd: engineDir,
		stdio: "inherit",
		env: getCargoEnvironment(),
	});
} catch {
	process.exit(1);
}

// Locate the built library using Cargo's actual target directory. Under Nix this
// is often redirected to a shared cache path rather than native/target.
const cargoTargetRoot = getCargoTargetDirectory();

const targetDir = path.join(cargoTargetRoot, profile);
const platformTag = `${process.platform}-${process.arch}`;

const libraryNames = {
	darwin: "libforge_engine.dylib",
	linux: "libforge_engine.so",
	win32: "forge_engine.dll",
};

const libName = libraryNames[process.platform];
if (!libName) {
	console.error(`Unsupported platform: ${process.platform}`);
	process.exit(1);
}

const sourcePath = path.join(targetDir, libName);
if (!fs.existsSync(sourcePath)) {
	console.error(`Built library not found at: ${sourcePath}`);
	process.exit(1);
}

fs.mkdirSync(addonDir, { recursive: true });

const destFilename = isDev
	? "forge_engine.dev.node"
	: `forge_engine.${platformTag}.node`;
const destPath = path.join(addonDir, destFilename);

fs.copyFileSync(sourcePath, destPath);
console.log(`Installed: ${destPath}`);

// Also copy into the npm platform package so `require('@singularity-forge/engine-<platform>')`
// works when the package is symlinked into node_modules during local development.
if (!isDev) {
	const platformPackageMap = {
		"darwin-arm64": "darwin-arm64",
		"darwin-x64": "darwin-x64",
		"linux-x64": "linux-x64-gnu",
		"linux-arm64": "linux-arm64-gnu",
		"win32-x64": "win32-x64-msvc",
	};
	const npmSuffix = platformPackageMap[platformTag];
	if (npmSuffix) {
		const npmPackageDir = path.join(nativeRoot, "npm", npmSuffix);
		const npmDest = path.join(npmPackageDir, "forge_engine.node");
		if (fs.existsSync(npmPackageDir)) {
			fs.copyFileSync(sourcePath, npmDest);
			console.log(`Installed npm package binary: ${npmDest}`);
		}
	}
}

console.log("Build complete.");
