// Run an npm-style lifecycle script (`prepack`, `prepare`, `prepublishOnly`,
// `postpack`, `postpublish`, …) from inside a `gjsify pack` / `gjsify
// publish` flow.
//
// Why this exists separately from `gjsify run`: `gjsify run <script>` ends
// with `process.exit(<code>)` so it can be used as a CLI entrypoint that
// behaves like `yarn run` / `npm run`. That's wrong for the embedded use
// case where pack/publish needs the lifecycle script to finish, then keep
// running its own logic. This helper resolves to a Promise that settles
// when the child exits — no process.exit, no GLib-mainloop intermingling
// from `ensureMainLoop`.
//
// Matches yarn / npm script semantics:
//   - `shell: true` so `&&` / `|` / env-var refs work
//   - PATH prepended with `<wsDir>/node_modules/.bin` + monorepo-root bin
//   - `npm_lifecycle_event` / `npm_package_name` / `npm_package_version`
//     env vars set
//   - FORCE_COLOR=1 default unless caller overrides
import { spawn } from 'node:child_process';
import { delimiter, join } from 'node:path';
import { findWorkspaceRoot } from './workspace-root.js';
/**
 * Run a lifecycle script defined in `pkg.scripts[name]` from `wsDir`.
 * Returns `true` if the script existed and exited 0. Returns `false` if
 * `optional: true` and the script is missing. Throws on non-zero exit.
 */
export async function runLifecycleScript(wsDir, pkg, name, opts = {}) {
    const scripts = pkg.scripts ?? {};
    const literal = scripts[name];
    if (typeof literal !== 'string') {
        if (opts.optional !== false)
            return false;
        throw new Error(`gjsify lifecycle-script: no "${name}" in ${wsDir}/package.json`);
    }
    const monorepoRoot = findWorkspaceRoot(wsDir);
    const binDirs = [join(wsDir, 'node_modules', '.bin')];
    if (monorepoRoot && monorepoRoot !== wsDir) {
        binDirs.push(join(monorepoRoot, 'node_modules', '.bin'));
    }
    // Match yarn / npm color-forcing default — see runScript() in run.ts
    // for the full reasoning. Without this, lifecycle scripts that call
    // tools like biome / esbuild / tsc lose ANSI color in piped contexts
    // (CI logs, redirected output) because `process.stdout.isTTY` is
    // false for the spawned child.
    const colorEnv = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined ? {} : { FORCE_COLOR: '1' };
    const env = {
        ...process.env,
        ...colorEnv,
        PATH: [...binDirs, process.env.PATH ?? ''].filter(Boolean).join(delimiter),
        npm_lifecycle_event: name,
        npm_package_name: pkg.name ?? '',
        npm_package_version: pkg.version ?? '',
        ...opts.env,
    };
    // `'inherit-stderr'` is our extension on top of node's stdio modes —
    // child stdin inherits, child stdout → parent's stderr (fd 2), child
    // stderr → parent's stderr. Used by `gjsify pack --json` so the
    // prepack's log lines don't get interleaved with the JSON we emit on
    // parent stdout. `spawn`'s `stdio` accepts numeric fds in array form
    // and routes the child's matching stream to that fd.
    const stdioConfig = opts.stdio === 'inherit-stderr' ? ['inherit', 2, 2] : (opts.stdio ?? 'inherit');
    await new Promise((resolveOk, reject) => {
        const child = spawn(literal, [], {
            cwd: wsDir,
            env: env,
            stdio: stdioConfig,
            shell: true,
        });
        child.on('close', (code) => {
            if (code === 0)
                resolveOk();
            else {
                reject(new Error(`gjsify lifecycle-script: "${name}" in ${wsDir} exited with code ${code}`));
            }
        });
        child.on('error', reject);
    });
    return true;
}
