import { existsSync as defaultExistsSync } from "node:fs";
import { join } from "node:path";

/**
 * Returns the correct Node.js type-stripping flag for subprocess spawning.
 *
 * Node v26 enforces ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING for files
 * resolved under `node_modules/`. When SF is installed globally via npm,
 * all source files live under `node_modules/sf-run/src/...`, so
 * `--experimental-strip-types` fails deterministically.
 *
 * `--experimental-transform-types` applies a full TypeScript transform that
 * works regardless of whether the file is under `node_modules/`. SF requires
 * Node 26+, so this flag is always available.
 */
export function resolveTypeStrippingFlag(packageRoot: string): string {
	return isUnderNodeModules(packageRoot)
		? "--experimental-transform-types"
		: "--experimental-strip-types";
}

/**
 * Returns true when the given path sits inside a `node_modules/` directory.
 * Handles both Unix and Windows path separators.
 */
export function isUnderNodeModules(filePath: string): boolean {
	const normalized = filePath.replace(/\\/g, "/");
	return normalized.includes("/node_modules/");
}

export interface SubprocessModuleResolution {
	/** Absolute path to the module file (either src/.ts or dist/.js). */
	modulePath: string;
	/** When true the module is pre-compiled JS — skip TS flags and loader. */
	useCompiledJs: boolean;
}

/**
 * Resolves a subprocess module path, preferring compiled `dist/*.js` when the
 * package root is under `node_modules/`.
 *
 * Node v26 unconditionally refuses `.ts` files under `node_modules/` — even
 * with `--experimental-transform-types`.  When SF is installed globally via
 * npm, every subprocess that loads a `.ts` extension module crashes with
 * `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`.
 *
 * The compiled JS files already ship in the npm package (`dist/` is in the
 * `files` array in package.json) and are the correct artefacts to use when
 * running from a packaged install.
 *
 * @param packageRoot  Absolute path to the SF package root.
 * @param relPath      Path relative to `src/`, e.g.
 *                     `"resources/extensions/sf/workspace-index.ts"`.
 * @param checkExists  Optional `existsSync` override (for testing).
 */
export function resolveSubprocessModule(
	packageRoot: string,
	relPath: string,
	checkExists: (path: string) => boolean = defaultExistsSync,
): SubprocessModuleResolution {
	const jsRelPath = relPath.replace(/\.ts$/, ".js");
	const distPath = join(packageRoot, "dist", jsRelPath);
	if (checkExists(distPath)) {
		return { modulePath: distPath, useCompiledJs: true };
	}

	const sourceJsPath = join(packageRoot, "src", jsRelPath);
	if (checkExists(sourceJsPath)) {
		return { modulePath: sourceJsPath, useCompiledJs: true };
	}

	return {
		modulePath: join(packageRoot, "src", relPath),
		useCompiledJs: false,
	};
}

/**
 * Builds the Node.js subprocess prefix args for running a SF extension module.
 *
 * When the module resolved to compiled JS (`useCompiledJs === true`), returns
 * only `["--input-type=module"]` — no TS loader, no TS stripping flag.
 *
 * When the module is TypeScript source, returns the full prefix:
 * `["--import", <loaderHref>, <tsFlag>, "--input-type=module"]`.
 */
export function buildSubprocessPrefixArgs(
	packageRoot: string,
	resolution: SubprocessModuleResolution,
	tsLoaderHref: string,
): string[] {
	if (resolution.useCompiledJs) {
		return ["--input-type=module"];
	}
	return [
		"--import",
		tsLoaderHref,
		resolveTypeStrippingFlag(packageRoot),
		"--input-type=module",
	];
}
