#!/usr/bin/env node
const { spawnSync } = require("node:child_process");
const {
	copyFileSync,
	mkdirSync,
	readFileSync,
	readdirSync,
	rmSync,
	writeFileSync,
} = require("node:fs");
const { dirname, join, resolve } = require("node:path");

const root = resolve(__dirname, "..");
const srcResources = join(root, "src", "resources");
const distResources = join(root, "dist", "resources");
const resourcesTsconfig = join(root, "tsconfig.resources.json");

function copyNonTsFiles(srcDir, destDir) {
	for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
		const srcPath = join(srcDir, entry.name);
		const destPath = join(destDir, entry.name);

		if (entry.isDirectory()) {
			copyNonTsFiles(srcPath, destPath);
			continue;
		}

		if (
			entry.name.endsWith(".ts") ||
			entry.name.endsWith(".tsx") ||
			entry.name.endsWith(".js") ||
			entry.name.endsWith(".jsx")
		) {
			continue;
		}

		mkdirSync(dirname(destPath), { recursive: true });

		// Rewrite pi.extensions paths from .ts to .js in package.json files
		// so they match the compiled output (tsc compiles index.ts → index.js
		// but package.json is copied as-is).
		if (entry.name === "package.json") {
			try {
				const pkg = JSON.parse(readFileSync(srcPath, "utf-8"));
				if (Array.isArray(pkg?.pi?.extensions)) {
					pkg.pi.extensions = pkg.pi.extensions.map((ext) =>
						ext.replace(/\.ts$/, ".js").replace(/\.tsx$/, ".js"),
					);
					writeFileSync(destPath, JSON.stringify(pkg, null, 2) + "\n");
					continue;
				}
			} catch {
				/* fall through to plain copy */
			}
		}

		copyFileSync(srcPath, destPath);
	}
}

rmSync(distResources, { recursive: true, force: true });

// Check if there are any .ts files to compile
function hasTsFilesRecursive(dir) {
	for (const entry of readdirSync(dir, { withFileTypes: true })) {
		const fullPath = join(dir, entry.name);
		if (entry.isDirectory()) {
			if (hasTsFilesRecursive(fullPath)) return true;
		} else if (entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) {
			return true;
		}
	}
	return false;
}
const hasTsFiles = hasTsFilesRecursive(srcResources);

if (hasTsFiles) {
	const tsgoBin = join(
		dirname(require.resolve("@typescript/native-preview/package.json")),
		"bin",
		"tsgo.js",
	);
	const compileWithTsgo = () =>
		spawnSync(process.execPath, [tsgoBin, "--project", resourcesTsconfig], {
			cwd: root,
			stdio: "inherit",
		});
	const compileWithTsc = () => {
		const tscBin = require.resolve("typescript/bin/tsc");
		return spawnSync(
			process.execPath,
			[tscBin, "--project", resourcesTsconfig],
			{
				cwd: root,
				stdio: "inherit",
			},
		);
	};

	let compile = compileWithTsgo();

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

	// Native tsgo has reported successful emits while leaving .ts resources copied
	// without their .js runtime output. Verify a known TS-only resource before the
	// launcher tries to import it, and fall back to local tsc if needed.
	const requiredJs = join(
		distResources,
		"extensions",
		"sf",
		"model-registry.js",
	);
	try {
		readFileSync(requiredJs);
	} catch {
		console.warn(
			"[copy-resources] tsgo did not emit model-registry.js; retrying resources build with tsc",
		);
		compile = compileWithTsc();
		if (compile.status !== 0) {
			process.exit(compile.status ?? 1);
		}
	}
} else {
	// No .ts files — just create the dist/resources directory and copy .js files
	mkdirSync(distResources, { recursive: true });
}

copyNonTsFiles(srcResources, distResources);

// Also copy .js files since they're not compiled from .ts
function copyJsFiles(srcDir, destDir) {
	for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
		const srcPath = join(srcDir, entry.name);
		const destPath = join(destDir, entry.name);

		if (entry.isDirectory()) {
			copyJsFiles(srcPath, destPath);
			continue;
		}

		if (entry.name.endsWith(".js")) {
			mkdirSync(dirname(destPath), { recursive: true });
			copyFileSync(srcPath, destPath);
		}
	}
}
copyJsFiles(srcResources, distResources);
writeFileSync(
	join(distResources, ".sf-resource-build-stamp"),
	`${new Date().toISOString()}\n`,
);
