#!/usr/bin/env node
/**
 * link-workspace-packages.cjs
 *
 * Creates node_modules/@singularity-forge/* symlinks pointing to shipped
 * packages/* directories.
 *
 * During development, npm workspaces creates these automatically. But in the
 * published tarball, workspace packages are shipped under packages/ (via the
 * "files" field) and the @singularity-forge/* imports in compiled code need node_modules/@singularity-forge/*
 * to resolve. This script bridges the gap.
 *
 * Runs as part of postinstall (before any ESM code that imports @singularity-forge/*).
 *
 * On Windows without Developer Mode or administrator rights, creating symlinks
 * (even NTFS junctions) can fail with EPERM. In that case we fall back to
 * cpSync (directory copy) which works universally.
 */
const {
	existsSync,
	mkdirSync,
	symlinkSync,
	cpSync,
	lstatSync,
	readlinkSync,
	unlinkSync,
} = require("node:fs");
const { resolve, join } = require("node:path");

const root = resolve(__dirname, "..");
const packagesDir = join(root, "packages");
const scope = "@singularity-forge";
const scopeDir = join(root, "node_modules", scope);

// Directory names under packages/ that should be linked as @singularity-forge/<dir>
const packageDirs = [
	"native",
	"pi-agent-core",
	"google-gemini-cli-provider",
	"pi-ai",
	"pi-coding-agent",
	"pi-tui",
	"rpc-client",
	"daemon",
];

if (!existsSync(scopeDir)) {
	mkdirSync(scopeDir, { recursive: true });
}

let linked = 0;
let copied = 0;
for (const dir of packageDirs) {
	const source = join(packagesDir, dir);
	const target = join(scopeDir, dir);

	if (!existsSync(source)) continue;

	// Skip if already correctly linked or is a real directory (bundled)
	if (existsSync(target)) {
		try {
			const stat = lstatSync(target);
			if (stat.isSymbolicLink()) {
				const linkTarget = readlinkSync(target);
				if (
					resolve(join(scopeDir, linkTarget)) === source ||
					linkTarget === source
				) {
					continue; // Already correct
				}
				unlinkSync(target); // Wrong target, relink
			} else {
				continue; // Real directory (e.g., copied or from bundleDependencies), don't touch
			}
		} catch {
			continue;
		}
	}

	let symlinkOk = false;
	try {
		symlinkSync(source, target, "junction"); // junction works on Windows too
		symlinkOk = true;
		linked++;
	} catch {
		// Symlink failed — common on Windows without Developer Mode or admin rights.
		// Fall back to a directory copy so the package is still resolvable.
	}

	if (!symlinkOk) {
		try {
			cpSync(source, target, { recursive: true });
			copied++;
		} catch {
			// Non-fatal — loader.ts will emit a clearer error if resolution still fails
		}
	}
}

if (linked > 0)
	process.stderr.write(
		`  Linked ${linked} workspace package${linked !== 1 ? "s" : ""}\n`,
	);
if (copied > 0)
	process.stderr.write(
		`  Copied ${copied} workspace package${copied !== 1 ? "s" : ""} (symlinks unavailable)\n`,
	);

// Platform-specific native engine packages live under rust-engine/npm/<suffix>/, not packages/.
// Wire them into node_modules/@singularity-forge/ so native.ts can require() them without
// a registry install. Only link platforms where the binary (forge_engine.node) is present.
const nativeNpmDir = join(root, "native", "npm");
const engineSuffixes = [
	"linux-x64-gnu",
	"linux-arm64-gnu",
	"win32-x64-msvc",
];
for (const suffix of engineSuffixes) {
	const source = join(nativeNpmDir, suffix);
	const binaryPath = join(source, "forge_engine.node");
	if (!existsSync(source) || !existsSync(binaryPath)) continue;

	const target = join(scopeDir, `engine-${suffix}`);
	if (existsSync(target)) {
		try {
			const stat = lstatSync(target);
			if (stat.isSymbolicLink()) {
				const linkTarget = readlinkSync(target);
				if (
					resolve(join(scopeDir, linkTarget)) === source ||
					linkTarget === source
				)
					continue;
				unlinkSync(target);
			} else {
				continue;
			}
		} catch {
			continue;
		}
	}

	try {
		symlinkSync(source, target, "junction");
		process.stderr.write(
			`  Linked native engine: @singularity-forge/engine-${suffix}\n`,
		);
	} catch {
		try {
			cpSync(source, target, { recursive: true });
		} catch {
			/* non-fatal */
		}
	}
}
